Why can’t I set cookies in WebView when using NavigateToString

The title says it all and we have seen some forum and support cases about this subject.  I have a workaround for you though!  

Background

When programming with WebView you can use NavigateToString to populate a HTML and JavaScript and if that JavaScript sets cookies, the document. cookie property will fail to set cookies.  Also if you use NavigateToString to create HTML and later you set cookies by injecting JavaScript it does not work (see Matt’s post here for a post about injecting JavaScript: How to inject javascript into a WebView page).

The reason that you cannot set cookies is because of the legacy security model of the Internet Explorer engine (used for the WebView control) that prevents cookies from being set for a page that does not have a URL.  This is done to prevent certain injection attacks.  When you use NavigateToString and pass HTML the URL of the page is set to ‘about:blank’ and the security kicks in preventing you from setting cookies.

Solution

I will give you some sample code showing the problem and then a solution to workaround this.

Basic Code to set cookies

The JavaScript to set a cookie is fairly simple and can be found by ‘Bing-ing’ it on the web:

function set_cookie() { document.cookie = 'name=abc'; }

 

Target HTML For all scenarios

    

The HTML that I ultimately want to have in my WebView for all the scenarios is included in the file ‘test.html’ and uses for the Button 2 scenario:

<!DOCTYPE html>
<html>
<head>
<script type='text/javascript'>
function set_cookie() { document.cookie = 'name=abc'; }
function show_cookie() {
var txt = document.getElementById('show'); txt.value = document.cookie;
}

    </script>

</head>

<body>
<input type='text' id='show' value='unknown' />
<br />
<input type='button' onclick='set_cookie()' value='set cookie' />
<input type='button' onclick='show_cookie()' value='get cookie' />

</body>

</html>

Code and Description illustrating Problem and Solution

I created a blank C# Windows Store app in Visual Studio.  I then put a WebView in my XAML, named it ‘wvTest’ and three buttons to illustrate the issue.  Finally I wired up the three buttons to click event handlers by using the property page and clicking on the event  tab (lighting bolt icon) and double clicking the click event text box.  The last step was to create ‘test.html’ with the above HTML in it and a ‘blank.html’ with the default HTML you get when adding an HTML page using the wizard.

Clicking Button 1 invokes code which fails to set the cookie.  The entire HTML for the WebView is passed as a string in the NavigateToString method of the WebView and fails for the security reason I already discussed.

Clicking Button 2 invokes code which succeeds, but it does not allow you to generate the HTML and script dynamically.  In this case we navigate to a URL which is the html and script contained in ‘test.html’ which is included in the project.  Because we are navigating to a URL this will not have the security restriction and the cookie is set.  If you do not need to generate HTML and script dynamically, this is the best solution for you!

Clicking Button 3 also succeeds.  This shows how you can dynamically generate HTML and script and use it.  Blank.html is the blank HTML template created when you add a blank HTML page to the project using the add, new item wizard in Visual Studio.  Then the code uses the ‘EVAL’ JavaScript function to inject the functions for the event handlers and HTML into the document body.

Sample code:

// Used to detect the third scenario in the LoadComplete handler of the WebView
bool is3rdButton = false;

//Fail to set cookie for security reasons
private void Button_Click_1(object sender, RoutedEventArgs e)
{
is3rdButton = false;
String strHTML = "<!DOCTYPE html><html><head><script type='text/javascript'> function set_cookie() { document.cookie = 'name=abc'; } function show_cookie() { var txt = document.getElementById('show'); txt.value = document.cookie.type; }</script></head> <body> <input type='text' id='show' value='unknown' /> <br /> <input type='button' onclick='set_cookie()' value='set cookie' /> <input type='button' onclick='show_cookie()' value='get cookie' /></body></html>";
wvTest.NavigateToString(strHTML);
}

//Success but all the code and HTML is hardcoded in the html file
private void Button_Click_2(object sender, RoutedEventArgs e)
{
is3rdButton = false;
wvTest.Navigate(new Uri("ms-appx-web:///test.html"));
}

//Success and solution where the blank template HTML is used and HTML and functions are injected
// in the LoadComplete event of the WebView!
private void Button_Click_3(object sender, RoutedEventArgs e)
{
is3rdButton = true;
wvTest.Navigate(new Uri("ms-appx-web:///blank.html"));

}

private void wvTest_LoadCompleted(object sender, NavigationEventArgs e)
{
if (is3rdButton)
{
is3rdButton = false;

       //set the functions that create the cookie and the HTML of the page dynamically
wvTest.InvokeScript("eval", new String[] { "function set_cookie() { document.cookie = 'name=abc'; } function show_cookie() { var txt = document.getElementById('show'); txt.value = document.cookie; } document.body.innerHTML=\" <input type='text' id='show' value='unknown' /> <br /> <input type='button' onclick='set_cookie()' value='set cookie' /> <input type='button' onclick='show_cookie()' value='get cookie' /> \" " });
}
}

Summary

You cannot successfully use NavigateToString and set cookies but you can get the same behavior by using the Navigate and injecting your HTML with the two methods I have described.

Enjoy developing Windows Store apps and please feel free to provide feedback.  Let us know if this helped you at Twitter @WSDevSol