Welcome Guest Search | Active Topics | Sign In | Register

EO.WebBrowser preloading Options
Eurice
Posted: Thursday, June 30, 2016 3:01:34 AM
Rank: Advanced Member
Groups: Member

Joined: 12/10/2014
Posts: 133
eo_support wrote:
Hi,

We have looked into this issue and it appears that you can not preload a WebView this way. The root of the problem is WPF does not invoke it's internal Measure/Layout/Template logic until the window is visible. We are adding a static WebView.Preload method that can do the preload internally and it should be available in our next build.

Thanks!



Yaaaaay thanks !
eo_support
Posted: Friday, July 1, 2016 1:26:10 PM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,080
Hi,

We have posted a new build that added this method:

http://www.essentialobjects.com/doc/eo.webbrowser.webview.preload.aspx

Please take a look and let us know if it works for you.

Thanks!
Tamas Monus
Posted: Tuesday, July 5, 2016 9:42:27 AM
Rank: Advanced Member
Groups: Member

Joined: 5/6/2016
Posts: 30
Hi,

Sorry for this late response. I'm working in a research team and I've just got time to watch it now.

I have to preload several WebViews, and also I have to expose .NET functions to it. So I modified my Preloading logic like this:

Quote:

public Task Prefetch()
{
var tc = new TaskCompletionSource<bool>();

_webControl.Visibility = Visibility.Hidden;
WebView.Preload((int)WindowSize.Width, (int)WindowSize.Height, "about:blank", (view, args) =>
{
_webControl.WebView = view;
_webView = view;
tc.TrySetResult(true);
return null;
}, null);

return tc.Task;
}

// And here's how I call it.
{
// ...
await _browser.Prefetch();
RefreshContainer(); // this method registers the custom resourcehandlers, exposes .NET functions and set up the webView's JSInitCode, then navigate to the correct URL
}


So basicly I just use the Preload method to create the webView. And according to IsCreated property it works fine, because it is true, but after 2-3 success it stucks, and I got the following exception:

Quote:

ContextSwitchDeadlock occured:

Additional information: The CLR has been unable to transition from COM context 0xc3c8b0 to COM context 0xc3c7f8 for 60 seconds. The thread that owns the destination context/apartment is most likely either doing a non pumping wait or processing a very long running operation without pumping Windows messages. This situation generally has a negative performance impact and may even lead to the application becoming non responsive or memory usage accumulating continually over time. To avoid this problem, all single threaded apartment (STA) threads should use pumping wait primitives (such as CoWaitForMultipleHandles) and routinely pump messages during long running operations.

Eurice
Posted: Tuesday, July 5, 2016 10:53:12 AM
Rank: Advanced Member
Groups: Member

Joined: 12/10/2014
Posts: 133
Hi,

I'm also facing deadlocks when trying to use preload in a new Task.

Best regards
eo_support
Posted: Tuesday, July 5, 2016 11:02:42 AM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,080
Hi,

You can not use await to wait for Prefetch to complete because it does not pump messages. You can try to use something like this:

Code: C#
while (_WebView == null)
  WebView.DoEvents();


Also please remove this line from your doneCallback handler:

Code: C#
_webControl.WebView = view;


You can call that line when you really need to use the WebView.

We are also changing the return value of Preload from void to a PreloadTask that you can wait on. PreloadTask will continue to pump messages in its wait functions.

Thanks!
eo_support
Posted: Tuesday, July 5, 2016 11:03:59 AM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,080
Eurice wrote:
Hi,

I'm also facing deadlocks when trying to use preload in a new Task.

Best regards


WebViews are thread specific. You can not create a WebView in one thread and uses it in another thread.

Thanks!
Tamas Monus
Posted: Wednesday, July 6, 2016 4:22:24 AM
Rank: Advanced Member
Groups: Member

Joined: 5/6/2016
Posts: 30
I tried to rewrite this part of my code, but I still get the same error.

