Thanks to Michael Scherotter for the details explained in this post.

Say you have an x-ms-webview element on a page in your app:

ms-webview id="webview" class="Content" src="ms-appx-web:///contenthost.html">

and you want to print only the contents of the WebView. To do this, it seems at first that you need to get a DOM document object for the Webview to pass to MSApp.getHtmlPrintDocumentSource within a PrintTaskSourceRequested event. However, as noted in an earlier post, the Webview's DOM isn't accessible by the host app, for many good reasons, so it seems you're left with solutions like obtaining a bitmap of the Webview and printing that.

But there's another way that can work in a number of scenarios, especially when you know the content you're loading into the Webview and aren't wanting to print arbitrary pages. The trick is to load that content into a new document fragment and use that to obtain a source:

    //content is what you load in the Webview; printTask and printDeferral come from the WinRT print event

    var fragment = document.createDocumentFragment();
    var printSource;
    div = document.createElement("div");

    WinJS.Utilities.setOuterHTMLUnsafe(div, content);

    source = MSApp.getHtmlPrintDocumentSource(fragment);

You new methods like setOuterHTMLUnsafe had a use somewhere!

When you have a Webview hosting an iframe element, it's possible to monitor what's going on in the iframe using the MSWebViewFrame* events, which are documented with the x-ms-webview element. These are MSWebViewFrameNavigationStarting, MSWebViewFrameContentLoading, MSWebViewFrameDOMContentLoaded, and MSWebViewFrameNavigationComplete.

In the event you have multiple iframe elements in a webview, the way to distinguish between frames is with the e.uri property, where e is the event args object for the event in question. This property is presently undocumented, but should be–this comes directly from the engineers who built the webview, so we can trust that it's there!

Generally speaking, the content of a Webview is generally a black box from the host app's perspective, and for this reason you can't get into the Webview's DOM, you can't get element references (e.g. to an iframe), and so on. There'd be serious overhead to go that route.

Which brings us to the question of what happens when code within an iframe or Webview crashes. You've certainly seen some of those sites–the ones that cause the browser to pop up debugging messages about JavaScript exceptions and whatnot. Overall, crashes in a Webview or iframe should not crash the host app.

However, there is the possibility that long-running scripts within whatever web page you're hosting are taking so long that your app is considered to be unresponsive. Those are messages you've seen in the browser as well, and if the app gets blocked on the same kind of script, it's possible the Windows will take the app down. This is a known issue that might be addressed in future releases of the operating system. At present, you can consider watching the Webview/iframe NavigationStarting and setting up your own timeout so you can catch an unresponsive site before Windows takes down the app.

I've seen the question come up on occasion about using WinJS within a webview. This is entirely supported, and has been since the introduction of WinJS–the library always checks whether WinRT is present and adjusts itself accordingly. For example, if WinRT is available, then it will use WinRT app data for saving its settings; if WinRT isn't available, it uses standard HTML5 APIs instead.

This strategy wisely foresaw the day when WinJS was licensed for use in web page. This wasn't originally the case (as the copyright headers in the WinJS files made clear), but with WinJS going open source in April 2014, WInJS can be used anywhere.

When using WinJS, and especially WinJS controls, inside a webview or web page, it's important to remember steps that are included by default within the Windows app templates, most notable the step of calling WinJS.UI.processAll. That is, when you create a new Windows app, the templates in Visual Studio supply the following default activation code:

var app = WinJS.Application;
var activation = Windows.ApplicationModel.Activation;

