Windows 8 Development - Dynamic HTML Restrictions

04 June 2014

As I mentioned in my previous post, Microsoft places restrictions on what you are allowed to do to your HTML via JavaScript; they do not allow you to do anything that they consider “unsafe,” and the documentation is a little confusing on the subject. The important thing to know is that these restrictions only affect dynamic HTML and there are some simple ways of dealing with it.

Static HTML

Any HTML that is static to your page is not affected by the restrictions . For example, the following HTML will not cause an error (even though I am not sure why you would include it in a Windows Store Application):

1 <form name="input" action="foo.aspx" method="POST">
2   Username: <input type="text" name="user" />
3   Password: <input type="password" name="pwd" />
4   <input type="submit" value="Submit" />
5 </form>

Note that in the HTML above, it includes some restricted attributes (i.e. name, action, and method). But, at runtime, there is no exception thrown because it is static HTML.

Dynamic HTML

Dynamically created HTML is what is restricted. This means that you will get errors when trying to add any “unsafe” HTML via one of the following methods.

This also affects any “unsafe” HTML that is produced using common frameworks like JQuery and will not protect you from getting an exception when using methods such as html(), append(), and appendTo(). The following will throw exceptions:

1 //Using plan ECMAScript...
2 document.getElementById('foo')
3   .innerHTML = "<blink>Blink is unsafe.</blink>";
4 document.getElementById('foo')
5   .innerHTML = "Password: <input type='password' name='pwd'/>";
7 //Using JQuery...
8 $("#foo").html("<blink>Blink is unsafe.</blink>");
9 $("#foo").html("Password: <input type='password' name='pwd'/>");

The part that I find weird and inconsistent about these restrictions is that they are not all handled the same by the framework (in a good way). For example, even though comments (i.e. <!-- -->) are “unsafe,” rather then throwing an exception, the comments are merely stripped out. The following will not throw exceptions:

1 //Using plan ECMAScript...
2 document.getElementById('foo')
3   .innerHTML = "<!-- Comments are unsafe. -->";
5 //Using plan JQuery...
6 $("#foo").html("<!-- Comments are unsafe. -->");

Dealing with Unsafe HTML Restrictions

There are several ways that you can address exceptions caused by unsafe HTML. The first and most obvious is to simply edit your HTML template to not contain unsafe segments. But, this is not always as easy as it might sound when using large libraries such as AngularJS.

A similar approach to manually removing unsafe HTML is to use toStaticHTML(). This method will simply strip unsafe items out of the string indiscriminately.

For example:

1 var unsafe = 'Password: <input type="password" name="pwd"/>';
2 var html = toStaticHTML(unsafe);
3 document.getElementById('foo').innerHTML = html;

Results in the following HTML:

1 <div id="foo">Password: <input type="password"/></div>

Note that name="pwd" was removed. I find this approach to be the least desirable for two reasons.

  1. It can result in unexpected behavior. For example, with name="pwd" removed from the HTML, the input element is left without an identifier.
  2. toStaticHTML() is a method that is only found in IE. Therefore, if you are sharing code between your application and a website application and you are not smart about it, you will no longer be supporting Chrome, Firefox, and Safari.

The final way to avoid Exceptions from unsafe HTML is to wrap the JavaScript in an unsafe block using MSApp.execUnsafeLocalFunction().

For example:

1 MSApp.execUnsafeLocalFunction(function () {
2   var unsafe = 'Password: <input type="password" name="pwd"/>';
3   var html = toStaticHTML(unsafe);
4   document.getElementById('foo').innerHTML = html;
5 });

Results in the following HTML:

1 <div id="foo">Password: <input type="password" name="pwd"/></div>

Although MSApp.execUnsafeLocalFunction() shares the problem of limited support that toStaticHTML() has (this time with the Windows Store framework), I find it to be the better option because you are getting exactly what you expect. Also, since this is a framework function and not an IE function, you can easily circumvent it in your web application that shares the code by including something like the following:

1 (function () {
2   window.MSApp = {
3       execUnsafeLocalFunction: function (render) {
4           render();
5       }
6   };
7 }());