The GoBack and GoForward methods on the UWP WebView (x-ms-webview in HTML, Windows.UI.Xaml.Controls.WebView in XAML, and Windows.Web.UI.Interop.WebViewControl in Win32) act the same as the Back
and Forward buttons in the Edge browser. They don't necessarily change the top level document of the WebView. If inside the webview an iframe navigates then that navigation will be recorded in
the forward/back history and the GoBack / GoForward call may result in navigating that iframe. This makes sense as an end user using the Edge browser since if I click a link to navigate one place
and then hit Back I expect to sort of undo that most recent navigation regardless of if that navigation happened in an iframe or the top level document.
If that doesn't make sense for your application and you want to navigate forward or back ignoring iframe navigates, unfortunately there's no perfect workaround.
One workaround could be to try calling GoBack and then checking if a FrameNavigationStarting event fires or a NavigationStarting event fires. If a frame navigates then try calling GoBack again.
There could be async races in this case since other navigates could come in and send you the wrong signal and interrupt your multi step GoBack operation.
You could also try keeping track of all top level document navigations and manually navigate back to the URIs you care about. However, GoBack and GoForward also restore some amount of user state
(form fills etc) in addition to navigating. Manually calling navigate will not give this same behavior.
JSBrowser is a basic browser built as a Win10 JavaScript UWP app around the WebView HTML element. Its fun and relatively simple to
implement tiny browser features in JavaScript and in this post I'm implementing crash resistance.
The normal DOM mechanisms for creating an HTML WebView create an in-process WebView, in which the WebView runs on a
unique UI thread. But we can use the MSWebView constructor instead to create an out-of-process WebView in which the WebView runs in its own distinct WebView process. Unlike an in-process WebView,
Web content running in an out-of-process WebView can only crash the WebView process and not the app process.
this.replaceWebView = () => { let webview = document.querySelector("#WebView"); // Cannot access webview.src - anything that would need to communicate with the webview process may fail let oldSrc = browser.currentUrl; const webviewParent = webview.parentElement; webviewParent.removeChild(webview); webview = new MSWebView(); Object.assign(this, { "webview": webview }); webview.setAttribute("id", "WebView");
// During startup our currentUrl field is blank. If the WebView has crashed // and we were on a URI then we may obtain it from this property. if (browser.currentUrl && browser.currentUrl != "") { this.trigger("newWebview"); this.navigateTo(browser.currentUrl); } webviewParent.appendChild(webview);
I run replaceWebView during startup to replace the in-process WebView created via HTML markup with an out-of-process WebView. I could be doing more to dynamically copy styles, attributes, etc but
I know what I need to set on the WebView and just do that.
When a WebView process crashes the corresponding WebView object is no longer useful and a new WebView element must be created. In fact if the old WebView object is used it may throw and will no
longer have valid state. Accordingly when the WebView crashes I run replaceWebView again. Additionally, I need to store the last URI we've navigated to (browser.currentUrl in the above) since the
crashed WebView object won't know what URI it is on after it crashes.
webview.addEventListener("MSWebViewProcessExited", () => { if (browser.currentUrl === browser.lastCrashUrl) { ++browser.lastCrashUrlCrashCount; } else { browser.lastCrashUrl = browser.currentUrl; browser.lastCrashUrlCrashCount = 1; } // If we crash again and again on the same URI, maybe stop trying to load that URI. if (browser.lastCrashUrlCrashCount >= 3) { browser.lastCrashUrl = ""; browser.lastCrashUrlCrashCount = 0; browser.currentUrl = browser.startPage; } this.replaceWebView(); });
I also keep track of the last URI that we recovered and how many times we've recovered that same URI. If the same URI crashes more than 3 times in a row then I assume that it will keep happening
and I navigate to the start URI instead.
In Win8.1 JavaScript UWP apps we supported multiple windows using MSApp DOM APIs. In Win10 we use window.open and window and a new MSApp API getViewId and the previous MSApp APIs are gone:
We use window.open and window for creating new windows, but then to interact with WinRT APIs we add the MSApp.getViewId API. It takes a window object as a parameter and returns a viewId number
that can be used with the various Windows.UI.ViewManagement.ApplicationViewSwitcher APIs.
Delaying Visibility
Views in WinRT normally start hidden and the end developer uses something like TryShowAsStandaloneAsync
to display the view once it is fully prepared. In the web world, window.open shows a window immediately and the end user can watch as content is loaded and rendered. To have your new windows act
like views in WinRT and not display immediately we have added a window.open option. For example let newWindow = window.open("https://example.com", null, "msHideView=yes");
Primary Window Differences
The primary window that is initially opened by the OS acts differently than the secondary windows that it opens:
Primary
Secondary
window.open
Allowed
Disallowed
window.close
Close app
Close window
Navigation restrictions
ACUR only
No restrictions
The restriction on secondary windows such that they cannot open secondary windows could change in the future depending on feedback.
Same Origin Communication Restrictions
Lastly, there is a very difficult technical issue preventing us from properly supporting synchronous, same-origin, cross-window, script calls. That is, when you open a window that's same origin,
script in one window is allowed to directly call functions in the other window and some of these calls will fail. postMessage calls work just fine and is the recommended way to do things if
that's possible for you. Otherwise we continue to work on improving this.
The documentation for printing in JavaScript UWP apps is out of date as it all references MSApp.getHtmlPrintDocumentSource but that method has been replaced by MSApp.getHtmlPrintDocumentSourceAsync since WinPhone
8.1.
Background
Previous to WinPhone 8.1 the WebView's HTML content ran on the UI thread of the app. This is troublesome for rendering arbitrary web content since in the extreme case the JavaScript of some
arbitrary web page might just sit in a loop and never return control to your app's UI. With WinPhone 8.1 we added off thread WebView in which the WebView runs HTML content on a separate UI
thread.
Off thread WebView required changing our MSApp.getHtmlPrintDocumentSource API which could no longer synchronously produce an HtmlPrintDocumentSource. With WebViews running on their own threads it
may take some time for them to generate their print content for the HtmlPrintDocumentSource and we don't want to hang the app's UI thread in the interim. So the MSApp.getHtmlPrintDocumentSource
API was replaced with MSApp.getHtmlPrintDocumentSourceAsync which returns a promise the resolved value of which is the eventual HtmlPrintDocumentSource.
Sample
However, the usage of the API is otherwise unchanged. So in sample code you see referencing MSApp.getHtmlPrintDocumentSource the sample code is still reasonable but you need to call
MSApp.getHtmlPrintDocumentSourceAsync instead and wait for the promise to complete. For example the PrintManager docs has an example implementing a PrintTaskRequested event handler in a JavaScript UWP app.
function onPrintTaskRequested(printEvent) { var printTask = printEvent.request.createPrintTask("Print Sample", function (args) { args.setSource(MSApp.getHtmlPrintDocumentSource(document)); });
Instead we need to obtain a deferral in the event handler so we can asynchronously wait for getHtmlPrintDocumentSourceAsync to complete:
The x-ms-webview HTML element has the void addWebAllowedObject(string name, any value) method and the webview
XAML element has the void AddWebAllowedObject(String name,
Object value) method. The object parameter is projected into the webview’s top-level HTML document’s script engine as a new property on the global object with property name set to the name
parameter. It is not injected into the current document but rather it is projected during initialization of the next top-level HTML document to which the webview navigates.
Lifetime
If AddWebAllowedObject is called during a NavigationStarting event handler the object will be injected into the document resulting from the navigation corresponding to that event.
If AddWebAllowedObject is called outside of the NavigationStarting event handler it will apply to the navigation corresponding to the next explicit navigate method called on the webview or the
navigation corresponding to the next NavigationStarting event handler that fires, whichever comes first.
To avoid this potential race, you should use AddWebAllowedObject in one of two ways: 1. During a NavigationStarting event handler, 2. Before calling a Navigate method and without returning to the
main loop.
If called both before calling a navigate method and in the NavigationStarting event handler then the result is the aggregate of all those calls.
If called multiple times for the same document with the same name the last call wins and the previous are silently ignored.
If AddWebAllowedObject is called for a navigation and that navigation fails or redirects to a different URI, the AddWebAllowedObject call is silently ignored.
After successfully adding an object to a document, the object will no longer be projected once a navigation to a new document occurs.
WinRT access
If AddWebAllowedObject is called for a document with All WinRT access then projection will succeed and the object will be added.
If AddWebAllowedObject is called for a document which has a URI which has no declared WinRT access via ApplicationContentUriRules then Allow for web only WinRT access is given to that document.
If the document has Allow for web only WinRT access then projection will succeed only if the object’s runtimeclass has the Windows.Foundation.Metadata.AllowForWeb metadata attribute.
Object requirements
The object must implement the IAgileObject interface. Because the XAML and HTML webview elements run on ASTA view threads and the webview’s content’s JavaScript thread runs on another ASTA thread
a developer should not create their non-agile runtimeclass on the view thread. To encourage end developers to do this correctly we require the object implements IAgileObject.
Property name
The name parameter must be a valid JavaScript property name, otherwise the call will fail silently. If the name is already a property name on the global object, that property is overwritten if
the property is configurable. Non-configurable properties on the global object are not overwritten and the AddWebAllowedObject call fails silently. On success, the projected property is writable,
configurable, and enumerable.
Errors
Some errors as described above fail silently. Other issues, such as lack of IAgileObject or lack of the AllowForWeb attribute result in an error in the JavaScript developer console.
JavaScript Microsoft Store apps have some details related to activation that are specific to JavaScript Store apps and that are poorly documented which I’ll describe here.
StartPage syntax
The StartPage attributes in the AppxManifest.xml (Package/Applications/Application/@StartPage, Package/Applications/Extensions/Extension/@StartPage) define the HTML page entry point for
that kind of activation. That is, Application/@StartPage defines the entry point for tile activation, Extension[@Category="windows.protocol"]/@StartPage defines the entry point for URI handling
activation, etc. There are two kinds of supported values in StartPage attributes: relative Windows file paths and absolute URIs. If the attribute doesn’t parse as an absolute URI then it is
instead interpreted as relative Windows file path.
This implies a few things that I’ll declare explicitly here. Windows file paths, unlike URIs, don’t have a query or fragment, so if you are using a relative Windows file path for your StartPage
attribute you cannot include anything like ‘?param=value’ at the end. Absolute URIs use percent-encoding for reserved characters like ‘%’ and ‘#’. If you have a ‘#’ in your HTML filename then you
need to percent-encode that ‘#’ for a URI and not for a relative Windows file path.
If you specify a relative Windows file path, it is turned into an ms-appx URI by changing all backslashes to forward slashes, percent-encoding reserved characters, and combining the result with a
base URI of ms-appx:///. Accordingly the relative Windows file paths are relative to the root of your package. If you are using a relative Windows file path as your StartPage and need to switch
to using a URI so you can include a query or fragment, you can follow the same steps above.
StartPage validity
The validity of the StartPage is not determined before activation. If the StartPage is a relative Windows file path for a file that doesn’t exist, or an absolute URI that is not in the
Application Content URI Rules, or something that doesn’t parse as a Windows file path or URI, or otherwise an absolute URI that fails to resolve (404, bad hostname, etc etc) then the JavaScript
app will navigate to the app’s navigation error page (perhaps more on that in a future blog post). Just to call it out explicitly because I have personally accidentally done this: StartPage URIs
are not automatically included in the Application Content URI Rules and if you forget to include your StartPage in your ACUR you will always fail to navigate to that StartPage.
StartPage navigation
When your app is activated for a particular activation kind, the StartPage value from the entry in your app’s manifest that corresponds to that activation kind is used as the navigation target.
If the app is not already running, the app is activated, navigated to that StartPage value and then the Windows.UI.WebUI.WebUIApplication activated event is fired (more details on
the order of various events in a moment). If, however, your app is already running and an activation occurs, we navigate or don’t navigate to the corresponding StartPage depending on the current
page of the app. Take the app’s current top level document’s URI and if after removing the fragment it already matches the StartPage value then we won’t navigate and will jump straight to firing
the WebUIApplication activated event.
Since navigating the top-level document means destroying the current JavaScript engine instance and losing all your state, this behavior might be a problem for you. If so, you can use the
MSApp.pageHandlesAllApplicationActivations(true) API to always skip navigating to the StartPage and instead always jump straight to firing the WebUIApplication activated event. This
does require of course that all of your pages all handle all activation kinds about which any part of your app cares.
Parsing WinMD files, the containers of WinRT API metadata, is relatively simple using the appropriate .NET reflection APIs. However, figuring out which reflection APIs to use is not obvious. I've
got a completed C sharp class parsing WinMD files that you can check out for reference.
In this event handler you must resolve the unknown namespace reference by adding an assembly to the NamespaceResolveEventArgs's ResolvedAssemblies property. If you're only interested in OS system
WinMD files you can use System.Reflection.InteropServices.WindowsRuntimeMetadata.ResolveNamespace to
turn a namespace into the expected OS system WinMD path and turn that path into an assembly with ReflectionOnlyLoad.
First off calling WinRT from PowerShell involves a strange syntax. If you want to use a class you write [-Class-,-Namespace-,ContentType=WindowsRuntime] first to tell PowerShell about the type.
For example here I create a ToastNotification object:
With
this I can call WinRT methods and this is enough to show a toast but to handle the click requires a little more work.
To handle the user clicking on the toast I need to listen to the Activated event on the Toast object. However Register-ObjectEvent doesn't handle WinRT events. To work around this I created a
.NET event wrapper class to turn the WinRT event into a .NET event that Register-ObjectEvent can
handle. This is based on Keith Hill's blog post on calling WinRT async methods in
PowerShell. With the event wrapper class I can run the following to subscribe to the event:
function WrapToastEvent { param($target, $eventName);
To handle the Activated event I want to put focus back on the PowerShell window that created the toast. To do this I need to call the Win32 function SetForegroundWindow. Doing so from PowerShell
is surprisingly easy. First you must tell PowerShell about the function:
Add-Type @" using System; using System.Runtime.InteropServices; public class PInvoke { [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool SetForegroundWindow(IntPtr hwnd); } "@
But figuring out the HWND to give to SetForegroundWindow isn't totally straight forward. Get-Process exposes a MainWindowHandle property but if you start a cmd.exe prompt and then run PowerShell
inside of that, the PowerShell process has 0 for its MainWindowHandle property. We must follow up process parents until we find one with a MainWindowHandle:
This 30 day mission will help our researchers learn how isolation and close quarters affect individual and group behavior. This study at our Johnson Space Center prepares us for long duration
space missions, like a trip to an asteroid or even to Mars.
The Human Research Exploration Analog (HERA) that the crew members will be living in is one compact, science-making house. But
unlike in a normal house, these inhabitants won’t go outside for 30 days. Their communication with the rest of planet Earth will also be very limited, and they won’t have any access to
internet. So no checking social media kids!
The only people they will talk with regularly are mission control and each other.
The crew member selection process is based on a number of criteria, including the same criteria for astronaut selection.
What will they be doing?
Because this mission simulates a 715-day journey to a Near-Earth asteroid, the four crew members will complete activities similar to what would happen during an outbound transit, on location at
the asteroid, and the return transit phases of a mission (just in a bit of an accelerated timeframe). This simulation means that even when communicating with mission control, there will be a
delay on all communications ranging from 1 to 10 minutes each way. The crew will also perform virtual spacewalk missions once they reach their destination, where they will inspect the asteroid
and collect samples from it.
A few other details:
The crew follows a timeline that is similar to one used for the ISS crew.
They work 16 hours a day, Monday through Friday. This includes time for daily planning, conferences, meals and exercises.
They will be growing and taking care of plants and brine shrimp, which they will analyze and document.
But beware! While we do all we can to avoid crises during missions, crews need to be able to respond in the event of an emergency. The HERA crew will conduct a couple of emergency scenario
simulations, including one that will require them to maneuver through a debris field during the Earth-bound phase of the mission.
Throughout the mission, researchers will gather information about cohabitation, teamwork, team cohesion, mood, performance and overall well-being. The crew members will be tracked by numerous
devices that each capture different types of data.
Past HERA crew members wore a sensor that recorded heart rate, distance, motion and sound intensity. When crew members were
working together, the sensor would also record their proximity as well, helping investigators learn about team cohesion.
Researchers also learned about how crew members react to stress by recording and analyzing verbal interactions and by analyzing “markers” in blood and saliva samples.
In total, this mission will include 19 individual investigations across key human research elements. From psychological to physiological experiments, the crew members will help prepare us for
future missions.
This 30 day mission will help our researchers learn how isolation and close quarters affect individual and group behavior. This study at our Johnson Space Center prepares us for long duration
space missions, like a trip to an asteroid or even to Mars.
The Human Research Exploration Analog (HERA) that the crew members will be living in is one compact, science-making house. But
unlike in a normal house, these inhabitants won’t go outside for 30 days. Their communication with the rest of planet Earth will also be very limited, and they won’t have any access to
internet. So no checking social media kids!
The only people they will talk with regularly are mission control and each other.
The crew member selection process is based on a number of criteria, including the same criteria for astronaut selection.
What will they be doing?
Because this mission simulates a 715-day journey to a Near-Earth asteroid, the four crew members will complete activities similar to what would happen during an outbound transit, on location at
the asteroid, and the return transit phases of a mission (just in a bit of an accelerated timeframe). This simulation means that even when communicating with mission control, there will be a
delay on all communications ranging from 1 to 10 minutes each way. The crew will also perform virtual spacewalk missions once they reach their destination, where they will inspect the asteroid
and collect samples from it.
A few other details:
The crew follows a timeline that is similar to one used for the ISS crew.
They work 16 hours a day, Monday through Friday. This includes time for daily planning, conferences, meals and exercises.
They will be growing and taking care of plants and brine shrimp, which they will analyze and document.
But beware! While we do all we can to avoid crises during missions, crews need to be able to respond in the event of an emergency. The HERA crew will conduct a couple of emergency scenario
simulations, including one that will require them to maneuver through a debris field during the Earth-bound phase of the mission.
Throughout the mission, researchers will gather information about cohabitation, teamwork, team cohesion, mood, performance and overall well-being. The crew members will be tracked by numerous
devices that each capture different types of data.
Past HERA crew members wore a sensor that recorded heart rate, distance, motion and sound intensity. When crew members were
working together, the sensor would also record their proximity as well, helping investigators learn about team cohesion.
Researchers also learned about how crew members react to stress by recording and analyzing verbal interactions and by analyzing “markers” in blood and saliva samples.
In total, this mission will include 19 individual investigations across key human research elements. From psychological to physiological experiments, the crew members will help prepare us for
future missions.
The story of Chris Knight, living in isolation in the woods of Maine for 27 years.
'Anyone who reveals what he's learned, Chris told me, is not by his definition a true hermit. Chris had come around on the idea of himself as a hermit, and eventually embraced it. When I
mentioned Thoreau, who spent two years at Walden, Chris dismissed him with a single word: “dilettante.”'
'But still, I pressed on, there must have been some grand insight revealed to him in the wild…”Get enough sleep.”'
I don’t want to brag, but I’ve been telling that people all along and I didn’t have to live alone in the woods for decades.
Some time back while I was working on getting the Javascript Windows Store app platform running on Windows Phone
(now available on the last Windows Phone release!) I had an interesting bug that in retrospect is amusing.
I had just finished a work item to get accessibility working for JS WinPhone apps when I got a new bug: With some set of JS apps, accessibility appeared to be totally broken. At that time in
development the only mechanism we had to test accessibility was a test tool that runs on the PC, connects to the phone, and dumps out the accessibility tree of whatever app is running on the
phone. In this bug, the tool would spin for a while and then timeout with an error and no accessibility information.
My first thought was this was an issue in my new accessibility code. However, debugging with breakpoints on my code I could see none of my code was run nor the code that should call it. The code
that called that code was a more generic messaging system that hit my breakpoints constantly.
Rather than trying to work backward from the failure point, I decided to try and narrow down the repro and work forwards from there. One thing all the apps with the bug had in common was their
usage of WinJS, but not all WinJS apps demonstrated the issue. Using a binary search approach on one such app I removed unrelated app code until all that
was left was the app's usage of the WinJS AppBar and the bug still occurred. I replaced the WinJS AppBar usage with direct usage of the underlying AppBar WinRT APIs and continued.
Only some calls to the AppBar WinRT object produced the issue:
Just
setting the background color appeared to cause the issue and I didn't even have to display the AppBar. Through additional trial and error I was blown away to discover that some colors I would set
caused the issue and other colors did not. Black wouldn't cause the issue but transparent black would. So would aqua but not white.
I eventually realized that predefined WinRT color values like Windows.UI.Colors.aqua would cause the issue while JS literal based colors didn't cause the issue (Windows.UI.Color is a WinRT struct which projects in JS as a JS literal object with the struct members as JS
object properties so its easy to write something like {r: 0, g: 0, b: 0, a: 0} to make a color) and I had been mixing both in my tests without realizing there would be a difference.
I debugged into the backgroundColor property setter that consumed the WinRT color struct to see what was different between Windows.UI.Colors.black and {a: 1, r: 0, g: 0, b: 0} and
found the two structs to be byte wise exactly the same.
On a hunch I tried my test app with only a reference to the color and otherwise no interaction with the AppBar and not doing anything with the actual reference to the color:
Windows.UI.Colors.black;. This too caused the issue. I knew that the implementation for these WinRT const values live in a DLL and guessed that something in the code to create these
predefined colors was causing the issue. I debugged in and no luck. Now I also have experienced crusty code that would do exciting things in its DllMain, the function that's called when a DLL is loaded into the process so I tried modifying my
C++ code to simply LoadLibrary the DLL containing the WinRT color definition, windows.ui.xaml.dll and found the bug still occurred! A short lived moment of relief as the world seemed to make
sense again.
Debugging into DllMain nothing interesting happened. There were interesting calls in there to be sure, but all of them behind conditions that were false. I was again stumped. On another hunch I
tried renaming the DLL and only LoadLibrary'ing it and the bug went away. I took a different DLL renamed it windows.ui.xaml.dll and tried LoadLibrary'ing that and the bug came back. Just the name
of the DLL was causing the issue.
I searched for the DLL name in our source code index and found hits in the accessibility tool. Grinning I opened the source to find that the accessibility tool's phone side service was trying to
determine if a process belonged to a XAML app or not because XAML apps had a different accessibility contract. It did this by checking to see if windows.ui.xaml.dll was loaded in the target
process.
At this point I got to fix my main issue and open several new bugs for the variety of problems I had just run into. This is a how to on writing software that is difficult to debug.
Shortly after Disneyland opened in 1955, the founder of Frito-Lay got permission from Walt Disney to open a restaurant in Frontierland with a Mexican-ish theme. “Casa de
Fritos” was, unsurprisingly, all about the Fritos. Customers got free Fritos, and Fritos were incorporated into many of the dishes. Fritos were dispensed by an animatronic vending machine
that featured the terrifying “Frito Kid”asking his assistant “Klondike” to bring
the bag up from a mineshaft. I guess the conceit is that Fritos were mined by Forty-Niners?
Casa de Fritos contracted their tortilla production to a company called Alex Foods. One of the salesmen from Alex Foods, making a delivery to Casa de Fritos, noticed stale tortillas in the
garbage and gave the cook a little tip: fry them and sell them as chips instead of throwing them away. Casa de Fritos began making these fried, seasoned chips to enormous
success, but didn’t report this new menu item to the Frito-Lay company.
Eventually Frito-Lay found out what they were doing with the chips, packaged them, and sold them by the truckload. See, dumpster diving works out sometimes!