16

I have a WebBrowser control in my C# application. The web browser is under the user's control, that is, he can load any web page his computer can access on the web (of course limited by proxy, hosts file and so on).

I need to know and to be notified when there is a Javascript call inside the page loaded in the web browser component.

First example: given a link like this

<a href="javascript:void(0)" onclick="jsFunct();">test</a>

When the user clicks the link I need to know that the function "jsFunct" has been called.

Second example: given a call like

<script type="text/javascript">
    window.setTimeout("jsFunct()", 1000);
</script>

I need to know that, 1 second after the execution of the script, the function jsFunct has been called.

The best thing would be to have an event fired when the function is called. It would also be great if the event could get the Javascript code executed, or at least the function name in the arguments.

EDIT:

Even if the question is related to the webbrowser component, anything that allows the user to detect javascript activation (even via js) would be fine, being able to inject a js that handles the javascript event and passes it to the wb control triggering some event that it can handle.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Gabber
  • 5,152
  • 6
  • 35
  • 49

3 Answers3

8

You can use window.external to call a C# method when a global function is fired in JavaScript. See WebBrowser Control Overview for details on window.external.

You'll need to set ObjectForScripting: Webbrowser control's window.external is ALWAYS null. for this to work.

Take @Krishna's answer to add the JavaScript (but drop jQuery because it won't be needed):

private void addScript(HtmlElement head, string scriptSource) 
{ 
HtmlElement lhe_script = head.Document.CreateElement("script"); 
IHTMLScriptElement script = (IHTMLScriptElement)lhe_script.DomElement; 
script.src = scriptSource;
head.AppendChild(lhe_script);            
} 

addScript(WebBrowser.Head, @"InjectMonitor.js");

The JavaScript below (InjectMonitor.js) will find all global functions and attach your specified handler:

function augment(withFn) {
    var name, fn;
    for (name in window) {
        fn = window[name];
        if (typeof fn === 'function') {
            window[name] = (function(name, fn) {
                var args = arguments;
                return function() {
                    withFn.apply(this, args);
                    fn.apply(this, arguments);

                };
            })(name, fn);
        }
    }
}

augment(function(name, fn) {
    console.log("calling " + name, fn);

    // window.external.yourC#method
});

In this example, taken from Adding Console Log to Every Function, it just logs the call to console; but using window.external you could send some message back to your C# application with details of what function was called from the client.

Finally, here's a JS Bin example (run it and don't forget the console): JS Bin Example

Community
  • 1
  • 1
Jeff
  • 846
  • 8
  • 13
  • 1
    At the moment I have not much time to test this, could you explain how can I detect a call to a specific js function in the webpage with `window.external `? A small code example would also be appreciated – Gabber Oct 12 '12 at 09:19
  • updated, I added the relevant JS and hopefully complete solution. – Jeff Oct 12 '12 at 18:09
  • Quite what I needed, truly happy to award the bounty and to solve this problem, thanks! – Gabber Oct 12 '12 at 18:50
  • No problem, FYI, I had played with Function.prototype.call thinking that would do it but was never able to get it to work unless invoking call(). – Jeff Oct 12 '12 at 18:55
  • What is `WebBrowser.Head`? this is an example and you mean `WebBrowser.Document.GetElementsByTagName("head")[0]`? – Jack Jan 28 '15 at 16:38
8

On the webbrowser load event,

  1. Inject Jquery
  2. Inject Monitor scripts

,

private void addScript(HtmlElement head, string scriptSource) 
{ 
HtmlElement lhe_script = head.Document.CreateElement("script"); 
IHTMLScriptElement script = (IHTMLScriptElement)lhe_script.DomElement; 
script.src = scriptSource;
head.AppendChild(lhe_script);            
} 

addScript(Webbrowser.Head, @"<Change File Path here>jquery.min.js");
addScript(WebBrowser.Head, @"InjectMonitor.js");

your file InjectMonitor.js should be something like this

 $(document).ready(function () { 
        //Add click event for every anchor on the page loaded- note this merely alerts text on click. you can however add your own function
        $("a").click(function (e) { alert($(this).text()); return false;}) 
    }); 
Krishna
  • 2,451
  • 1
  • 26
  • 31
  • +1 for pointing me in the correct direction, let's do a bit more of work to get it an answer – Gabber Mar 16 '12 at 10:07
  • do let me know how you get along. if you are stuck somewhere, I will try to set this project up at my end aswell. this is a good one and believe quite possible – Krishna Mar 16 '12 at 10:59
  • Could you explain better what you mean by "Monitor scripts"? thanks – Gabber Mar 16 '12 at 11:27
  • jQuery supports multiple handlers for same event, so if you would like to know when somebody clicked on an anchor (for e.g.) you can add the your code to the MonitorScript (see my edited answer above) – Krishna Mar 16 '12 at 12:40
  • I understand now, but this triggers only on events, not on each javascript function call (the "Second example" of my question won't fit in your answer). Besides I should register an event on every possible action of the user, this might be the only way, but I think it is overkill, I hope there is a better way. – Gabber Mar 16 '12 at 13:02
  • if you want to monitor all events (on a page loaded at runtime) this may be the only generic way. on the second part of the question, I believe you can inject calls to javascript function as well - using jquery. I found a post in SO that may lead you in the right direction http://stackoverflow.com/questions/6320773/wrap-the-event-handler-in-jquery – Krishna Mar 18 '12 at 09:02
  • But I need to monitor all the javascript calls, not all the events – Gabber Mar 19 '12 at 08:14
  • 1
    I believe that is what the post is saying, wrap additional calls to existing javascript code (not in your control) loaded in the webbrowser code. i.e. for every javascript function out there in the page, inject a call to your listener (which is/will be defined in the MonitorScript.js). let me see if I can get this code together for you. I am away this week, but this will be a good task to ponder upon – Krishna Mar 19 '12 at 16:59
  • This is ok in the case presented by the first example (only if the javascript function is called on click of `a`) but not in the second. Thanks anyway – Gabber Oct 05 '12 at 10:43
1

Well what krishna has answered is interms of pure javascript attaching to events, however i see that you might need to attach it to all the tags(a,p,div,input) etc and to all the events attached to each tag.

i believe the another way is to play around with the BHO(browser helper object) available to your in .net, and if not and you are good at VC++ and MFC you can also play around with Windows Hooks.

  • The problem with attaching javascript to events is that it doesn't detect the execution of javascript executed with functions like `setTimeout`. Moreover what if a click doesn't trigger a javascript call? No good. I need to detect the execution of a javascript function whenever it happens, should it be called in an event handler, triggered by a timeout or in every other way. – Gabber Oct 09 '12 at 11:56