Using postMessage in iframes, IE 8 & 9

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

Using postMessage in iframes, IE 8 & 9
 
Sergey Grischenko


Add-in Express team


Posts: 7202
Joined: 2004-07-05
Hi Frank,

The code above doesn't use the Add-in Express library. Probably the issue is in MSHTML. I will check it and will let you know about results soon.
Posted 31 Oct, 2012 12:06:18 Top
Frank Oyer




Posts: 11
Joined: 2012-10-25
Hi Sergey, thank you for your responses on this. I would like to say that it is calling MSHTML inside of IEModule_DocumentComplete2, that's really the only way I'm integrating add-in express at this moment. Is there a better way I can do this (besides mshtml)? I'm open to suggestions. I really have no idea what I am doing when it comes to visual studio/c# this is new territory for me. I apologize for that.
Posted 31 Oct, 2012 13:38:57 Top
Sergey Grischenko


Add-in Express team


Posts: 7202
Joined: 2004-07-05
Hi Frank,

It seems that I found the cause of the issue.
The code of the first post contains and error. Please change the code as shown below:

//the wrong code:
doc.parentWindow.execScript(
"var iframe = document.getElementById('iframe-id');
iframe.onload = function() { alert('yup..');
// here 'postMessage' is executed in the wrong context
iframe.contentWindow.postMessage('TESTTTTT!!!!','*');
};", "JScript");

// the correct one:
doc.parentWindow.execScript(
"var iframe = document.getElementById('iframe-id');
iframe.onload = function() { alert('yup..'); };
// here 'postMessage' is executed in the context of the main page
iframe.contentWindow.postMessage('TESTTTTT!!!!','*');", "JScript");

I have just tested it in my test add-on + web application created on the local IIS.
It works fine in Windows 7 + IE8. To emulate the parent web page and an external iframe I used the code from this web site: http://help.dottoro.com/ljjqtjsj.php
Posted 31 Oct, 2012 16:18:53 Top
Frank Oyer




Posts: 11
Joined: 2012-10-25
Hello Sergey,

Thank you I have been able to get the postMessage to communicate with the iframe and back and forth but it works sometimes and I have another issue that I think is part of my problem. DocumentComplete2 function is getting called multiple times, intermittently, and it only works when it does get called multiple times, but then the script executes multiple times and I get duplicated responses from the iframe. But like I said sometimes it doesn't I can't explain it...

First I have IE 8 setup to open on debug and it is set to open up www.newegg.com right on startup.

Here is my entire IEModule_DocumentComplete2 function:



        private void IEModule_DocumentComplete2(object pDisp, string url, bool rootDocLoaded)
        {
            if (rootDocLoaded == true)
            {
                MessageBox.Show("root doc loaded");

                mshtml.HTMLDocument doc = this.HTMLDocument;

                //check if iframe is already injected.
                var isInjected = doc.getElementById("iframe-id");
                if (isInjected != null)
                {
                    MessageBox.Show("iframe is already inject, abort!");
                    //return;
                }

                bool active = true;
                if (active.Equals(true))
                {
                    mshtml.IHTMLElement head = (mshtml.IHTMLElement)((mshtml.IHTMLElementCollection)doc.all.tags("head")).item(null, 0);
                    mshtml.IHTMLScriptElement scriptObject = (mshtml.IHTMLScriptElement)doc.createElement("script");
                    scriptObject.type = "text/javascript";
                    scriptObject.text = "var iframe = docu ment.createElement('iframe'); iframe.id = 'iframe-id'; iframe.style.width = '300px'; iframe.style.height = '300px'; iframe.setAttribute('src', 'http://foyer.sytes.net/ietest.html'); document.getElementsByTagName('body')[0].appendChild(iframe); window   .attachEvent("onmessage", f unction(e){ if (e.origin == 'http://foyer.sytes.net') { a lert(e.data); } });";
                    ((mshtml.HTMLHeadElement)head).appendChild((mshtml.IHTMLDOMNode)scriptObject);

                    doc.parentWindow   .execScript("var iframe = docu ment.getElementById('iframe-id'); iframe.contentWindow   .postMessage('from addon','*');");

                    Marshal.ReleaseComObject(doc);
                }
            }
        }





Sometimes I have to load the page multiple times before it works. When MessageBox.Show("iframe is already inject, abort!"); executes, it works, but when that message shows, it should abort because the iframe is already loaded. Like I said, it only works when it executes the second time. I'm at a total loss at this point. Does this make sense?
Posted 01 Nov, 2012 11:10:06 Top
Frank Oyer




Posts: 11
Joined: 2012-10-25
If instead I use IEModule_DocumentComplete, is there a way to know if the top level document is loaded, and have access to it's, and only it's DOM? DocumentComplete2 doesn't reload on a browser refresh but DocumentComplete seems to.

Edit:
The more I play around with this, it only executes properly when it gets called twice or more, if the code only runs once than it doesn't postMessage or alert. I believe it's being called more than once because of ajax calls in the page and other things like that.

Another thing that helped get this working which was in one of the links you posted, Sergey. Is
window  .s etTimeout(f unction() { iframe.contentWindow  .postMessage('from addon','*'); }, 0);
using setTimeout function to postMessage, at least when injecting the code directly into the head.

So basically, I believe if I can figure out why it is loading DocumentComplete2 multiple times I will be happy with the results.
Posted 01 Nov, 2012 11:12:57 Top
Sergey Grischenko


Add-in Express team


Posts: 7202
Joined: 2004-07-05
Hi Frank,

You are right, DocumentComplete2 is called multiple times because of ajax requests that change the content of the HTML document. I used to face a similar problem when I developed an add-on that called a web service to check the user's notifications in CRM. The add-on displayed a jQuery based popup window if there were any notifications in inbox. My solution was based on a timer that I ran from DocumentComplete2. The timer injected javascript when the ReadyState property of the browser is set to 'READYSTATE_COMPLETE'. And if it was not, the timer tried again after 300ms delay. This approach works so far. Probably you need to implement the same logic in your app.

By the way, to detect if the user refreshes the document (via F5, Ctrl+F5 or Refresh command), you can use the OnBeforeRefresh event of iemodule. The event requires the 'HandleDocumentUICommands' property to be set to true.
Posted 01 Nov, 2012 16:29:51 Top
Frank Oyer




Posts: 11
Joined: 2012-10-25
Hello Sergey. Thanks for the input. I did look into the OnBeforeRefresh function, but it fires right before refresh. I didn't know how to make it call the onDocumentComplete2 event after it was called. I ended up useing OnDocumentComplete like this: This loads the "root" doc, and works on refresh.


        private void IEModule_DocumentComplete(object pDisp, string url)
        {
            mshtml.HTMLDocument doc = this.HTMLDocument;

            if (url.StartsWith(doc.location.protocol + "//" + doc.location.hostname))
            {
                //check if iframe is already injected.
                var isInjected = doc.getElementById("ifrm-01");
                if (isInjected != null)
Posted 03 Nov, 2012 14:21:09 Top
Sergey Grischenko


Add-in Express team


Posts: 7202
Joined: 2004-07-05
Hi Frank,

You can set up a local variable to true in the OnBeforeRefresh event handler and then use this variable in DocumentComplete to detect if the refresh has occured.
Posted 05 Nov, 2012 05:35:01 Top