WinRT WebView does not update the current URL based on HTML5 pushState? - c#

The WinRT WebView control in both Windows 8.1 and Windows Phone 8.1 does not update its Source property in response to history.pushState or history.replaceState, which can modify the current URL from JavaScript without actually navigating to a different page.
You can easily verify this by creating a new blank app, adding a WebView control named WebView to MainPage.xaml and replacing the constructor with the following:
public MainPage()
{
this.InitializeComponent();
var timer = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(1) };
timer.Tick += (o, e) => Debug.WriteLine("WebView.Source: " + WebView.Source);
timer.Start();
// you could also replace the URL with a simpler demo page like this one:
// http://www.skjapp.com/wp-content/uploads/2012/06/HistoryAPI-pushState.htm
WebView.Navigate(new Uri("http://try.discourse.org"));
}
Opening any discussion thread on Discourse will not update WebView.Source. The native IE browser handles this correctly, and strange enough the old Windows Phone Silverlight WebBrowser control does as well.
So, am I doing something wrong or is there any other way to get the current URL of a WebView that properly supports the HTML5 history API?

Related

Clear the navigation history of WebView in WinRT Windows Store App C#

Scenario
I am creating a simple embedded "browser" for my app, which opens in a Popup, when the user clicks on a button with a hyperlink designed to open "in-app".
The popup is opened, and the (simply-named) WebView is navigated to the URL specified in the hyperlink.
There are the typical Back, Forward and Refresh/Stop buttons that are enabled/disabled accordingly.
Current Situation
I have wired up the necessary events for NavigationStarted, NavigationCompleted and others for Falied, Loaded etc. etc.
These are performed along with some "naughty" ViewModel wiring when the UserControl is loaded TL;DR - there is no way I can find to keep to MVVM practice with WebViews, what a PITA!:
private void OnLoaded(object sender, RoutedEventArgs e)
{
if (this.DataContext is IWebViewUserControlViewModel)
{
this.WebView.ContentLoading += OnWebViewContentLoading;
this.WebView.DOMContentLoaded += OnWebViewDomContentLoaded;
this.WebView.NavigationStarting += OnWebViewNavigationStarting;
this.WebView.NavigationCompleted += OnWebViewNavigationCompleted;
this.WebView.UnviewableContentIdentified += OnWebViewUnviewableContentIdentified;
this.WebView.NavigationFailed += OnWebViewNavigationFailed;
this.viewModel = DataContext as IWebViewUserControlViewModel;
NavigateToUrl(this.viewModel?.Url);
}
}
This is so that I navigate to the URL when the UserControl is loaded, and can evaluate the button states as the user navigates around using the events above.
The NavigateToUrl() method just contains a try/catch block to counteract any errors forming the Uri etc.:
private void NavigateToUrl(string url)
{
try
{
var uri = new Uri(url);
this.WebView.Navigate(uri);
}
catch (Exception ex)
{
this.WebView.NavigateToString($"An error occurred: {ex.Message}")
}
}
Back/Forward UX
In particular the Back and Forward buttons are disabled/enabled when navigation to a page is started/completed respectively.
I evaluate their IsEnabled states like so:
btnBackButton.IsEnabled = this.WebView.CanGoBack;
btnForwardButton.IsEnabled = this.WebView.CanGoForward;
This works fine throughout the entire time that the user is browsing.
The Issue
Should the user close the popup, and re-open it via the same or a different link, the correct URL is navigated to - all good.
The issue is, that their previous browsing session was never cleared from the MyWebView, and thus the btnBackButton (not the forward, as this the latest navigation in the history stack) is now enabled again - allowing them to traverse their previously visited pages.
I don't want this behaviour.
I would like it to appear that their session is a "new", fresh one - without the Back button enabled - as if it had just been opened.
What I have already tried...
I am unable to manually set the MyWebView.CanGoBack/MyWebView.CanGoForward properties to false when the popup is (re)opened.
They are get-only properties, so this is not possible.
I have tried re-initializing the WebView control when the containing UserControl is Loaded (in the same OnLoaded delegate as above):
private void OnLoaded(object sender, RoutedEventArgs e)
{
if (this.DataContext is IWebViewUserControlViewModel)
{
// Re-initialize the WebView
this.WebView = new WebView();
// Detect when the new control has loaded, and then wire up events/navigate as normal
this.WebView.Loaded += (sender, e) =>
{
this.WebView.ContentLoading += OnWebViewContentLoading;
this.WebView.DOMContentLoaded += OnWebViewDomContentLoaded;
this.WebView.NavigationStarting += OnWebViewNavigationStarting;
this.WebView.NavigationCompleted += OnWebViewNavigationCompleted;
this.WebView.UnviewableContentIdentified += OnWebViewUnviewableContentIdentified;
this.WebView.NavigationFailed += OnWebViewNavigationFailed;
this.viewModel = DataContext as IWebViewUserControlViewModel;
NavigateToUrl(this.viewModel?.Url);
}
}
}
In the hope that this might work - but the Loaded delegate of the WebView is never fired.
In the UI, the WebView just doesn't appear.
Help!?
Is there any way for me to clear the navigation history for the WebView control, so it appears that the browsing session is a "new" one?
Your help is appreciated, as always. Many thanks.
If your app is Windows 8.1, actually there is no programmatic way to clear the webview cache according to what #MattĀ said in thisĀ link(part 7).
And if it is a UWP app, please refer to this doc.

