SKLn-Rad / Xam.Plugin.Webview

Xamarin Plugin for a HybridWebView in PCL projects.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

OnNavigationStarted called multiple times after page load completes

dczaretsky opened this issue · comments

Hello,
I seem to be having a problem with OnNavigationStarted event in opening (external) website content. I'm using OnNavigationStarted to show the activity indicator, and then OnNavigationCompleted to disable the activity indicator.

Frow what I can tell, OnNavigationStarted fires and some time later OnNavigationCompleted / OnContentLoaded both fire. But then it seems other items on the web page that are subsequently loaded seem to be triggering OnNavigationStarted again, with no subsequent OnNavigationCompleted event. Some of these seem to be "about:blank" URLs, others look to be JS code such as Google Analytics, banner ads, and iFrames, all likely asyncronous loading.

The resulting behavior is that the loading indicator turns on and never turns off. When starting from an input URL, I have something to compare to (e.g. the domains). However, in dealing with link clicks, http redirects, etc the only place I can determine the URL is in the OnNavigationStarted event.

I've been looking at different variables, such as (bool) Navigating to see if there's a way of determining if it's an actual navigation or just content loading on the same page, but I can't seem to make any headway. I'm not sure if this is a bug, or if something is wrong with my code. Any recommendations?

Thanks,
David

`

private void OnNavigationStarted(object sender, DecisionHandlerDelegate e)
{
    Loading.IsVisible = true;
    Loading.IsRunning = true;
    Loading.IsEnabled = true;

    BackButton.IsEnabled = (webView.CanGoBack) ? true : false;
    BackButton.Opacity = (webView.CanGoBack) ? 1 : 0.5;
    ForwardButton.IsEnabled = (webView.CanGoForward) ? true : false;
    ForwardButton.Opacity = (webView.CanGoForward) ? 1 : 0.5;

    //e.Cancel = ViewModel.IsCancelled;
}

private void OnNavigationCompleted(object sender, string url)
{
    Loading.IsVisible = false;
    Loading.IsRunning = false;
    Loading.IsEnabled = false;

    BackButton.IsEnabled = (webView.CanGoBack) ? true : false;
    BackButton.Opacity = (webView.CanGoBack) ? 1 : 0.5;
    ForwardButton.IsEnabled = (webView.CanGoForward) ? true : false;
    ForwardButton.Opacity = (webView.CanGoForward) ? 1 : 0.5;

    scrollView.ScrollToAsync(0, 0, false);
}

`

Looking into this further, I realized that any content loaded on the page (ads, iframes, js, etc) would trigger the onNavigationStarted function, which made the event unusable in my case.

Instead I used a hack to inject JS into the page after the content loads to trigger an event just before navigating away from the page (beforeunload & unload events). It seems to work really well in most cases where the web page allows for it.

Perhaps a new event like onPageNavigationStarted could be added.

In my constructor, I had to declare the local/global callback functions:
`

        webView.EnableGlobalCallbacks = true;
        webView.AddLocalCallback("pageNavigation", OnPageNavigation);
        FormsWebView.AddGlobalCallback("pageNavigation", OnPageNavigation);

`

Here is my code. Hoping this could be useful for anyone stuck on the same problem.

`

    void OnPageNavigation(string obj)
    {
        Device.BeginInvokeOnMainThread(() =>
        {
            //System.Diagnostics.Debug.WriteLine($"Got callback: {obj}");
            Loading.IsVisible = true;
            Loading.IsRunning = true;
            Loading.IsEnabled = true;
        });
    }

    private async void OnContentLoaded(object sender, EventArgs e)
    {
        if (sender == null || !(sender is FormsWebView))
        {
            return;
        }

        FormsWebView _webview = sender as FormsWebView;

        // inject beforeunload & unload events to trigger pageNavigation() function
        string js_script = "window.addEventListener('beforeunload', function() { try { pageNavigation(window.location.href); } catch(e) {} }); " +
                           "window.addEventListener('unload', function() { try { pageNavigation(window.location.href); } catch(e) {} }); ";
        var response = await _webview.InjectJavascriptAsync(js_script);

        Device.BeginInvokeOnMainThread(() =>
        {
            Loading.IsVisible = false;
            Loading.IsRunning = false;
            Loading.IsEnabled = false;

            BackButton.IsEnabled = (webView.CanGoBack) ? true : false;
            BackButton.Opacity = (webView.CanGoBack) ? 1 : 0.5;
            ForwardButton.IsEnabled = (webView.CanGoForward) ? true : false;
            ForwardButton.Opacity = (webView.CanGoForward) ? 1 : 0.5;
        });

        await scrollView.ScrollToAsync(0, 0, false);
    }

`