Cefsharp: Javascript Bound objects no longer registered after cross-site navigation

Created on 17 Aug 2015  路  26Comments  路  Source: cefsharp/CefSharp

When you call the method named "LoadUrl" losted all registered JsObject.

What I do:

  1. Create a browser: new ChromiumWebBrowser (null).
  2. Registering JsObject .: browser.RegisterAsyncJsObject
  3. Call the method LoadUrl. Registered object "JsObject" available on the page.
  4. The method LoadUrl is called again with a different address. On the loaded page registered object "JsObject" is not available

How can re-register JsObject or prevent registserd objects clean?

blocking bug

Most helpful comment

Hi,

so the permanent solution would be to implement this in CEF: https://bitbucket.org/chromiumembedded/cef/issues/1088/allow-synchronously-sending-extra_info-to

I'm already testing this and I expect that I can contribute it to CEF when it's ready in prod quality and I got approvals. I think unfortunately it will take same time to appear in an official CEF release, but as soon as it's in the open codebase, at least people can compile it themselves.

Basically we can use this to pass the bound objects during the browser instance <-> subprocess assignment, so every time the instance gets assigned to a new subprocess the binding info will be available in the subprocess before anything loads up and then we don't need additional hacks.

Also, unfortunately despite what I thought #1562 won't solve this, it only makes the bindings viable when multiple browser instances are sharing a subprocess and one needs different bindings on each, for example when creating a popup using window.open.

All 26 comments

I did a few experiments, but it seems like the new render process won't receive the messages sent to PID_RENDERER until the old one closes + some time (I actually found a thread describing something like this: http://www.magpcss.org/ceforum/viewtopic.php?f=6&t=10573&p=16293&hilit=domain+new+render+process#p16293). This usually means that the context is already created when messages are starting to get through to the new render process :disappointed: ...it still seems like the only stable point in time to transmit things is the callback where extensions are getting forwarded.

Added --process-per-tab as a temp workaround, will release a new -pre shortly and see if there's any feedback.

https://github.com/cefsharp/CefSharp/commit/56766723785dc82c6c7b539f90c553e8faf0eb95

I'm testing --process-per-tab for a while now and it seems to solve this for regular browser instances. However the new context for the popups are created before the OnRenderViewReady is called, so the bound objects are unavailable in the custom popups.

However the new context for the popups are created before the OnRenderViewReady is called, so the bound objects are unavailable in the custom popups.

Thanks for the heads up :+1:

Interesting background read can be found at http://magpcss.org/ceforum/viewtopic.php?f=6&t=13386&sid=cf4881bba36a72c96ca7de0e891391de#p27469

May not be practical in a CefSharp sense, will need to investigate further.

The solution of using --process-per-tab has served us well until now, it appears that it's broken in Chromium. in the 3029 branch. As we're not actually generating a release using this branch, it's not too big a concern. If the problem persists in 3071 then we'll need to come up with another solution.

https://bitbucket.org/chromiumembedded/cef/issues/2163/process-per-tab-no-longer-works

In CEF Python I do javascript bindings:

  1. After browser is created sending IPC message
  2. In renderer process every time OnContextCreated is being called

Seems to be working fine.

Worth noting that there are sometimes issues with V8 context not being valid, so you should check context->IsValid() before doing bindings. If it's not valid, abort it. Code comments below:

if (!context->IsValid()) {
            // BUG in CEF (Issue 130), the "context" provided by CEF may
            // not be valid. May be a timing issue. Or may be caused by
            // a redirect to a different origin and that creates a new
            // renderer process.
            // This message is logged in the tutorial.py example which
            // uses data uri created from html string.
            LOG(INFO) << "[Renderer process] DoJavascriptBindingsForFrame():"
                         " V8 context provided by CEF is invalid";
            return;
        }

The --process-per-tab error has been raised here: https://bugs.chromium.org/p/chromium/issues/detail?id=719961

Is it possible that this PR helps address this issue? https://github.com/cefsharp/CefSharp/pull/1562

will --renderer-process-limit=1 not help?