app.onactivated = function (args) {
    if (args.detail.kind === activation.ActivationKind.launch) {
        if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
            // TODO: This application has been newly launched. Initialize
            // your application here.
        } else {
            // TODO: This application has been reactivated from suspension.
            // Restore application state here.

Clearly, if you're using WinJS outside of an app, then the Windows.ApplicationModel.Activation namespace won't mean anything, so naturally you'll delete all the code inside the onactivated handler here. But if you delete WinJS.UI.processAll then none of your WinJS controls will show up. This can be confusing and might lead to the idea that WinJS doesn't work in a webview. But it does, because in the end WinJS is just a JavaScript library using standard HTML5/DOM to do its work. So just make sure that somewhere you have a line like this:



To close this series, I wanted to share a few additional bits related to webview, but not necessarily related to one another!

First, in the previous post I showed how to wrap some of the webview’s capture methods in a promise. The same thing is easy to do with invokeScriptAsync, where the promise is fulfilled with the value returned from the webview’s script:

functon callWebviewScript(webview, targetFunction, argsIn) {
return new WinJS.Promise(function (cd, ed) {
var op = webview.invokeScriptAsync(targetFunction, argsIn);

        op.oncomplete = function (args) {;
//Return value from the invoked function (always a string) is in

op.onerror = function (e) { ed(e); };

This kind of structure helps you remember the necessary call to start at the end.

With invokeScriptAsync, be aware that the result you pass back must be a string, otherwise you’ll get an empty string.

In the callWebviewScript function above, note that targetFunction can be set to “eval” and argsIn to whatever script string you want to evaluate–meaning that the webview doesn’t necessarily have to have a named function inside it. Just remember that the result of that eval must again be a string.

The next little tip is just knowing that webview cache cookies and other content just like the browser, even if you remove the webview from the DOM and let it get garbage collected. There aren’t facilities to clear information that’s cached in the webview, though you might be able to achieve this through the Windows.Web.HttpClient API instead.

Lastly, the webview has a characteristic that if you overlay another element on a webview that has 100% opacity, the webview will go black as it fails “layer candidacy” as it’s called. The same thing happens with an overlay element with 0% opacity. (See under “Unsupported Scenarios” section for the description of scenarios where it fails layer candidacy.)

If, however, the overlay element’s style has an opacity of anything other than 1 or 0, like 0.9999 or 0.0001, then the webview is happy. It’s an odd behavior, but a good one to know about.

One of the key feature of the webview that really sets it apart from the iframe is the ability to capture its content, something that you simply cannot do with an iframe. There are three ways this can happen.

First is the src attribute. Once the webview has navigated (and fired its MSWebViewNavigationCompleted event), src will contain a URI to the content as the webview sees it. For web content, this will be an http[s] URI, which can be opened in a browser. Local content (loaded from strings or app data files) will start with ms-local-web, which can be rendered into another webview using navigateToLocalStream. Be aware that while navigation is happening prior to MSWebViewNavigationCompleted, the state of the src property is indeterminate; use the uri property in those handlers instead.

Second is the webview’s captureSelectedContentToDataPackageAsync method, which reflects whatever selection the user has made in the webview directly. The fact that a data package is part of this API suggests its primary use: the share contract. From a user’s perspective, any web content you’re displaying in the app is really part of the app. So if they make a selection there and invoke the Share charm, they’ll expect that their selected data is what gets shared, and this method lets you obtain the HTML for that selection. Of course, you can use this anytime you want the selected content—the Share charm is just one of the potential scenarios.

As with invokeScriptAsync, the return value from captureSelectedContentToDataPackage­Async is again a DOM-ish object with a start method that you must call to perform the operation. If you want to wrap this in a promise, you can use a structure like this:

function captureWebviewSelectionAsync(webview) {
//Wrap the capture method in a promise
return new WinJS.Promise(function (cd, ed) {
var op = webview.captureSelectedContentToDataPackageAsync();
op.oncomplete = function (args) { cd(; };
op.onerror = function (e) { ed(e); };
In this case, the result you care about us in the oncomplete event, which is a Windows.ApplicationModel.DataTransfer.­DataPackage object as used with the Share charm. Calling its getView method will produce a DataPackageView whose availableFormats object tells you what it contains. You can then use the appropriate get* methods like getHtmlFormatAsync to retrieve the selection data itself. Note that if there is no selection, will be null, so you’ll need to guard against that. Here’s some code, for instance, to copy the selection from one webview to another, using the function above:

function captureSelection() {
var source = document.getElementById(“webviewSource”);
var promise = captureWebviewSelectionAsync(source);

//Navigate the output webview to the selection, or show an error
var output = document.getElementById(“webviewOutput”);

promise.then(function (dataPackage) {
if (dataPackage == null) { throw “No selection”; }

var view = dataPackage.getView();
return view.getHtmlFormatAsync();
}).done(function (text) {
}, function (e) {
output.navigateToString(“Error: ” + e.message);

Note that the captured selection is an HTML clipboard format that includes the extra information at the top before the HTML from the webview. If you need to extract just the straight HTML, you’ll need to strip off this prefix text up to <!DOCTYPE html>.

Generally speaking, captureSelectedContentToDataPackageAsync will produce the formats AnsiText, Text, HTML Format, Rich Text Format, and msSourceUrl, but not a bitmap. For this you need to use the third method, capturePreviewToBlobAsync, which again has a start method and complete/error events. The results of this capture (in within the complete handler) is a blob object for whatever content is contained within the webview’s display area. Here’s another function to wrap that operation in a promise:

function captureWebviewBitmapAsync(webview) {
return new WinJS.Promise(function (cd, ed) {
var op = webview.capturePreviewToBlobAsync();

op.oncomplete = function (args) {
var ras = Windows.Storage.Streams.RandomAccessStreamReference;
var bitmapStream = ras.createFromStream(;

op.onerror = function (e) { ed(e); };

The result of the promise generated by this function is a blob, which you can use for a number of purposes. If you want to display it in an img element, you can use URL.createObjectURL on this blob directly. This means you can easily load some chunk of HTML in an offscreen webview (make sure the display style is not “none”) and then capture a blob and display the results in an img. Besides preventing interactivity, you can also animate that image much more efficiently than a full webview, applying 3D CSS transforms, for instance. You can also stream the blob in an upload, save it to a file, and so forth.

For other purposes, like the Share charm, you can call this blob’s msDetachStream method, which conveniently produces exactly what you need to provide to a data package’s setBitmap method. This is demonstrated in scenario 7 of the SDK’s HTML Webview control sample (though the sample neglects to use the datarequested event’s deferral correctly).

In part 3 I’ll share a few other closing bits on webview.

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.