The x-ms-webview is a powerful control that lets you embed web content directly in your app’s UX. This control has been available to XAML Windows Store apps since Windows 8, and in Windows 8.1 we’ve made lots of great updates, which are described in Sam Spencer’s blog post, What’s new in WebView in Windows 8.1. As noted in Sam’s post, one of the biggest updates is that the control is now available for apps written in HTML and JavaScript!
If you’re an HTML/JS app developer, the x-ms-webview offers you a great way to add web content to your app’s experience. In this post we’ll dive into why you’ll want to upgrade your app to use x-ms-webview. I’ll also give some tips on how to take advantage of all the x-ms-webview has to offer.
Why did we build an HTML x-ms-webview?
When I talk to HTML developers about the new x-ms-webview control the most common initial reaction I hear is, “Why would I need that?” Because apps written in JavaScript already have the ability to host web content in an iframe element, it’s a great question. But iframes have several limitations that make embedding web content cumbersome, or even impossible, in certain situations. For example:
- Framebusting sites – A lot of major web properties detect if they are being hosted in an iframe and simply don’t render. The x-ms-webview acts like a browser when communicating with the underlying content.
- Document loading events – For iframes, detecting when content was loaded was either a waiting game or an art form. By trying to use postMessage() to detect when content the DOM content is loaded, the x-ms-webview exposes a rich set of events to make this deterministic for app development.
The x-ms-webview solves all these problems. Additionally the x-ms-webview provides new functionality that was never possible with an iframe:
- Better access to local content – Access to local state folder content to load into x-ms-webview. This provides ebook readers with a means to render books that are stored locally.
- Screenshots of web content – The ability to take a screenshot of the x-ms-webview content to make it easy provide content to the Share contract.
So to answer the original question, of why an HTML and JavaScript app needs a webview… All these features mean the x-ms-webview is simply the best way for your app to display web content.
What does the x-ms-webview look like?
Just to cover the basics, the Webview is an HTML element that’s part of the DOM for JavaScript apps. We use x-ms- appended to the x-ms-webview element name to denote this is a vendor specific HTML feature (e.g. “x-vendor-feature”). Using this element should seem familiar as the syntax is similar to an iframe.
HTML element
<x-ms-webview id="webview" src="http://www.bing.com"></x-ms-webview>
JavaScript
var webview = document.createElement("x-ms-webview");
Adding a basic x-ms-webview control to the default.html page in a Windows Store app written in JavaScript looks like this:
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>WebView</title>
<!-- WinJS references -->
<link href="//Microsoft.WinJS.2.0/css/ui-dark.css" rel="stylesheet" />
<script src="//Microsoft.WinJS.2.0/js/base.js"></script>1:
2:
3: <script src="//Microsoft.WinJS.2.0/js/ui.js">1: </script>
2:
3: <!— app references -->
4:
5: <link href="/css/default.css" rel="stylesheet" />6:
7: <script src="/js/default.js"></script>
</head>
<body>
<x-ms-webview id="webview" src="http://www.bing.com" width="500px" height="500px"></x-ms-webview>
</body>
</html>
When simply adding a x-ms-webview control, the default size is 100px by 100px. You can specify the width and height just like an iframe, or you can use CSS styles for the iframe.
Navigation with x-ms-webview
Now that you have a sense for the x-ms-webview control and how it loads content, understanding the navigation workflow helps you control when things happen. One common problem for Windows 8 developers, when incorporating web content in their apps, was detecting when the entire DOM was loaded in order to start manipulating DOM objects or executing JavaScript. x-ms-webview supports the navigation flow events, as well as the navigation history, in order to give you control over web content loading in your app.
Navigation history properties and methods
API for handling x-ms-webview navigation
navigate() – When you call this API you can also subscribe to these events that are raised during a Navigation request:
DOM event name |
Description |
MSWebViewNavigationStarting |
Indicates the WebView is starting to navigate |
MSWebViewContentLoading |
The HTML content is downloaded and is being loaded into the control |
MSWebViewDOMContentLoaded |
Indicates that the main DOM elements have finished loading |
MSWebViewNavigationCompleted |
Indicates the navigation is complete, and all media elements are rendered |
MSWebViewUnviewableContentIdentified |
The WebView found the content was not HTML |
In addition to these events in case the site being loaded has iframes there is another set of events exposed for you to detect sub content loading via iframes:
DOM event name |
Description |
MSWebViewFrameNavigationStarting |
Indicates an iframe within a WebView is starting to navigate |
MSWebViewFrameContentLoading |
The HTML content is downloaded and is being loaded into the control iframe |
MSWebViewFrameDOMContentLoaded |
Indicates that the main DOM elements have finished loading in the iframe |
MSWebViewFrameNavigationCompleted |
Indicates the navigation is complete, and all media elements are rendered in the iframe |
x-ms-webview navigation history. Detecting if a user has navigated inside the x-ms-webview is simple with these properties and methods.
Properties:
- canGoBack – returns true/false if the control can navigate back.
- canGoForward – returns true/false if the control can navigate forward.
Methods:
- goBack() – Navigates the control to the previous location.
- goForward() – Navigates the control to the next location.
JavaScript navigation history
HTML
…
<body>
<p>Content goes here</p>
<x-ms-webview id="webview" src="http://t.msn.com"></x-ms-webview>
<div id="webviewNavigationButtons"><button id="goBack" onclick="goBack()" disabled>Back</button><button id="goForward" onclick="goForward()" disabled>Forward</button></div>
</body>
…
CSS
#webview{
width:500px;
height:500px;
}
button{
margin-right:15px;
}
JavaScript
webview.addEventListener("MSWebViewContentLoading", webViewContentLoading);
function webViewContentLoading(e) {
document.getElementById("goBack").disabled = !webview.canGoBack;
document.getElementById("goForward").disabled = !webview.canGoForward;
}
function goBack() {
webview.goBack();
}
function goForward() {
webview.goForward();
}
Content sources
Now that we know the WebView is a straightforward HTML element, let’s look at the types of content you can load into a x-ms-webview and the different methods available for each.
Graphic showing the methods designed for internet and local content navigation
Internet content
Integrating online content from the internet is the most common way to use an x-ms-webview. For example, one of the demo apps we built for Build 2013 showed a local product catalog, that used an x-ms-webview to handle the customer checkout with an existing website. For more details, see John Hazen’s Build talk, WebView: Bringing the Web to Your App.
One way to load web content in an x-ms-webview is to set the src attribute on the HTML element. Setting the src attribute automatically calls the navigate() method and passes in the attributes URI string.
<x-ms-webview id="webview" src="http://www.bing.com"></x-ms-webview>
Another way to load web content is to call navigate() directly. The x-ms-webview control navigates and loads websites, even ones that traditionally wouldn’t be loaded by an iframe due to framebusting.
NOTE: If the src attribute of the HTML element is set, and a call is made to the navigate method on load of an app, double navigation occurs.
Navigation supports different protocols to access both web and local content. JavaScript examples are provided for each protocol.
This table contains the different URI schemas supported by x-ms-webview. See MSDN for more details about these URI schemas.
URI schema |
Description |
http:// |
Arbitrary web content |
https:// |
Web content over secure socket layer |
ms-appdata:/// |
Content placed in the app’s local state folder |
ms-appx-web:/// |
Content a developer has included within their app package but should be treated as web content. This HTML cannot access Windows RT APIs |
ms-local-stream:// |
Content a developer has loaded into a memory stream |
JavaScript navigation to web content
//Navigate the webview
webview.navigate(new Windows.Foundation.Uri("http://www.msn.com"));
//Navigate to SSL content
webview.navigate(new Windows.Foundation.Uri(https://www.facebook.com));
navigateWithHttpRequestMessage() – The control supports adding HTTP request information before navigating. This allows the server to potentially take action on the request before returning content.
JavaScript – Get example
// Create a URI describing the site to navigate to
var siteUrl = new Windows.Foundation.Uri("http://www.msn.com");
// Specify the type of request
var httpRequestMessage = new Windows.Web.Http.HttpRequestMessage(Windows.Web.Http.HttpMethod.get, siteUrl);
// Append headers to request the webserver to check against the cache
httpRequestMessage.headers.append("Cache-Control", "no-cache");
httpRequestMessage.headers.append("Pragma", "no-cache");
// Navigate the WebView with the request info
webview.navigateWithHttpRequestMessage(httpRequestMessage);
JavaScript – Post example
// Create a URI describing the site to navigate to
var siteUrl = new Windows.Foundation.Uri("http://www.msn.com");
// Specify the type of request
var httpRequestMessage = new Windows.Web.Http.HttpRequestMessage(Windows.Web.Http.HttpMethod.post, siteUrl);
// Set post data
httpRequestMessage.Content = new Windows.Web.Http.HttpStringContent("cookie=abcxyz123");
// Navigate the WebView with the request info
webview.navigateWithHttpRequestMessage(httpRequestMessage); webview.navigateWithHttpRequestMessage(httpRequestMessage);
Local content
You can also point an x-ms-webview at locally stored content that was either included in your package, or downloaded into a local state folder. Creating offline experiences for apps helps to ensure no matter what a person’s connected state is, you’re still able to provide an optimal app experience. A common scenario for this is to enable a game to run when not connected to the network.
In other cases your app might need better performance for loading and rendering content that can only be achieved through local content. A common scenario for this pattern is creating an electronic publication (EPUB) reader. EPUB 3 formats support rich HTML 5, CSS, and JavaScript content, which is easily displayed in the x-ms-webview control. Let’s look at the APIs you can use to load local content into the x-ms-webview.
JavaScript – Navigating to local content
//Navigate to content included in your appx package
webview.navigate("ms-appx-web:///localPage.html");
//Navigate to content stored in your local state directory
webview.navigate("ms-appdata:///local/MyAppsLocalFolder/local.html");
JavaScript – Navigating to local content
While the simple example above works, you can use this example to create a simple HTML file in your local state directory.
// Vars for local storage
var applicationData = Windows.Storage.ApplicationData.current;
var localFolder = applicationData.localFolder;
// Write HTML file locally
function writeLocalHTMLThenNavigate() {
localFolder.createFolderAsync("MyAppsLocalFolder", Windows.Storage.CreationCollisionOption.openIfExists).then(function (folder) {
localFolder.createFileAsync("MyAppsLocalFolderlocal.html",
Windows.Storage.CreationCollisionOption.replaceExisting).then(function (htmlFile) {
var html = "<!DOCTYPE html><html><head>";
html += "<title>Local HTML Content</title></head>";
html += "<body><h1>Local HTML Content</h1></body></html>";
Windows.Storage.FileIO.writeTextAsync(htmlFile, html).then(function (e) {
document.getElementById("webview").navigate("ms-appdata:///local/MyAppsLocalFolder/local.html");
});
}).done(function () {
console.log("Local.html file created successfully.");
});
})
}
writeLocalHTMLThenNavigate();
navigateToLocalStreamUri() – This method provides the ability to intercept content before it’s loaded into the control. A common scenario is to decode local content before the output is sent to the x-ms-webview. An IUriToStreamResolver must be written in either C#/VB, or C++, so JavaScript developers need to include an extra project type in order to utilize this functionality. The SDK has an example of this baked in.
Example – Calling a navigateToLocalStreamUri from JavaScript, relies on underlying C# code to perform the stream resolution. For the complete example, look at the SDK.
JavaScript – Navigating to a stream
function navigateToStreamWithCSResolver() {
var contentUri = document.getElementById("webview").buildLocalStreamUri("NavigateToStream", "simple_example.html");
var uriResolver = new SDK.WebViewSampleCS.StreamUriResolver();
document.getElementById("webview").navigateToLocalStreamUri(contentUri, uriResolver);
}
navigateToString() – Constructing and rendering dynamic HTML is common for apps that want to use HTML layout for a small amount of content, either for a quick overlay of content, or a rich in-app ad experience.
JavaScript – Navigating to string HTML content
var strHTML = "<!DOCTYPE HTML>";
strHTML += "<HTML>";
strHTML += "<HEAD><TITLE>NavigateToString() Example</TITLE></HEAD>";
strHTML += "<BODY><H1>HTML markup from a string</H1></BODY>";
strHTML += "</HTML>";
webview.navigateToString(strHTML);
App to WebView communication
It’s very common that your app will need to communicate with the web content within your x-ms-webview to create the appropriate user experience. You can communicate between the app and the website by integrating your app with Windows features like AppBar, Search charm, or Share charm to send messages between the two.
The invokeScriptAsync method is similar to using a postMessage() call into an <iframe> element.
The app is always allowed to use the controls invokeScriptAsync method call to execute script inside the embedded content. However, the embedded content has some restrictions when trying to send messages via the MSWebViewScriptNotify event to the parent app container.
Host app to webcontent communication graphic
This table shows when the webview control allows messages to flow to the parent app.
URI schema |
MSWebViewScriptNotify |
http:// |
No |
https:// |
Yes* |
ms-appdata:/// |
No** |
ms-appx-web:/// |
Yes |
ms-local-stream:// |
Yes |
*Only if ContentURIs are declared in the Package.appxmanifest **Can use a UriResolver to have ScriptNotify enabled |
DOM event name |
Description |
MSWebViewScriptNotify |
This event fires when a page inside the webview calls the window.external.notify method. |
JavaScript
webview.addEventListener("MSWebViewScriptNotify", scriptNotify);
function scriptNotify(e) {
// check URI sending the notification
if (e.callingUri == "https://myUri.com") {
doSomething(e.value);
}
}
JavaScript
var operation = webview.invokeScriptAsync("startAnimation", "500");
operation.oncomplete = function (e) {
console.log(e.target.result);
};
operation.start();
Graphic showing the capturePreviewToBlogAsync method call to output image format.
You can now get a screenshot of the content within an x-ms-webview built into the API. <iframe> can’t render arbitrary HTML into a bitmap. However, the x-ms-webview capturePreviewToBlobAsync() method allows for producing an image.
JavaScript
var operation = webview.capturePreviewToBlobAsync();
operation.oncomplete = function (e) {
var url = window.URL.createObjectURL(e.target.result);
document.getElementById("imagetag").src = url;
};
operation.start();
Graphic showing Selected HTML (text+image) to output in the host application.
You are able to easily share content from an x-ms-webview control via the Windows Share contract. A built in API can extract content selected by a user. This content can then be returned to your app for sharing with other apps that support the share target contract.
JavaScript – Get user selected content
var operation = webview.captureSelectedContentToDataPackageAsync();
operation.oncomplete = function (e) {
// Check for a valid selection
if (e.target.result) {
var data = e.target.result.getView();
if (data.contains(Windows.ApplicationModel.DataTransfer.StandardDataFormats.html){
// Data contains HTML, so grab it!
data.getHtmlFormatAsync().then(function (htmlFormat) {
var htmlFragment = Windows.ApplicationModel.DataTransfer.HtmlFormatHelper.getStaticFragment(htmlFormat);
document.getElementById("output").innerHTML = htmlFragment;
});
}
}
};
operation.start();
Want to learn more?
As you can see, the x-ms-webview is a powerful tool for displaying web content in your HTML and JS apps. If you already have a Windows 8 app that uses iframes to embed web content, I highly recommend updating to the x-ms-webview in Windows 8.1. And if you’re starting a new app, definitely use the x-ms-webview control from the beginning.
If you’re interested in learning more about x-ms-webview, check out these great links
- Documentation
- Code samples
- Build talk
- Other blogs
-Kevin Hill, Senior Program Manager, Windows