Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The problem of calling C# by JS in child window #3191

Closed
haohaodz opened this issue Jul 22, 2020 · 19 comments
Closed

The problem of calling C# by JS in child window #3191

haohaodz opened this issue Jul 22, 2020 · 19 comments

Comments

@haohaodz
Copy link

After creating the ChromiumWebBrowser, bind the C# object。
javascriptObjectRepository.Register("domClickBound", new DomMouseEventBoundObject1(), isAsync: false, BindingOptions.DefaultBinder);

in ILifeSpanHandler.OnBeforePopup:
newBrowser =new ChromiumWebBrowser(null); newBrowser.JavascriptObjectRepository.Register("domClickBound", new DomMouseEventBoundObject2(), isAsync: false, BindingOptions.DefaultBinder); return false;

However, in the child window, the c# object called by JS is the DomMouseEventBoundObject1 object in the parent window.
I expect it to be DomMouseEventBoundObject2.

Another problem:
in ILifeSpanHandler.OnBeforePopup:
If the new window is set to itself, there will be an error. If it is set to a window that has already opened a web page, two pages will be displayed in the same window.It seems that I have to create a new browser.

I want to know how to open a new web page in the old browser and support post request and parent-child relationship.

thank you very much!

@amaitland
Copy link
Member

However, in the child window, the c# object called by JS is the DomMouseEventBoundObject1 object in the parent window.
I expect it to be DomMouseEventBoundObject2.

The objects are currently stored at a per render process level, you are using the same key which isn't currently supported. You will need to use a different key.

Duplicate of #2306

@amaitland
Copy link
Member

I want to know how to open a new web page in the old browser and support post request and parent-child relationship.

Fundamentally this is not possible, you cannot retain a parent-child relationship if you override the current browser.

As for posting to itself that's not currently supported by the ILifeSpanHandler implementation, you can likely use javascript to modify the form post target. Further questions please use one of the options listed at https://github.com/cefsharp/CefSharp#contact

This issue tracker is for bug reports only.

@amaitland
Copy link
Member

