Android webview containing hyperlinks inside IFRAM

2019-05-30 18:26发布

问题:

The hyperlink in this example is not working when embedded in an Android WebView. Can someone explain why and how I can correct it? It works fine in the Desktop Chrome browser. The example HTML code provided below was copied from the dynamic code generated by a twitter timeline widget. I cannot control the HTML that the twitter timeline widget creates so I need a solution that works around this limitation.

Here is an example set up:

test.html:

<iframe src="twitter.html"></iframe>

twitter.html:

<a href="http://t.co/zCcFf1SWtL" target="_blank" class="link media customisable" data-pre-embedded="true" dir="ltr">pic.twitter.com/zCcFf1SWtL</a>

Java code:

webView.loadUrl("file:///android_asset/test.html");

Actual Twitter Timeline widget HTML: (this injects the above HTML code)

<a class="twitter-timeline" height="355" data-dnt="true" href="https://twitter.com/VectrenStorm" data-widget-id="367009971554095104">Tweets by @VectrenStorm</a>
!function (d, s, id) { var js, fjs = d.getElementsByTagName(s)[0], p = /^http:/.test(d.location) ? 'http' : 'https'; if (!d.getElementById(id)) { js = d.createElement(s); js.id = id; js.src = p + "://platform.twitter.com/widgets.js"; fjs.parentNode.insertBefore(js, fjs); } } (document, "script", "twitter-wjs");

回答1:

Apparently this is a limitation of the Android WebView. When target="_blank" on a hyperlink inside an IFRAME then ShouldOverrideUrlLoading is not called when the link is clicked.

As a workaround I implemented a new WebChromeClient class and overrode the onCreateWindow method. This method is triggered when the link is clicked; inside onCreateWindow I create a new temporary webview and assign it to the WebViewTransport message and then return true. I also assign the new WebView a custom implementation of WebViewClient. When the new webview is created its ShouldOverrideUrlLoading method is triggered. Inside the ShouldOverrideUrlLoading method I load call loadUrl on my original webview and then destroy the new, temporary webview.

Here's some code (it's written in C#/mono):

public class TwitterChromeClient : WebChromeClient
{
    public override bool OnCreateWindow(WebView view, bool dialog, bool userGesture, Android.OS.Message resultMsg)
    {
        WebView newWebView = new WebView(view.Context);
        newWebView.SetWebViewClient(new TempWebClient(view));
        WebView.WebViewTransport transport = (WebView.WebViewTransport)resultMsg.Obj;
        transport.WebView = newWebView;
        resultMsg.SendToTarget();
        return true;
    }
}

public class TwitterWebClient : WebViewClient
{
    public override bool ShouldOverrideUrlLoading(WebView view, string url)
    {
        //Unfortunately this method will never be called for links inside an iframe when target="_blank"
        return true;
    }
}

public class TempWebClient : WebViewClient
{
    public WebView OriginalWebView { get; set; }

    public TempWebClient(WebView originalWebView)
    {
        OriginalWebView = originalWebView;
    }

    public override bool ShouldOverrideUrlLoading(WebView view, string url)
    {
        OriginalWebView.LoadUrl(url);
        OriginalWebView = null;
        view.Destroy();
        return true;
    }
}