How to inject Javascript in WebBrowser control?

Viewed 170783

I've tried this:

string newScript = textBox1.Text;
HtmlElement head = browserCtrl.Document.GetElementsByTagName("head")[0];
HtmlElement scriptEl = browserCtrl.Document.CreateElement("script");
lblStatus.Text = scriptEl.GetType().ToString();
scriptEl.SetAttribute("type", "text/javascript");
head.AppendChild(scriptEl);
scriptEl.InnerHtml = "function sayHello() { alert('hello') }";

scriptEl.InnerHtml and scriptEl.InnerText both give errors:

System.NotSupportedException: Property is not supported on this type of HtmlElement.
   at System.Windows.Forms.HtmlElement.set_InnerHtml(String value)
   at SForceApp.Form1.button1_Click(Object sender, EventArgs e) in d:\jsight\installs\SForceApp\SForceApp\Form1.cs:line 31
   at System.Windows.Forms.Control.OnClick(EventArgs e)
   at System.Windows.Forms.Button.OnClick(EventArgs e)
   at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
   at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.ButtonBase.WndProc(Message& m)
   at System.Windows.Forms.Button.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

Is there an easy way to inject a script into the dom?

15 Answers

For some reason Richard's solution didn't work on my end (insertAdjacentText failed with an exception). This however seems to work:

HtmlElement head = webBrowser1.Document.GetElementsByTagName("head")[0];
HtmlElement scriptEl = webBrowser1.Document.CreateElement("script");
IHTMLScriptElement element = (IHTMLScriptElement)scriptEl.DomElement;
element.text = "function sayHello() { alert('hello') }";
head.AppendChild(scriptEl);
webBrowser1.Document.InvokeScript("sayHello");

This answer explains how to get the IHTMLScriptElement interface into your project.

If all you really want is to run javascript, this would be easiest (VB .Net):

MyWebBrowser.Navigate("javascript:function foo(){alert('hello');}foo();")

I guess that this wouldn't "inject" it but it'll run your function, if that's what you're after. (Just in case you've over-complicated the problem.) And if you can figure out how to inject in javascript, put that into the body of the function "foo" and let the javascript do the injection for you.

The managed wrapper for the HTML document doesn't completely implement the functionality you need, so you need to dip into the MSHTML API to accomplish what you want:

1) Add a reference to MSHTML, which will probalby be called "Microsoft HTML Object Library" under COM references.

2) Add 'using mshtml;' to your namespaces.

3) Get a reference to your script element's IHTMLElement:

IHTMLElement iScriptEl = (IHTMLElement)scriptEl.DomElement;

4) Call the insertAdjacentText method, with the first parameter value of "afterBegin". All the possible values are listed here:

iScriptEl.insertAdjacentText("afterBegin", "function sayHello() { alert('hello') }");

5) Now you'll be able to see the code in the scriptEl.InnerText property.

Hth, Richard

As a follow-up to the accepted answer, this is a minimal definition of the IHTMLScriptElement interface which does not require to include additional type libraries:

[ComImport, ComVisible(true), Guid(@"3050f28b-98b5-11cf-bb82-00aa00bdce0b")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
[TypeLibType(TypeLibTypeFlags.FDispatchable)]
public interface IHTMLScriptElement
{
    [DispId(1006)]
    string text { set; [return: MarshalAs(UnmanagedType.BStr)] get; }
}

So a full code inside a WebBrowser control derived class would look like:

protected override void OnDocumentCompleted(
    WebBrowserDocumentCompletedEventArgs e)
{
    base.OnDocumentCompleted(e);

    // Disable text selection.
    var doc = Document;
    if (doc != null)
    {
        var heads = doc.GetElementsByTagName(@"head");
        if (heads.Count > 0)
        {
            var scriptEl = doc.CreateElement(@"script");
            if (scriptEl != null)
            {
                var element = (IHTMLScriptElement)scriptEl.DomElement;
                element.text =
                    @"function disableSelection()
                    { 
                        document.body.onselectstart=function(){ return false; }; 
                        document.body.ondragstart=function() { return false; };
                    }";
                heads[0].AppendChild(scriptEl);
                doc.InvokeScript(@"disableSelection");
            }
        }
    }
}

You can always use a "DocumentStream" or "DocumentText" property. For working with HTML documents I recommend a HTML Agility Pack.

i use this:

webBrowser.Document.InvokeScript("execScript", new object[] { "alert(123)", "JavaScript" })

What you want to do is use Page.RegisterStartupScript(key, script) :

See here for more details: http://msdn.microsoft.com/en-us/library/aa478975.aspx

What you basically do is build your javascript string, pass it to that method and give it a unique id( in case you try to register it twice on a page.)

EDIT: This is what you call trigger happy. Feel free to down it. :)

Related