Quote:

// I use this method to start preloading, I know it's a bit of weird that first, I go to about:blank, then I navigate to the actual page, but it would take too much time to rewrite that part
public void Prefetch()
{
_webControl.Visibility = Visibility.Hidden;
WebView.Preload((int)WindowSize.Width, (int)WindowSize.Height, "about:blank", (view, args) =>
{
InitWebView(view); // This method registers custom schemes, exposing .NET functions and setting up JSInitCode
view.LoadUrl(_webView.Url); // after we're ready we're going to the actual page (it needs the exposed .NET functions and the custom scheme, so I figured this is the only way to do it
return null;
}, null);
}

// ...

public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_root = GetTemplateChild("_root") as Grid;
if (_root == null) throw new Exception("There must be a Grid with name: '_root' in Browser ControlTemplate!");

if (_prefetched != null && _prefetched.IsCreated) // The webView is prefetched?
{
_webControl.WebView = _prefetched; // Switch webViews before we put on the WebControl
}

_root.Children.Add(_webControl);
_webControl.Loaded += (sender, args) =>
{
_webControl.Visibility = Visibility.Visible;
};
}


By the way, I don't know wether it is posible to you to do it, but it would be simpler if we could call the Preload function on the object itself. Because like this, it's a little bit tricky to work with custom schemes and everything. And the crucial part is to force the webView object to be created and start loading pages. So an other solution beside this PreLoad function would be to have a simple member function called something like ForceCreate, which would do this, so after that if somebody call a LoadUrl() on that webView that will actually navigate to somewhere.
eo_support
Posted: Wednesday, July 6, 2016 9:55:42 AM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,080
Hi,

Your code looks fine. Can you send a test app to us so that we can look further?

It is not possible to Load a Url when the WebView is not created.

Thanks!
eo_support
Posted: Thursday, July 7, 2016 5:16:37 PM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,080
Hi,

We have posted a new build that will allow you to wait for the Preload to complete. The new build returns a PreloadTask object. You will be able to do something like this:

Code: C#
//Preload the Url
PreloadTask task = WebView.Preload(url);

//Wait for the preload to finish
task.WaitOne();

//Get the preloaded webView
WebView webView = task.WebView;


Hope this will make the code easier for you.

Thanks!
Tamas Monus
Posted: Monday, July 11, 2016 9:05:33 AM
Rank: Advanced Member
Groups: Member

Joined: 5/6/2016
Posts: 30
Hi!

Thanks for the new build. I modified my code a little and now instead of a member function I created a static method to create the object with ensuring that the WebView is created (so I preload a webview)

I've done like this:
Quote:

public static Browser ForceCreateBrowser(string webObjectName, SDXWebObject webObject)
{
var preloadTask = WebView.Preload(800, 600, "about:blank");
preloadTask.WaitOne();
return new Browser(preloadTask.WebView, webObjectName, webObject);
}

// The constructor

private Browser(WebView prefetchedView, string webObjectName = null, SDXWebObject webObject = null)
{
DefaultStyleKey = typeof(Browser);
Unloaded += OnUnloaded;
_webControl = new WebControl
{
// ...
};

_webView = _webControl.WebView = prefetchedView;

Init(); // init here the webView (Add resource handlers, exposing .NET functions, etc.)
}


But If, I try to do this right after starting the program I got this message:
Dispatcher processing has been suspended, but messages are still being processed.
eo_support
Posted: Monday, July 11, 2016 1:04:46 PM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,080
Hi,

You might want to use Dispatcher.BeginInvoke for this. WPF disables pumping message at certain stages to avoid re-entry. However the WebView must pump messages in order to function. This means you can not preload the WebView at certain stages in a WPF application. You must move it to somewhere else.

Thanks!
Tamas Monus
Posted: Tuesday, July 12, 2016 6:51:20 AM
Rank: Advanced Member
Groups: Member