Hi,

so the permanent solution would be to implement this in CEF: https://bitbucket.org/chromiumembedded/cef/issues/1088/allow-synchronously-sending-extra_info-to

I'm already testing this and I expect that I can contribute it to CEF when it's ready in prod quality and I got approvals. I think unfortunately it will take same time to appear in an official CEF release, but as soon as it's in the open codebase, at least people can compile it themselves.

Basically we can use this to pass the bound objects during the browser instance <-> subprocess assignment, so every time the instance gets assigned to a new subprocess the binding info will be available in the subprocess before anything loads up and then we don't need additional hacks.

Also, unfortunately despite what I thought #1562 won't solve this, it only makes the bindings viable when multiple browser instances are sharing a subprocess and one needs different bindings on each, for example when creating a popup using window.open.

The cefclient sample also has a binding test that seems to work ok if I understand correctly.
Can someone confirm this?

If the cefclient is able to handle this behaviour correctly, I would like to understand why that works and if we can apply that mechanism to cefsharp somehow.

As far as I am aware the error is as follows go to a website like www.bbc.co.uk bind an object, then go to a different domain such as www.bbc.com and the bound object will be lost. Unfortunately the binding test in the cefclient application http://tests/binding can't really test this.

If you look at how the cef bindings are being done, https://bitbucket.org/chromiumembedded/cef/wiki/JavaScriptIntegration.md we seem to be already doing that.

As described above by the maintainer of CEF Python the fix may be to rebind the object after every:

After browser is created sending IPC message
In renderer process every time OnContextCreated is being called

Or by sending the additional info as described by @cefsharp-ms