Depending on your scenario you might be able to use CefSharp.PostMessage (#2775 (comment)) for communication between your different browser instances.

@amaitland
Copy link
Member

I want to know how to open a new web page in the old browser

Maybe you can try removing https://github.com/cefsharp/CefSharp/blob/cefsharp/83/CefSharp.Core/Internals/ClientAdapter.cpp#L133 I have never tried this scenario.

@haohaodz
Copy link
Author

Thank you very much for your help. Because I don't know English, maybe I didn't explain my question clearly. I'll study your reply again

@amaitland
Copy link
Member

The objects are cached in the render process. The first time you register domClickBound the object is cached, the second call you get the cached version.

You can manually clear the cache using https://github.com/cefsharp/CefSharp/wiki/JavaScript-Binding-API#cefsharpremoveobjectfromcacheobjectname

If you are only interested in mouse events then using PostMessage is recommend.

@haohaodz
Copy link
Author

When a user clicks on a web page, I may need to get the domid of the click element and block the user's click events. Since PostMessage cannot call the C ා code, I try to pass the return value through the callback function of JS.

I tried to use the PostMessage method。
js:

document.addEventListener('click', 
    function(e){
             function callback(isCancel){
                 if(isCancel)e.preventDefault()
             }
             CefSharp.PostMessage({Id:e.currentTarget.id,'Callback': callback})
   }
)

the callback is a closure function with one parameter called by C#.

C#

var msg = e.ConvertMessageTo<PostMessageExample>();
var callback = msg.Callback;
.....
callback.ExecuteAsync(true);

I'm not sure whether the code 'if (iscancel) e. preventdefault()' can block click events.

thanks very much!

@haohaodz
Copy link
Author

When I try CefSharp.RemoveObjectFromCache ("boundasync"); after that, JS cannot call C# without any response

await CefSharp.BindObjectAsync({ NotifyIfAlreadyBound: true, IgnoreCache: true }, "boundAsync2"); The effect is the same

The objects are cached in the render process. The first time you register domClickBound the object is cached, the second call you get the cached version.

You can manually clear the cache using https://github.com/cefsharp/CefSharp/wiki/JavaScript-Binding-API#cefsharpremoveobjectfromcacheobjectname

If you are only interested in mouse events then using PostMessage is recommend.

@amaitland
Copy link
Member

When I try CefSharp.RemoveObjectFromCache ("boundasync"); after that, JS cannot call C# without any response

It's unclear what you mean by this.

Please provide a detailed example of how you are calling the methods of your bound object.

@haohaodz
Copy link
Author

haohaodz commented Jul 30, 2020

When I try CefSharp.RemoveObjectFromCache ("boundasync"); after that, JS cannot call C# without any response

It's unclear what you mean by this.

Please provide a detailed example of how you are calling the methods of your bound object.

After creating the ChromiumWebBrowser, bind the C# object。

javascriptObjectRepository.Register("domClickBound", new DomMouseEventBoundObject1(), isAsync: false, BindingOptions.DefaultBinder);

in ILifeSpanHandler.OnBeforePopup:
newBrowser =new ChromiumWebBrowser(null); newBrowser.JavascriptObjectRepository.Register("domClickBound", new DomMouseEventBoundObject2(), isAsync: false, BindingOptions.DefaultBinder); return false;

Js:

await CefSharp.BindObjectAsync({ NotifyIfAlreadyBound: true, IgnoreCache: true},'domClickBound')
domClickBound.handle()

JS call C# without any response

@haohaodz
Copy link
Author

When a user clicks on a web page, I may need to get the domid of the click element and block the user's click events. Since PostMessage cannot call the C ා code, I try to pass the return value through the callback function of JS.

I tried to use the PostMessage method。
js:

document.addEventListener('click', 
    function(e){
             function callback(isCancel){
                 if(isCancel)e.preventDefault()
             }
             CefSharp.PostMessage({Id:e.currentTarget.id,'Callback': callback})
   }
)

the callback is a closure function with one parameter called by C#.

C#

var msg = e.ConvertMessageTo<PostMessageExample>();
var callback = msg.Callback;
.....
callback.ExecuteAsync(true);

I'm not sure whether the code 'if (iscancel) e. preventdefault()' can block click events.

thanks very much!

I want to know whether the above processing is correct

@amaitland
Copy link
Member

JS call C# without any response

What does without any response mean exactly?

I want to know whether the above processing is correct

No, you'd need to use a slightly different approach. I need a detailed example of what you are doing to say anything specific.

@haohaodz
Copy link
Author

haohaodz commented Jul 30, 2020

I am developing a web test program, which has a script recording function. The program needs to capture the user's mouse action, record the elements that the user clicks and cancel the page click event, so as to avoid opening a new web page.

The first time I used JS to call C#:

After creating the ChromiumWebBrowser, bind the C# object。

javascriptObjectRepository.Register("domClickBound", new DomMouseEventBoundObject1(), isAsync: false, BindingOptions.DefaultBinder);

in ILifeSpanHandler.OnBeforePopup:

public bool OnBeforePopup(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser)
{
             newBrowser =new ChromiumWebBrowser(null); 
             newBrowser.JavascriptObjectRepository.Register("domClickBound", new DomMouseEventBoundObject2(), isAsync: false, BindingOptions.DefaultBinder); 
            return false;
}

It is not possible to bind different objects for domclickbound in different browsers in the same process

Js:

await CefSharp.BindObjectAsync({ NotifyIfAlreadyBound: true, IgnoreCache: true},'domClickBound')
domClickBound.handle()

the C# objects cannot be bound in multiple browser objects of the same process

For the second time, I used PostMessage, C ා callback JS method, pass parameters, JS judge whether to cancel the page click event

js:

document.addEventListener('click', 
    function(e){
             function callback(isCancel){
                 if(isCancel)e.preventDefault()
             }
             CefSharp.PostMessage({Id:e.currentTarget.id,'Callback': callback})
   }
)

the callback is a closure function with one parameter called by C#.

c#:

var msg = e.ConvertMessageTo<PostMessageExample>();
var callback = msg.Callback;
.....
callback.ExecuteAsync(true);

I'm not sure whether the code 'if (iscancel) e. preventdefault()' can block click events.

thanks very much!

@amaitland
Copy link
Member

the C# objects cannot be bound in multiple browser objects of the same process

In theory you can bind if you choose a different object name, trying to bind different objects using the same name isn't supported directly. Injecting different JavaScript to hook your click event, that should be straight forward

As per the documentation assigning a new ChromiumWebBrowser instance to the newBrowser parameter in OnBeforePopup is experimental. You can debug and submit a PR if you'd like to improve support.

I'm not sure whether the code 'if (iscancel) e. preventdefault()' can block click events.

No, you cannot do that. I'd suggest you consider some other options, build a list of all element id's and inject that in with your JavaScript, perform all the processing in JavaScript or block all click events and pass in the url to PostMessage performing the navigation in code.

I'm sure you can think of something.

@haohaodz
Copy link
Author

haohaodz commented Jul 31, 2020

In theory you can bind if you choose a different object name, trying to bind different objects using the same name isn't supported directly. Injecting different JavaScript to hook your click event, that should be straight forward

I tried to use a different key, but failed

After creating the ChromiumWebBrowser1, bind the C# object。

chromiumWebBrowser1.JavascriptObjectRepository.Register("domClickBound1", new DomMouseEventBoundObject(), isAsync: false, BindingOptions.DefaultBinder);

in ILifeSpanHandler.OnBeforePopup:

public bool OnBeforePopup(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser)
{
       newBrowser =new ChromiumWebBrowser(null); 
       newBrowser.JavascriptObjectRepository.Register("domClickBound2", new DomMouseEventBoundObject(), isAsync: false, BindingOptions.DefaultBinder); 
       return false;
}

DomMouseEventBoundObject.class:

public class  DomMouseEventBoundObject{
   boolean Handle(String message){
      System.Console.WriteLine("js:"+message);
      return false;
   }
}

page1.html:

await CefSharp.BindObjectAsync('domClickBound1}';
domClickBound1.handle("page1");

page2.html:

await CefSharp.BindObjectAsync('domClickBound2}';
domClickBound2.handle("page2");

Open Page2 in a new window by clicking page1.
The JS of page1 can call the handle method of dommouseeventboundobject and output "JS: page1" on the console.

The JS of Page2 may not successfully call the handle method of dommouseeventboundobject. During debugging, there is no breakpoint, and "JS: Page2" is not output on the console.

thanks very much!

@haohaodz
Copy link
Author

haohaodz commented Aug 3, 2020

can help me? thanks very much!

@amaitland
Copy link
Member

No, you cannot do that. I'd suggest you consider some other options, build a list of all element id's and inject that in with your JavaScript, perform all the processing in JavaScript or block all click events and pass in the url to PostMessage performing the navigation in code.
I'm sure you can think of something.

You will need to find an alternative solution as I've already suggested.

@haohaodz
Copy link
Author

haohaodz commented Aug 3, 2020

OK, thank you for your help

@amaitland
Copy link
Member

The Async JavaScript Binding implementation should work as expected, it appears you are attempting to use the sync version which is no longer actively being developed (bug fixes only) as specified in https://github.com/cefsharp/CefSharp/wiki/General-Usage#sync-javascript-binding-jsb

The sync binding creates a WCF channel for each browser (including popups). For popups we route all requests through the host for the parent browser of a popup. Attempting to host a popup in a new ChromiumWebBrowser instance would need code changes to support this use case.

You can review the current code at https://github.com/cefsharp/CefSharp/blob/cefsharp/83/CefSharp.BrowserSubprocess.Core/WcfEnabledSubProcess.cpp#L18

As stated above I have no plans on implementing anything other than bug fixes. You are welcome to submit a PR for consideration.

See the Manually attaching to a child process section of https://www.chromium.org/developers/how-tos/debugging-on-windows for details on how to attach to a render processes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants