How to inject Javascript in WebBrowser control?

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?

171281 次浏览

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. :)

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

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

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.

Also, in .NET 4 this is even easier if you use the dynamic keyword:

dynamic document = this.browser.Document;
dynamic head = document.GetElementsByTagName("head")[0];
dynamic scriptEl = document.CreateElement("script");
scriptEl.text = ...;
head.AppendChild(scriptEl);

this is a solution using mshtml

IHTMLDocument2 doc = new HTMLDocumentClass();
doc.write(new object[] { File.ReadAllText(filePath) });
doc.close();


IHTMLElement head = (IHTMLElement)((IHTMLElementCollection)doc.all.tags("head")).item(null, 0);
IHTMLScriptElement scriptObject = (IHTMLScriptElement)doc.createElement("script");
scriptObject.type = @"text/javascript";
scriptObject.text = @"function btn1_OnClick(str){
alert('you clicked' + str);
}";
((HTMLHeadElementClass)head).appendChild((IHTMLDOMNode)scriptObject);
HtmlDocument doc = browser.Document;
HtmlElement head = doc.GetElementsByTagName("head")[0];
HtmlElement s = doc.CreateElement("script");
s.SetAttribute("text","function sayHello() { alert('hello'); }");
head.AppendChild(s);
browser.Document.InvokeScript("sayHello");

(tested in .NET 4 / Windows Forms App)

Edit: Fixed case issue in function set.

I believe the most simple method to inject Javascript in a WebBrowser Control HTML Document from c# is to invoke the "execScript" method with the code to be injected as argument.

In this example the javascript code is injected and executed at global scope:

var jsCode="alert('hello world from injected code');";
WebBrowser.Document.InvokeScript("execScript", new Object[] { jsCode, "JavaScript" });

If you want to delay execution, inject functions and call them after:

var jsCode="function greet(msg){alert(msg);};";
WebBrowser.Document.InvokeScript("execScript", new Object[] { jsCode, "JavaScript" });
...............
WebBrowser.Document.InvokeScript("greet",new object[] {"hello world"});

This is valid for Windows Forms and WPF WebBrowser controls.

This solution is not cross browser because "execScript" is defined only in IE and Chrome. But the question is about Microsoft WebBrowser controls and IE is the only one supported.

For a valid cross browser method to inject javascript code, create a Function object with the new Keyword. This example creates an anonymous function with injected code and executes it (javascript implements closures and the function has access to global space without local variable pollution).

var jsCode="alert('hello world');";
(new Function(code))();

Of course, you can delay execution:

var jsCode="alert('hello world');";
var inserted=new Function(code);
.................
inserted();

Hope it helps

Here is a VB.Net example if you are trying to retrieve the value of a variable from within a page loaded in a WebBrowser control.

Step 1) Add a COM reference in your project to Microsoft HTML Object Library

Step 2) Next, add this VB.Net code to your Form1 to import the mshtml library:
Imports mshtml

Step 3) Add this VB.Net code above your "Public Class Form1" line:
<System.Runtime.InteropServices.ComVisibleAttribute(True)>

Step 4) Add a WebBrowser control to your project

Step 5) Add this VB.Net code to your Form1_Load function:
WebBrowser1.ObjectForScripting = Me

Step 6) Add this VB.Net sub which will inject a function "CallbackGetVar" into the web page's Javascript:

Public Sub InjectCallbackGetVar(ByRef wb As WebBrowser)
Dim head As HtmlElement
Dim script As HtmlElement
Dim domElement As IHTMLScriptElement


head = wb.Document.GetElementsByTagName("head")(0)
script = wb.Document.CreateElement("script")
domElement = script.DomElement
domElement.type = "text/javascript"
domElement.text = "function CallbackGetVar(myVar) { window.external.Callback_GetVar(eval(myVar)); }"
head.AppendChild(script)
End Sub

Step 7) Add the following VB.Net sub which the Javascript will then look for when invoked:

Public Sub Callback_GetVar(ByVal vVar As String)
Debug.Print(vVar)
End Sub

Step 8) Finally, to invoke the Javascript callback, add this VB.Net code when a button is pressed, or wherever you like:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
WebBrowser1.Document.InvokeScript("CallbackGetVar", New Object() {"NameOfVarToRetrieve"})
End Sub

Step 9) If it surprises you that this works, you may want to read up on the Javascript "eval" function, used in Step 6, which is what makes this possible. It will take a string and determine whether a variable exists with that name and, if so, returns the value of that variable.

Here is the easiest way that I found after working on this:

string javascript = "alert('Hello');";
// or any combination of your JavaScript commands
// (including function calls, variables... etc)


// WebBrowser webBrowser1 is what you are using for your web browser
webBrowser1.Document.InvokeScript("eval", new object[] { javascript });

What global JavaScript function eval(str) does is parses and executes whatever is written in str. Check w3schools ref here.

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");
}
}
}
}

If you need to inject a whole file then you can use this:

With Browser.Document
Dim Head As HtmlElement = .GetElementsByTagName("head")(0)
Dim Script As HtmlElement = .CreateElement("script")
Dim Streamer As New StreamReader(<Here goes path to file as String>)
Using Streamer
Script.SetAttribute("text", Streamer.ReadToEnd())
End Using
Head.AppendChild(Script)
.InvokeScript(<Here goes a method name as String and without parentheses>)
End With

Remember to import System.IO in order to use the StreamReader. I hope this helps.

I used this :D

HtmlElement script = this.WebNavegador.Document.CreateElement("SCRIPT");
script.SetAttribute("TEXT", "function GetNameFromBrowser() {" +
"return 'My name is David';" +
"}");


this.WebNavegador.Document.Body.AppendChild(script);

Then you can execute and get the result with:

string myNameIs = (string)this.WebNavegador.Document.InvokeScript("GetNameFromBrowser");

I hope to be helpful

i use this:

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

bibki.js

webBrowser1.DocumentText =
"<html><head><script>" +
"function test(message) { alert(message); }" +
"</script></head><body><button " +
"onclick=\"window.external.Test('called from script code')\">" +
"call client code from script code</button>" +
"</body></html>";