[SOLVED] Catch Javascript Events in CEFSharp

Hi,

I have a VB.NET application using CEFSharp and I am really very very happy with it.
Currently I am able to inject JS code into a target site to e.g. trigger functions or set textbox values, on the site, etc.
I also managed in the meantime to read values from e.g. textbox controls on the target website.

Now I am struggeling with further development:
I would also like to be able to react with my code on a javascript event on the displayed website.
(e.g. I could call a vb.net function in my code when a specific javascript event is fired on the website)

At the moment I am looking specialy to catch mouseup events and (if possible) also when the mousewheel is moved.

I already found 2-3 related references on the web in C# (including the example in the CEFSharp FAQ) but I am not entirely sure how to port it to VB.NET.  See here the mentioned example:  https://github.com/cefsharp/CefSharp/wi … ns#JSEvent

So inprinciple it should be possible, but I am just lost in translation 1f600

Could someone point me to the right direction?
Thanks in advance for your help.

Otti

Like +1 Dislike

Hiya @hbottiki,

I haven't particularly worked with CEFSharp for a while, but the translation for the example you provided can be translated into VB.NET as follows:
https://paste.imirhil.fr/?16ea3f91116b7 … Im54bPthI=

The explanation provided on the FAQ should also work both ways:

  • RegisterJsObject should be called directly after you create an instance of ChromiumWebBrowser.

  • "bound" is the name of the object that will be created as a top-level object accessible to any JS scripts running on the webpage. You can use whatever you like, but ensure it does not conflict with an existing JS object, and that the names match in VB.NET and JS.

  • This sample injects JS code into the webpage on-the-fly using the FrameLoadEnd event. If you have control of the webpage, you can simply add the JS code into the webpage.

  • We call the OnSelected VB.NET method with a single string argument. You can use any number of arguments, or none, but they must all be primitive types.

  • Ensure you cast complex JS objects to strings or convert them to JSON when passing them.

  • Since CEFSharp will be searching for the method OnSelected by its name, ensure your obfuscator does NOT rename the method to something else, or it will not be executed.

Assuming you want to catch the MouseWheel event (as the MouseUp event use-case has already been provided), you can just switch out the JS code in the OnFrameLoadEnd method with the following:

document.addEventListener('mousewheel', function(e) 
{ 
   bound.onSelected(window.getSelection().toString());
}, false)

Hope this helps! 1f609

Like 0 Dislike

I was trying to reply here, but get Error 403 Forbidden messages (looks like when I remove the parts marked as "code" it works... ) 1f600

Thanks a lot. I only was able to test this code today. Unfortunately I do not get it running (already tried a few tweaks)
I get some errors thrown already within the IDE.

I added a new class to my project called BoundObject.vb and copied your class code. I think this is OK?
Then I added the last three lines of your code to Public Sub New() right after the code where ChromiumBrowser is initialized (as per FAQ suggestions)

In the class I had to add imports cefsharp and a reference to the class (Form1) where the browser is initialized in.
Otherwise it shows an error since the browser is a member of the main class (Form1)...

Now when I get to this line:
AddHandler obj.OnFrameLoadEnd, AddressOf browser.FrameLoadEnd

It shows two errors:
First OnFrameLoadEnd it says:
- For the parameter "sender" of "Public Sub OnFrameLoadEnd(sender As Object, e As FrameLoadEndEventArgs)" no argument was defined
- For the parameter "e" of "Public Sub OnFrameLoadEnd(sender As Object, e As FrameLoadEndEventArgs)" no argument was defined

And for the last part of that line it says:
- The AddressOf-Operator must be equal to a name of a Method (without parentheses)


Sorry if this is not 100% identical to the real English errors thrown by VS since I am using a non English locale (therefore need to roughly translate it) 1f600

Like 0 Dislike

Yeah sometimes the forum won't allow code to be posted due to XSS (Cross-Site-Scripting) security concerns (usually when JavaScript code is detected), and will instead throw a 403 error - which is why I posted the code to a pastebin 1f609

Oh I think it's supposed to be "OnFrameLoadEnd" for the "AddressOf" portion (line 20): https://paste.imirhil.fr/?3db41197e7a2e … LmWNkoOkI=

Like 0 Dislike

OK, thanks, next time I will use Pastebin also in case I want to share code containing JS....

Unfortunately if I change it to OnFrameLoadEnd it says that  OnFrameLoadEnd is not a member of "ChromiumWebBrowser".
(but FrameLoadEnd, as it was before, seems to be a member.....just throws that other error mentioned before)

