In Windows 8, an app written in JavaScript could host other HTML content–both local (in-package) and remote (http[s])–in an iframe element. In Windows 8.1, an iframe is still completely supported for in-package content (ms-appx-[web] URIs), but only supports secure remote content (https). For general hosting of web content (http), apps should be using the x-ms-webview element.

In this post and the few that follow, I wanted to share a few details about migrating code from iframe to webview. For a broader discussion of hosting web content, refer to Chapter 4 of my free ebook, Programming Windows Store Apps with HTML, CSS, and JavaScript, Second Edition. Also see What’s New in Webview for Windows 8.1 on the Windows App Builder’s blog.

For in-package content using ms-appx-[web] URIs, you can continue to use iframes. If you want to switch to webview for any reason with ms-appx-web (you can’t use webview with ms-appx), then you should be able to make a straight change from <iframe src=”…”> to <x-ms-webview src=”…”>. Then you need to change any uses of postMessage to communicate with the iframe to webview.invokeScriptAsync (to call methods in the webview’s script) and the MSWebViewScriptNotify event (to pick up events raised from within the webview using window.external.notify).

For web content, the story gets a little trickier. If you try to direct an iframe in Windows 8.1 to an http:// address, you’ll get a message like this:

APPHOST9625: Unable to navigate to: ‘’. An iframe attempted to navigate to a URI that is not included in the ApplicationContentUriRules for this app. Use a x-ms-webview element to view the URI instead, or add the URI to the ApplicationContentUriRules section of the package manifest so that the iframe can navigate to it. (In Visual Studio, add this URI to the Content URIs tab of the Manifest Designer.)

Like it says, an iframe in 8.1 must use https for remote content, and then the app’s manifest must have an entry for that site on its Content URIs tab. So if you can use https:// for that site, then you can continue to host it in an iframe. If not, then, you need to use a webview.

With a webview, however, window.external.notify (to raise events from the webview) will work only when src is set to an https:// (and you have a content URI in the manifest) or ms-appx-web::// URI, but not http://.

That said, the webview element can be loaded from alternate source, which is one of the reasons why we have it. For one, you can also use ms-appdata URIs for a webview, either through its src attribute or its navigate method. This makes it possible to download content from the web or generate it dynamically, then render it within the webview. The caveat, however, is that with ms-appdata URIs, you can use invokeScriptAsync but not window.external.notify.

That said, if you load content into a webview using its navigateToString or navigateToLocalStreamUri methods, then you can also use window.external.notify. This means that if you want to load up downloaded or dynamically-generated content into a webview and have it raise events, then you either need to load the content into a memory string and call navigateToString, or you create a local stream provider (refer to the HTML webview control sample for a demonstration, as well as

In part 2 we’ll look at capturing webview content.


  1. walid taraf
    Posted August 13, 2014 at 9:31 am | Permalink

    hey Kraig, new follower here. Looking into ways to retain browser history, even if content is Cross-domain, so we can offer navigational arrows in that web view. ANy insights?

  2. kraigb
    Posted August 19, 2014 at 8:30 am | Permalink

    The Webview already maintains a history, but won’t itself provide any surrounding controls for navigation. You’d need to provide those yourself, using the webview’s canGoBack and canGoForward properties to enable/disable the buttons appropriately. What you won’t be able to do is acquire the history from the Webview itself. You’ll have to watch the webview’s navigation events and keep your own list.