Sadly it is not working here is a simple test case (xaml just needs to define a content control with the name contentControl:

private async void TestCase() {
            var browser = new ChromiumWebBrowser();
            browser.RegisterAsyncJsObject("bound", new AsyncBoundObject());
            contentControl.Content = browser;

            browser.Address = "https://www.google.com/";
            await Task.Delay(5000);
            browser.GetMainFrame().ExecuteJavaScriptAsync($"bound.test('hello: 1');");
            browser.Address = "https://www.google.com/intl/en_us/policies/terms/?fg=1";
            await Task.Delay(5000);
            browser.GetMainFrame().ExecuteJavaScriptAsync($"bound.test('hello: 2');");
            browser.Address = "https://status.github.com/messages";
            await Task.Delay(5000);
            browser.GetMainFrame().ExecuteJavaScriptAsync($"bound.test('hello: 3');");
        }
        public class AsyncBoundObject {
            public void test(string arg) {
                Debug.WriteLine($"bound method test called with: {arg}");
            }
        }

Output from debug is:

bound method test called with: hello: 1
bound method test called with: hello: 2
[1212/130730.823:INFO:CONSOLE(1)] "Uncaught ReferenceError: bound is not defined", source: about:blank (1)

As this primarily happens when the domain changes (I believe) those who this is an issue for one option is to create separate ChromiumWebBrowser for each domain (if you have a limited number of domains) and just swap what the UI is bound to. Keep in mind you can share requestcontext without breaking the JSBinding (I believe) as well. Not a perfect solution but may be workable for some use cases.

I've removed quite a few of the old comments. If your comment has been deleted it's nothing personal, just keeping the size of the overall issue down.

Now to clarify a few points, the problem still exists any the latest 63.0.0-pre01 release (3239 branch)

will --renderer-process-limit=1 not help?

@jogibear9988 With code changes it could be made to work, the current implementation does not support that scenario, see #2142

In CEF Python I do javascript bindings:

After browser is created sending IPC message
In renderer process every time OnContextCreated is being called

Seems to be working fine.

@cztomczak This solution would appear to be workable in version 62 and below, unfortunately I'm seeing problems with IPC messaging being sent from OnContextCreated after a render process switch, see http://magpcss.org/ceforum/viewtopic.php?f=6&t=15677 for details. I'd be curious to hear feedback from your testing with the 3239 branch.

The cefclient sample also has a binding test that seems to work ok if I understand correctly.

@japj cefclient does indeed have it's own binding feature, you can see https://bitbucket.org/chromiumembedded/cef/src/master/include/wrapper/cef_message_router.h?at=master&fileviewer=file-view-default#cef_message_router.h-59 for an example of it's usage. In my opinion it's very limited and not worth implementing.

I do have an alpha version of what I believe to be a workable solution. I will post more information when I've received feedback.

@amaitland

In CEF Python I do javascript bindings:

  1. After browser is created I am sending IPC message from browser process to renderer process. That IPC message contains js bindings and a copy of them is saved for later re-binding in step 2. I create browser synchronously and send js bindings after browser is created, so there may be some delay before js bindings are available, however haven't noticed any issue, in practice browser is still loading after being created.

  2. In renderer process every time OnContextCreated is being called I re-bind js bindings using a copy of data sent in step 1. The data is already available for me to expose it in javascript (or will be available in a moment as it is already being sent in step 1), I don't have to send IPC message from renderer to browser process at this point to get them - as what you described is an issue in v63.

  3. If user modifies js bindings in browser process I send another IPC message as in step 1.

After browser is created I am sending IPC message from browser process to renderer process. That IPC message contains js bindings and a copy of them is saved for later re-binding in step 2. I create browser synchronously and send js bindings after browser is created, so there may be some delay before js bindings are available, however haven't noticed any issue, in practice browser is still loading after being created.

@cztomczak Have you tested with the 3239 branch? When the cross-origin switch occurs I'm seeing messages go missing or sent to the incorrect (old) render process. I've looked at your code and I'd be surprised if you didn't have exactly the same problem, at least with the 3239 branch. Soon as the cross-origin switch occurs the IPC messages aren't delivered reliably.

In renderer process every time OnContextCreated is being called I re-bind js bindings using a copy of data sent in step 1. The data is already available for me to expose it in javascript (or will be available in a moment as it is already being sent in step 1), I don't have to send IPC message from renderer to browser process at this point to get them - as what you described is an issue in v63.

That's what CefSharp did previously, data was sent in OnRenderViewReady, as there was only one render process per browser instance, this was reliable for almost all cases (there is a problem with popups reported above)

Thanks for taking the time to respond 馃槃

No, I haven't yet tested 3239 branch and neither "cross-origin" switch. I haven't had previously the need to expose js bindings to different addresses loaded in browser, so yeah looks like it would be an issue in cefpython, as this would create new renderer processes. However user can always manually rebind js bindings by calling Rebind() func if he knows an address changed (he can using OnBeforeBrowse or similar).

Looks like I've discovered an issue in cefpython, reported (https://github.com/cztomczak/cefpython/issues/406) and referenced the three comments above, so please don't delete them :)

However user can always manually rebind js bindings by calling Rebind() func if he knows an address changed (he can using OnBeforeBrowse or similar).

I tried OnBeforeBrowser and OnBeforeResourceLoad, was unable to get either of them reliably working in the cross-origin switch scenario. I'm basically seeing the same problem as outlined in https://bitbucket.org/chromiumembedded/cef/issues/929#comment-16412458 which I know is very old and marked as wontfix, I was able to reliably reproduce the problem last time I tested.

so please don't delete them :)

Understood 馃槃

See https://github.com/cefsharp/CefSharp/issues/2246 for details on the new option for binding objects.

The changes in #2246 have been published as part of the 63.0.0-pre02 packages which are now on Nuget.org.

Just as an addition to this, I submitted a PR towards CEF: https://bitbucket.org/chromiumembedded/cef/pull-requests/147/allow-synchronously-sending-extra_info-to/diff
If/when this is merged, it could be used to transmit bound objects in a sync way.

63.0.0 has now been released, see https://github.com/cefsharp/CefSharp/issues/2246 for details on the new Javascript Binding implementation.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nico87 picture nico87  路  46Comments

dinoc picture dinoc  路  40Comments

amaitland picture amaitland  路  34Comments

ay2015 picture ay2015  路  31Comments

8 picture 8  路  39Comments