ie-runtime-scripting example failing on IE 10... Or I can't inject a script

Add-in Express™ Support Service
That's what is more important than anything else

ie-runtime-scripting example failing on IE 10... Or I can't inject a script
The example fails on IE 10, so does a simple script injection during DocumentComplete2 
George Kierstein




Posts: 47
Joined: 2013-05-04
Hi,

I have followed the ie-runtime-scripting example, as well as forum posts, to create a simple add-on that attempts to inject a script. What I am finding is that both my code and the example both fail to inject or alter the document when the DocumentComplete2 callback is called.

For testing I have create a simple html file:


<html>
  <head></head>
  <body>Empty Body</body>
<html>


and a simple add-on which successfully gets the DocumentComplete2 callback triggered:


   private void IEModule_DocumentComplete2(object pDisp, string url, bool rootDocLoaded)
        {
            MessageBox.Show("DocumentComplete2> Called - " + url);

            var head = HTMLDocument.getElementsByTagName("head").item(null, 0) as mshtml.IHTMLElement;

            if (head == null)
            {
                MessageBox.Show("No Head");
                return;
            }

            var head2 = head as mshtml.HTMLHeadElement;
            // var head2 = head as mshtml.IHTMLHeadElement; - works too 

            if (head2 == null)
            {
                MessageBox.Show("No Head 2");
                return;
            }

            var el = HTMLDocument.createElement("script") as mshtml.IHTMLScriptElement;

            string scriptText = "function done(){alert("hello"); }";
            el.type = @"text/javascript";
            el.text = "
" + scriptText + "
";
            //el.src = @"//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js";

            head2.appendChild((mshtml.IHTMLDOMNode)el);

            Marshal.ReleaseComObject(head2);
        }


The add-on's callback is successfully called and none of this code generates an exception but there is nothing put into the page. When trying out the scripting example I get an exception when it attempts to call the script it swapped out (which I can see it do in the debugger). (Yes I did click the 'replace the script' button first!)

Is there something about IE 10 that I need to do ? Is it because I used Visual Studio 2010 ? Did I forget to release something ?

Thanks
Ms. G
Posted 04 May, 2013 23:36:29 Top
George Kierstein




Posts: 47
Joined: 2013-05-04
P.S. This is Windows 7
Posted 04 May, 2013 23:39:21 Top
George Kierstein




Posts: 47
Joined: 2013-05-04
Hmmm... I've down-graded to IE 9 and have found that this function fails to inject the script quite often. It is consistent on say the google page but in general there is no obvious reasons for how often this fails.

Also, is there a way to programatically tell if the new script took ? There are no exceptions being generated and I don't notice until I try to call it.

Thanks
G
Posted 05 May, 2013 19:25:51 Top
Sergey Grischenko


Add-in Express team


Posts: 7233
Joined: 2004-07-05
Hi George,

Please try the code below. To run the 'done' javascript function from the add-on you can use the 'HTMLDocument.parentWindow.execScript("done();", "JScript")' statement.

        private bool IsTargetDomain(string url)
        {
            try
            {
                if ((!String.IsNullOrEmpty(url)) && (url != "about:blank"))
                {
                    Uri uri = new Uri(url);
                    return uri.Host.StartsWith("www.google.");
                }
            }
            catch
            {
            }

            return false;
        }

        private string ScriptText = "var MyScriptFlag = true;
function done() { alert('hello'); }";

        private bool IsMyScriptInjected()
        {
            try
            {
                if (HTMLDocument != null)
                {
                    object scriptEngine = HTMLDocument.Script;
                    if (scriptEngine != null)
                    {
                        return Convert.ToBoolean(scriptEngine.GetType().InvokeMember(
                            "MyScriptFlag", System.Reflection.BindingFlags.GetProperty, null, scriptEngine, null));
                    }
                }
            }
            catch (Exception)
            {
            }

            return false;
        }

        private void IEModule_DocumentComplete2(object pDisp, string url, bool rootDocLoaded)
        {
            if (rootDocLoaded && (HTMLDocument != null))
            {
                if (IsTargetDomain(url) && (!IsMyScriptInjected()))
                {
                    mshtml.IHTMLScriptElement scriptObject =
                        (mshtml.IHTMLScriptElement)HTMLDocument.createElement("script");
                    scriptObject.type = @"text/javascript";
                    scriptObject.text = "
" + ScriptText + "
";
                    ((mshtml.HTMLBody)HTMLDocument.body).appendChild((mshtml.IHTMLDOMNode)scriptObject);
                }
            }
        }
Posted 06 May, 2013 04:48:58 Top
George Kierstein




Posts: 47
Joined: 2013-05-04
Hi,

Thanks for giving me an example. Basically I am trying to inject some javascript on to every page that loads. Ironically Google always works and most other pages don't. I've noticed that for the most part the parameter rootDocLoaded is always returning false. I've adapted some code from the Refresh example on the forum's that use a timer and wait for IE.tagREADYSTATE.READYSTATE_COMPLETE but it is rarely triggered.