Set custom WebView header in UWP

This could seem to be duplicates of other similar questions but they are old thread and not specific to Windows UWP apps.
I'm unable to set custom header in WebView so the loaded URLs in WebView could work for me.
I have seen many forums giving solution like using HttpClient/WebRequest with header but that doesn't work as in my case, the web address usage Javascript for redirection and before redirection it needs few custom header to load correctly.
Also WebView.NavigateWithHttpRequestMessage is not so suitable as it will postback and I need the headers for each request including javascript redirected URLs in web View.
I'm able to set custom headers in Xamarin.Droid project using Renderers but I couldn't find any solution for UWP Windows.UI.Xaml.Controls.WebView.
On Universal Windows 10 Platform, the WebView.NavigateWithHttpRequestMessage method is the right way.
a. I need the headers for each request including javascript redirected URLs in web View.
b. This didn't resolve my issue as after setting the headers the OnWebViewNavigationStarting method is called multiple times and App crashes automatically with System.StackOverflowException error
This is due to the infinite navigation will happen if we do navigation in the NavigationStarting event. We should cancel navigation in a handler for this event by setting the WebViewNavigationStartingEventArgs.Cancel property to true.
And we need to add/remove handler for NavigationStarting event carefully.
Code sample:
private void NavigateWithHeader(Uri uri)
{
var requestMsg = new Windows.Web.Http.HttpRequestMessage(HttpMethod.Get, uri);
requestMsg.Headers.Add("User-Name", "Franklin Chen");
wb.NavigateWithHttpRequestMessage(requestMsg);
wb.NavigationStarting += Wb_NavigationStarting;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
NavigateWithHeader(new Uri("http://openszone.com/RedirectPage.html"));
}
private void Wb_NavigationStarting(WebView sender, WebViewNavigationStartingEventArgs args)
{
wb.NavigationStarting -= Wb_NavigationStarting;
args.Cancel = true;//cancel navigation in a handler for this event by setting the WebViewNavigationStartingEventArgs.Cancel property to true
NavigateWithHeader(args.Uri);
}
The screenshot is the log info in Fiddler, the request record in the second red rectangle included the custom header:
I shared my UWP sample in here, you can easily integrate into your Xamarin UWP app.
With the Xamarin Tag, it seems like you are using this with Xamarin.Forms and hence the below answer is with respect to Xamarin.Forms.
However, the code holds true for WebView in UWP as well.
You can try creating a Custom Renderer for WebView and then try making use of the same WebView.NavigateWithHttpRequestMessage.
Before navigating you can try setting the Headers like this:
var requestMsg = new Windows.Web.Http.HttpRequestMessage(HttpMethod.Get, new Uri("https://www.whatismybrowser.com/detect/what-http-headers-is-my-browser-sending"));
requestMsg.Headers.Add("User-Name", "AnubhavRanjan");
Control.NavigateWithHttpRequestMessage(requestMsg);
The Uri above can be set based on your requirement.
In case the request is happening multiple times, you can always set the delegate for NavigationStarting event and handle it in the method.
Control.NavigationStarting += OnWebViewNavigationStarting

