Xamarin.forms: [iOS] WebView.Source URL's are HTML encoded in Xamarin.Forms 3.0.0.482510

Created on 16 May 2018  路  7Comments  路  Source: xamarin/Xamarin.Forms

Description

When setting the WebView.Source property on iOS, the URL is encoded. On Android it is not. This did not happen in previous versions of Xamarin.Forms and is causing a massive issue when redirecting to an Identity Server which expects a valid formatted redirect uri, thus generating an error which prevents us from logging in into our application.

The attached sample demonstrates this problem by querying Google for the Microsoft website.

Steps to Reproduce

  1. Add a WebView control to a page.
  2. Set the source to a URL which contains already encoded data, for example: https://www.google.com/search?q=http%3A%2F%2Fmicrosoft.com

Expected Behavior

The WebView should not encode the URL.

Actual Behavior

The URL is encoded again, so the %3A is encoded to %253A. So the actual opened page is as follows:

  • Android: https://www.google.com/search?q=http%3A%2F%2Fmicrosoft.com
  • iOS: https://www.google.com/search?q=http%253A%252F%252Fmicrosoft.com

If you paste the URL's in your browser you can see the wanted behavior on Android, and the erroneous behavior on iOS.

Basic Information

  • Version with issue: 3.0.0.482510
  • Last known good version: 2.5.1.444934
  • IDE: Visual Studio 2017 15.7.1
  • Platform Target Frameworks:

    • iOS: 11.3

    • Android: 8.1

Screenshots

Android:
image

iOS:
image

Reproduction Link


Bug.WebUriDoubleEncoding.zip

regression iOS 馃崕 bug

Most helpful comment

I believe the issue is caused by https://github.com/xamarin/Xamarin.Forms/commit/14aa258915842caf292e61bde005889e98cd410b#diff-1f852655bc37c1648eb2589cb1398b20R82 where the URL encoding was added for iOS devices.

All 7 comments

/cc @samhouts

I believe the issue is caused by https://github.com/xamarin/Xamarin.Forms/commit/14aa258915842caf292e61bde005889e98cd410b#diff-1f852655bc37c1648eb2589cb1398b20R82 where the URL encoding was added for iOS devices.

The same (CRITICAL) problem with FormUrlEncodedContent (iOS only; previously worked well):

var p = new List>
{
    new KeyValuePair("a", "b"),
    new KeyValuePair("c", "https://www.google.com/search?source=hp&ei=x&q=trolo+boli&oq=trolo+boli")
};

var c = new FormUrlEncodedContent(p);

var q = c.ReadAsStringAsync().Result;
System.Diagnostics.Debug.WriteLine("var q = " + q);

var b = new UriBuilder("example.com")
{
    Query = q
};

System.Diagnostics.Debug.WriteLine("var b = " + b);

Debug:

var q = a=b&c=https%3A%2F%2Fwww.google.com%2Fsearch%3Fsource%3Dhp%26ei%3Dx%26q%3Dtrolo%2Bboli%26oq%3Dtrolo%2Bboli
var b = http://example.com:80/?a=b&c=https%3A%2F%2Fwww.google.com%2Fsearch%3Fsource%3Dhp%26ei%3Dx%26q%3Dtrolo%2Bboli%26oq%3Dtrolo%2Bboli

Even more (iOS only, previously worked well):

var webview = new WebView { 
 ... URL: 
};

webview.Navigating += async (sender, args) =>
{
    args.Url // will be twice url encoded, no way to use this property anymore 
}

I've faced the same issue - loading mp4 videos from Azure using WebView suddenly stopped working on iOS after updating to XF 3.0. Couldn't downgrade, because I've used 3.0 new features everywhere in the app. I believe I've got working workaround for LoadUrl function issue referenced here: https://github.com/xamarin/Xamarin.Forms/issues/2736#issuecomment-389443737

Create custom control:

public class CustomWebView : WebView
{
}

Create renderer:

[assembly: ExportRenderer(typeof(MyProject.Core.CustomControls.CustomWebView), typeof(MyProject.iOS.CustomRenderers.CustomWebViewTerribleBugRenderer))]
namespace MyProject.iOS.CustomRenderers
{
    public class CustomWebViewTerribleBugRenderer : Xamarin.Forms.Platform.iOS.WebViewRenderer
    {
        public override void LoadRequest(NSUrlRequest r)
        {
            var stackTrace = new StackTrace();
            var stackFrames = stackTrace.GetFrames();

            // check if this was(n't) called from LoadUrl method, since we can't override it...
            if (!stackFrames.Any(x => x.GetMethod().Name == "LoadUrl" && x.GetMethod().DeclaringType == typeof(Xamarin.Forms.Platform.iOS.WebViewRenderer)))
            {
                // called from other place so just call base and return
                base.LoadRequest(r);
                return;
            }

            // reconstruct original URL
            var decodedStringUrl = new NSString(r.Url.AbsoluteString).CreateStringByReplacingPercentEscapes(NSStringEncoding.UTF8);

            // call load request with original string
            base.LoadRequest(new NSUrlRequest(new NSUrl(decodedStringUrl)));
        }
    }
}

Use it instead of normal WebView:

<cc:CustomWebView 
                HorizontalOptions="FillAndExpand"
                VerticalOptions="FillAndExpand"
                x:Name="webView"
                BackgroundColor="#0b0b0b" 
                Source="{Binding VideoSource}"
                Grid.Row="0" />

This is just workaround, it's a bit hacky but it works in my case. Tested on real device - Release.

The current fix for iOS scrapes away any port passed along in the url.
url="http://192.168.0.33:5000/connect" is evaluated as http://192.168.0.33/connect

Was this page helpful?
0 / 5 - 0 ratings