Here's my code:


 private void InjectScript(IE.WebBrowser browser)
        {
            MessageBox.Show("Injecting Script");

            mshtml.IHTMLScriptElement scriptObject =
                        (mshtml.IHTMLScriptElement)HTMLDocument.createElement("script");

            scriptObject.type = @"text/javascript";

            string scriptText = "debug done() { a lert("hello"); }";
            scriptObject.text = "
" + scriptText + "
";

            ((mshtml.HTMLBody)HTMLDocument.body).appendChild((mshtml.IHTMLDOMNode)scriptObject);
        }


        private Timer timer = new Timer();

        void timer_Tick(object sender, EventArgs e)
        {
            if (IEApp.ReadyState == IE.tagREADYSTATE.READYSTATE_COMPLETE)
            {
                timer.Enabled = false;
                InjectScript(IEApp);
            }
        }

        private void IEModule_DocumentComplete2(object pDisp, string url, bool rootDocLoaded)
        {
            if (!rootDocLoaded)
            {
                MessageBox.Show("DocumentComplete> Starting Timer - Waiting for READYSTATE_COMPLETE");

                timer.Interval = 30;
                timer.Tick += new EventHandler(timer_Tick);
            }
            else
            {
                InjectScript(IEApp);
            }
        }


Why would most of the DocumentComplete2 callbacks report that the root doc isn't loaded ? Why is the timer basically never recieving the IE.tagREADYSTATE.READYSTATE_COMPLETE event ?

Am I completely off base on my approach ?

Thanks
G
Posted 08 May, 2013 00:09:05 Top
George Kierstein




Posts: 47
Joined: 2013-05-04
P.S. Try this against a site like Imgur which does a lot of page requests.
Posted 08 May, 2013 00:09:59 Top
Sergey Grischenko


Add-in Express team


Posts: 7233
Joined: 2004-07-05
Hi George,

The 'DocumentComplete' event fires when IE loads the data completely for a particular request. The event also fires for frames and iframes. I would advise you to change your code as show below:



        private void IEModule_OnConnect(object sender, int threadId)
        {
            // initialize the timer
            timer.Interval = 200;
            timer.Tick += new EventHandler(timer_Tick);   
        }

       private void IEModule_DocumentComplete2(object pDisp, string url, bool rootDocLoaded)   
       {   
           if (rootDocLoaded && (!timer.Enabled))   
           {   
               timer.Enabled = true;
           }   
       }  
Posted 08 May, 2013 06:48:07 Top
George Kierstein




Posts: 47
Joined: 2013-05-04
Thanks, this thread has been very helpful. I am now able to inject into the body of the document for every page and successfully call the embedded javascript!

One thing I am getting an error when I try injecting into an HTTPS page when the DocumentComplete callback is triggered. Is there restrictions on embedding into a page that was served over HTTPS ?

Here's the exception:


Application Domain: C:\Users\georgek\Documents\Disconnect.Me\IE Port\Working\DisconnectIE\DisconnectIE\bin\Debug\
Assembly Codebase: file:///C:/Windows/assembly/GAC/Microsoft.mshtml/7.0.3300.0__b03f5f7f11d50a3a/Microsoft.mshtml.dll
Assembly Full Name: Microsoft.mshtml, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Assembly Version: 7.0.3300.0

Exception Source:
Exception Type: System.Runtime.InteropServices.COMException
Exception Message: Could not set the src property. Operation aborted

Exception Target Site: set_src

---- Stack Trace ----
mshtml.IHTMLScriptElement.set_src(p As String)
Microsoft.mshtml.dll: N 00000 (0x0) JIT
DisconnectIE.IEModule.InjectScript(browser As WebBrowser)
IEModule.cs: line 0152, col 13, IL 0046 (0x2E)
DisconnectIE.IEModule.IEModule_DocumentComplete2(pDisp As Object, url As String, rootDocLoaded As Boolean)
IEModule.cs: line 0160, col 13, IL 0013 (0xD)
AddinExpress.IE.ADXIEModule.ADXIEModule_DocumentComplete(pDisp As Object, url As Object&)
Microsoft.mshtml.dll: N 0551 (0x227) IL



(Outer Exception)
Date and Time: 5/11/2013 5:14:17 PM
Machine Name: GEORGEKIERS20A0
IP Address: fe80::74af:7b8e:bc06:17b1%11
Current User: GEORGEKIERS20A0\georgek

Application Domain: C:\Users\georgek\Documents\Disconnect.Me\IE Port\Working\DisconnectIE\DisconnectIE\bin\Debug\
Assembly Codebase: file:///C:/Windows/assembly/GAC_MSIL/AddinExpress.IE/8.2.5067.0__4416dd98f0861965/AddinExpress.IE.dll
Assembly Full Name: AddinExpress.IE, Version=8.2.5067.0, Culture=neutral, PublicKeyToken=4416dd98f0861965
Assembly Version: 8.2.5067.0

Exception Source:
Exception Type: AddinExpress.IE.ADXIEExternalException
Exception Message: An error has occurred in the code of the extension.
Exception Target Site: Object reference not set to an instance of an object.

---- Stack Trace ----
Posted 11 May, 2013 19:19:07 Top
Sergey Grischenko


Add-in Express team


Posts: 7233
Joined: 2004-07-05
Hi George,

I am not aware of such restrictions. Do you check the state of the browser (the ReadyState property) in the Tick event handler of the timer?
Posted 13 May, 2013 04:22:31 Top