Joined: 5/6/2016
Posts: 30
This is starting to get into my nerves now.

I modified my code, so now the code which is doing the prefetch is done on the Page's Dispatcher with BeginInvoke. But still if I call that part of code immediatly after startup, then I get the error. I managed to solve that by putting a Task.Delay before I call that (just for a HotFix), but my application is built up from tabs each tab can be added dynamically. The problem is, that it seems that the Dispatcher is always suspended when I adding a tab. So it seems, that I can not really know when is the Dispatcher is ready to run my Prefetch code.

Can I move this preloading stuff into a service, or something like that?
eo_support
Posted: Tuesday, July 12, 2016 12:20:40 PM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,080
Hi,

BeginInvoke should not run into that problem. If you still have problem, you can try to create a test project and send it to us. See here for more details:

http://www.essentialobjects.com/forum/test_project.aspx

You should not abuse Task.Delay/service/background thread for this. You can not switch thread for background loading.

Thanks!
JK
Posted: Monday, August 1, 2016 3:38:03 PM
Rank: Advanced Member
Groups: Member

Joined: 7/20/2015
Posts: 52
I've run into deadlock using WebView.Preload if I call it more than once (to preload > 1 WebView). First call succeeds. 2nd call never returns the PreloadTask.
eo_support
Posted: Monday, August 1, 2016 8:36:46 PM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,080
JK wrote:
I've run into deadlock using WebView.Preload if I call it more than once (to preload > 1 WebView). First call succeeds. 2nd call never returns the PreloadTask.


This is normal. Because WebView is thread specific, Preload is done by the calling thread. The calling thread can not start another Preload task when it is busy with the previous one. So it is not possible for you to start two Preload one after another. If you wish to Preload two Urls, you can start the second one asynchronously in the previous one's doneCallback. Note that you can not call Preload directly from the doneCallback. it will cause the same deadlock scenario. If you use Windows Forms, you can use Control.BeginInvoke, if you use WPF, you can use Dispatcher.BeginInvoke to start the second preload asynchronously.

We will also look into this to see if there is anyway to avoid this from our side.

Thanks!
Tamas Monus
Posted: Tuesday, August 2, 2016 2:01:16 AM
Rank: Advanced Member
Groups: Member

Joined: 5/6/2016
Posts: 30
Firstly, sorry if I mistype something. I'm writing from my phone.

I solve it by hacking a little. I used a new window to preload the webview, well at least it forced the creation of the webview.

First create a window:
var wnd = new Window {
// there are props here that tell the wpf to not show this window in taskbar and etc. Sorry but I can't remember them
};

wnd.Content = _webControl;
wnd.Show();
wnd.Hide(); // we call this so that the window will not flash, it won't be visible even on touch devices. It will take the main windows focus tough, that's the downside of this method.

From here on you can use the _webControl.WebView just like before. If you want to show the webcontrol then remove from the window and put on some other control. Then close the window.

But Of course, it's just a workaround.

Hope I could help!
eo_support
Posted: Tuesday, August 2, 2016 10:46:14 AM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,080
Thanks for sharing!
eo_support
Posted: Friday, August 5, 2016 11:57:15 AM
Rank: Administration
Groups: Administration

Joined: 5/27/2007
Posts: 24,080
Hi,

This is just to let you know that we have modified our code to allow multiple Preload calls. Because all Preload calls are still done in the same thread, if you have multiple PreloadTask objects, they are all marked as done almost at the same time.

Thanks!
Drags
Posted: Monday, October 3, 2016 4:22:19 AM
Rank: Member
Groups: Member

Joined: 10/4/2015
Posts: 13
Hi

Thnx for thread. Just my 2 cents. I need to do json post for tab that is not visible.
So now I
1) Do preload to about:blank
2) Then grab webview and do LoadRequest

I think using about:blank first is best way, waiting for actual page to load requires too much mucking around.


You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.