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(args.target.result); };
op.onerror = function (e) { ed(e); };
op.start();
});
In this case, the result you care about us args.target.result 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, args.target.result 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) {
output.navigateToString(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 args.target.result 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(args.target.result.msDetachStream());
cd(bitmapStream);
};

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

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.


Comments are closed