Search code examples
javascriptwindowsuwpnotifyappdata

How to Javascript notify() with ms-appdata on Windows 10 UWP


Due to file-writing security issues we have tried to switch our app from using ms-appx-web: to using ms-appdata:. But it immediately fails because we rely on window.external.notify() which works fine with ms-appx-web: but seems to behave as a no-op with ms-appdata:. As a test we loaded the following html into a WebView object:

<html>
<head>
    <script>
        function demofunc( str ) {
            document.getElementById("demo").innerHTML += str;
            window.external.notify( str );
        }
    </script>
</head>
<body onLoad="demofunc('demofunc()');">
    demo &lt;body&gt;
    <div id="demo"></div>
</body>
</html>

which produces this result as it should:

demo <body> 
demofunc()

BUT, does NOT produce a popup message of any kind. Why? Clearly the demofunc() method is being called to add the second line of output in the demo div, but window.external.notify() is not producing a popup message. Are there special rules regarding notify() along with ms-appdata:?

Update - the question Can't run javascript alerts in universal app's webview at payment gateway is similar and works for ms-appx-web: but not for ms-appdata:. That question catches ScriptNotify() which then uses Windows.UI.Popups.MessageDialog to popup a dialog. With ms-appx-web: the ScriptNotify() is called but with ms-appdata: the ScriptNotify() is not called. That is our problem, there is no popup occurring.


Solution

  • But it immediately fails because we rely on window.external.notify() which works fine with ms-appx-web: but seems to behave as a no-op with ms-appdata:.

    For your scenario please use the scheme ms-local-stream:///, rather than ms-appdata:///. And I have tested ms-local-stream:/// scheme, it is working pretty well.

    To use the NavigateToLocalStreamUri method, you must pass in an IUriToStreamResolver implementation that translates a URI pattern into a content stream. Please reference the following code.

    StreamUriWinRTResolver

    public sealed class StreamUriWinRTResolver : IUriToStreamResolver
        {
            /// <summary>
            /// The entry point for resolving a Uri to a stream.
            /// </summary>
            /// <param name="uri"></param>
            /// <returns></returns>
            public IAsyncOperation<IInputStream> UriToStreamAsync(Uri uri)
            {
                if (uri == null)
                {
                    throw new Exception();
                }
                string path = uri.AbsolutePath;
                // Because of the signature of this method, it can't use await, so we
                // call into a separate helper method that can use the C# await pattern.
                return getContent(path).AsAsyncOperation();
            }
    
            /// <summary>
            /// Helper that maps the path to package content and resolves the Uri
            /// Uses the C# await pattern to coordinate async operations
            /// </summary>
            private async Task<IInputStream> getContent(string path)
            {
                // We use a package folder as the source, but the same principle should apply
                // when supplying content from other locations
                try
                {
                    Uri localUri = new Uri("ms-appdata:///local" + path);
                    StorageFile f = await StorageFile.GetFileFromApplicationUriAsync(localUri);
                    IRandomAccessStream stream = await f.OpenAsync(FileAccessMode.Read);
                    return stream.GetInputStreamAt(0);
                }
                catch (Exception) { throw new Exception("Invalid path"); }
            }
        }
    

    MainPage

    public MainPage()
     {
         this.InitializeComponent();
         MyWebView.ScriptNotify += MyWebView_ScriptNotify;
         Uri url = MyWebView.BuildLocalStreamUri("MyTag", "/Test/HomePage.html");
         StreamUriWinRTResolver myResolver = new StreamUriWinRTResolver();
         MyWebView.NavigateToLocalStreamUri(url, myResolver);
     }
    

    I have uploaded the code sample to github. Please check.