0

I am developing an application for Xamarin.UWP which is trying to inject Javascript into a local html file (uri: ms-appdata:///local/index.html) like so:

async void OnWebViewNavigationCompleted(WebView sender, WebViewNavigationCompletedEventArgs args)
{
    if (args.IsSuccess)
    {
        // Inject JS script
        if (Control != null && Element != null)
        {
            foreach (var f in Element.RegisteredFunctions.Where(ff => !ff.IsInjected))
            {
                await Control.InvokeScriptAsync("eval", new[] { string.Format(JavaScriptFunctionTemplate, f.Name) });
                f.Injected();
            }
        }
    }
}

Then when the Javascript method is called this will call the OnWebViewScriptNotify method so that I can proccess the request in my application.

The trouble is this doesnt work for some kind of security reasons:

This was a policy decision we made that we have had feedback on so we re-evaluate it. The same restriction doesn't apply if you use NavigateToStreamUri together with a resolver object. Internally that's what happens with ms-appdata:/// anyway.

I then tried what is advised in this case which was to use a resolver as mentioned here: https://stackoverflow.com/a/18979635/2987066

But this has a massive affect on performance, because it is constantly converting all files to a stream to load in, as well as certain pages loading incorrectly.

I then looked at using the AddWebAllowedObject method like so:

private void Control_NavigationStarting(WebView sender, WebViewNavigationStartingEventArgs args)
{
    if (Control != null && Element != null)
    {
        foreach (var f in Element.RegisteredFunctions)
        {
            var communicator = new HtmlCommunicator(f);
            Control.AddWebAllowedObject("HtmlCommunicator", communicator);
        }
    }
}

Where HtmlCommunicator is:

[AllowForWeb]
public sealed class HtmlCommunicator
{
    public JSFunctionInjection Function { get; set; }

    public HtmlCommunicator(JSFunctionInjection function)
    {
        Function = function;
    }

    public void Fred()
    {
        var d = 2;
        //Do something with Function
    }
}

and in my html it is like so:

try { window.HtmlCommunicator.Fred(); } catch (err) { }

But this doesn't work either.

So is there a way to work around this rediculous limitation?

Community
  • 1
  • 1
JKennedy
  • 18,150
  • 17
  • 114
  • 198
  • From [http://blog.damiendelaire.com/2016/06/communicating-back-and-forth-with.html](http://blog.damiendelaire.com/2016/06/communicating-back-and-forth-with.html) I read that `For some weird reason you need to add create this in new WRC library and mark this class this [AllowForWeb]` I will investigate more – JKennedy Feb 28 '17 at 17:34
  • I noticed in the link above and [this link](https://www.suchan.cz/2016/01/hacking-uwp-webview-part-2-bypassing-window-external-notify-whitelist/) they both use `ms-app-web`, So I'm not sure whether the last method works for `ms-app-data`. putting the class in another WRC library didn't work – JKennedy Mar 01 '17 at 09:13

1 Answers1

1

So I found this answer: C# class attributes not accessible in Javascript

It says:

I believe you need to define the method name starting with a lower case character.

For example: change GetIPAddress to getIPAddress.

I tested it on my side and found if I use the upper case name 'GetIPAddress', it won't work. But if I use getIPAddress, it works.

So I tried this:

I created a new project of type Windows Runtime Component as suggested here and I changed my method names to lower case so I had:

[AllowForWeb]
public sealed class HtmlCommunicator
{
    public HtmlCommunicator()
    {
    }

    public void fred()
    {
        var d = 2;
        //Do something with Function
    }
}

In my javascript I then had:

try { window.HtmlCommunicator.fred(); } catch (err) { }

and in my main UWP project I referenced the new Windows Runtime Component library and had the following:

public HtmlCommunicator communicator { get; set; }

private void Control_NavigationStarting(WebView sender, WebViewNavigationStartingEventArgs args)
{
    if (Control != null && Element != null)
    {
        communicator = new HtmlCommunicator();
        Control.AddWebAllowedObject("HtmlCommunicator", communicator);
    }
}

And this worked!

Community
  • 1
  • 1
JKennedy
  • 18,150
  • 17
  • 114
  • 198