Do I need to place it in a separate class or could I add the Subs just to the main class? Maybe I am just doing something wrong with that class...

Otti

Like 0 Dislike

Hi,

I made some progress with some trial and error coding.....I noticed that perhaps the AdressOf should be the other way around (at least it would be more logic for me)?

If I use this code the other way around (see below) there are no more errors shown in the IDE:

  
CefSharpSettings.LegacyJavascriptBindingEnabled = True
Dim obj As New BoundObject()
browser.RegisterJsObject("bound", obj)
AddHandler browser.FrameLoadEnd, AddressOf obj.OnFrameLoadEnd

I had to add the first line, because CEFSharp was throwing an error at runtime suggesting using this code.....
Seems the folks at CEFSharp have changed the method but with this line still provide support for the old method?
(see https://github.com/cefsharp/CefSharp/issues/2246 )

But now I have the problem that I get an system.exception as follows:

"Cef can only be initialized once. Use Cef.IsInitialized to guard against this exception.."

It seems to happen in the BoundObject class in the line where the JavaScript is executed...
I find it strange because it only initialized once in the Public Sub New()

Sorry for my noobish problems, but I really struggle with this thing - it looks so simple in theory 1f600

Like 0 Dislike

How are you accessing "browser" within the BoundObject class? Is it already accessible? I'm assuming you'd have to declare it again and then assign it (seeing as though the BoundObject class is in a separate file unlike in the example):

Public Class BoundObject
  Public browser = ChromiumWebBrowser()
  Public Sub OnFrameLoadEnd(ByVal sender As Object, ByVal e As FrameLoadEndEventArgs)
    If e.Frame.IsMain Then
      browser.ExecuteScriptAsync("")
    End If
  End Sub
 
  Public Sub OnSelected(ByVal selected As String)
    MessageBox.Show("The user selected some text [" & selected & "]")
  End Sub
End Class
CefSharpSettings.LegacyJavascriptBindingEnabled = True
Dim obj As New BoundObject()
obj.browser = browser
browser.RegisterJsObject("bound", obj)
AddHandler browser.FrameLoadEnd, AddressOf obj.OnFrameLoadEnd

I'm kinda lost on this one 1f615

Like 0 Dislike

Sorry, I did not notice your reply (reply notification went into spam). Thanks for your patience.

Now I got it almost running....(actually it runs, but not fully as it should)
This is what I did.

Here the class code (minor modification from yours, like imports and declaring browser as friend):

Imports CefSharp.WinForms
Imports CefSharp

Public Class BoundObject
    Friend browser As ChromiumWebBrowser

    Public Sub browser_FrameLoadEnd(sender As Object, e As FrameLoadEndEventArgs)
        If e.Frame.IsMain Then
            browser.ExecuteScriptAsync("javascript function goes here;")
        End If
    End Sub

    Public Sub OnSelected(ByVal selected As String)
        MessageBox.Show("The user selected some text [" & selected & "]")
    End Sub
End Class

Then to the main code I added this code (looks like later CEFSharp versions use different event names, so I picked the most similar one):

       

 CefSharpSettings.LegacyJavascriptBindingEnabled = True
        Dim obj As New BoundObject()
        obj.browser = browser
        browser.RegisterJsObject("bound", obj)
        AddHandler browser.FrameLoadEnd, AddressOf obj.browser_FrameLoadEnd

I removed the Javascript from this post to prevent the script filer blocking my post - but I used the JS code from the original code example which was the same as from your pastebin.

Now, when I start the application and load a website and the site finishes loading I can click or mark text with the left mouse button and nothing fires.

At least the link between JS and CEFSharp is working, because when I open in CEF the developer tools console  I can call the bound.onSelected function and pass a string over to CEFSharp and the textbox fires correctly in my program.

So I assume something is perhaps wrong with the JS initialisation string to bind a mouseclick to call this exposed bound JS function?

Not sure what else I can try...

Like +1 Dislike

Hmm maybe it's the JS that's not working, try changing "document.body.onmouseup" to "document.onmouseup"...

Like 0 Dislike

Hi....I tried now different Javascript for browser.ExecuteScriptAsync and it seems to work now....thanks for your suggestions and kind help. Thumbs up for this forum.

I tried now setting up an event listener in JavaScript and I use  document.addEventListener(""click"", function(){alert('On Selected fired')});

Instead of just calling an alert box as in this example I can now also call the bound.onSelected and it will call the VB.NET function in the BoundObject class. I can also pass a variable from the website, e.g. updated value of a textbox value on the website to my VB.NET code.

Works like a charme - Thanks...
I will mark it solved.

Like +2 Dislike