Start app at a specific page whenever app is suspended WP 8.1

I am working on a Windows Phone 8.1 (non SL) application.
My app has multiple views, these are Page01.xaml, Page02.xaml and Page03.xaml.
How can I get my app to get back to Page01.xaml whenever the app is suspended?
So for example, the user is on Page02.xaml and he is interacting (doing whatever he wants), then he suspends the app. When he gets back to my app, I want him starting at Page01.xaml instead of where he was!
How can this be achieved?
In App.xaml.cs you have method which triggers when app is called from tombstoning. In this method you can remove from RootPage's stack all pages and navigate to Page01.
#edit:
Window.Current.Activated += (sender, eventArgs) =>
{
var rootFrame = Window.Current.Content as Frame;
if (rootFrame.BackStack.Count > 0)
{
rootFrame.BackStack.Clear();
rootFrame.Navigate(typeof(Page01));
}
};

Windows 8.1 Tabs & Windows with webview

When navigating using the webview in a Windows Store app any links which try to open in a new tab/windows or opened in internet explorer thus practically pulling users from my app. is there any way to handle the link event to either force the links to open in current view or a way to run code to create a new tab within my own app. i have had a look around and can't seem to see much in the way of a defined way of doing this.
You can hook up to the WebViews "NavigationStarting" event.
The you can cancel the navigation and reissue it from within your code so it navigates inside the webview.
Xaml:
<WebView NavigationStarting="WebView_NavigationStarting" />
Codebehind:
private void WebView_NavigationStarting(WebView sender, WebViewNavigationStartingEventArgs args)
{
if (args.Uri != null)
{
args.Cancel = true;
sender.Navigate(args.Uri);
}
}
But you will loose the ability to open links in new windows completely, as you only have the uri itself available in the event handler.
Uri will be null if you use the NavigateToString operations on the webview.

Desktop App in C#: Can't get the access token from the embedded webbrowser

I've created a WPF Desktop Application with C# and placed a System.Windows.Controls.WebBrowser.
Typing this (Where {0} is my app id/key)
https://www.facebook.com/dialog/oauth?&client_id={0}&response_type=token&redirect_uri=https://www.facebook.com/connect/login_success.html&display=popup&scope=publish_stream,offline_access
manually into my firefox/ie/whatever and going to the workflow sends my back to
https://www.facebook.com/connect/login_success.html#access_token=TOKEN
that's great so far.
But navigating my System.Windows.Controls.WebBrowser to the workflow redirects this browser to
https://www.facebook.com/connect/login_success.html
WITHOUT the access token. What am I doing wrong?
I hit something like this while implementing Facebook PowerShell Module. You may be hitting a bug in WPF per http://facebooksdk.codeplex.com/discussions/261528. I had to drop back to WinForms for implementing the login capability only. This also fixed an odd crash-on-exit which I had been experiencing.
I've come up with a workaround. The WPF browser cuts off the hash-part of an url, the WinForms webbrowser doesn't.
So watch this code behind of my XAML window which I'm going to use for getting Facebook app permissions from a user:
public partial class DiagnosticBrowserWindow : Window
{
public DiagnosticBrowserWindow(string urlToRequest)
{
InitializeComponent();
System.Windows.Forms.WebBrowser shadowBrowser = new System.Windows.Forms.WebBrowser();
shadowBrowser.Navigated += (sender, e) =>
{
// the access token is now
// here in e.Url
};
this.Browser.Navigated += (sender, e) =>
{
if (this.Browser.Source.AbsoluteUri.StartsWith("https://www.facebook.com/connect/login_success.html"))
{
shadowBrowser.Navigate(urlToRequest);
}
};
this.Browser.Navigate(urlToRequest);
}
}
This is working, because as soon the app permissions have been granted (which we detect by detecting a redirect to login_success.html) we send the shadow browser (which is a WinForms Webbrowser) to the inital request page which is:
https://www.facebook.com/dialog/oauth?&client_id={0}&response_type=token&redirect_uri=https://www.facebook.com/connect/login_success.html&display=popup&scope=publish_stream,offline_access
Facebook will detect, that the permissions already have been granted and send the shadowBrowser back to login_success.html and this time you can read the hash-part.

Categories