Search code examples
c#internet-explorercomieaddon

How to inject styles to a page using IE addon


I am trying to convert a content script I wrote for Google Chrome into an IE Addon, mainly using the code in this answer.

I needed to inject an stylesheet and, I found a way to do it using Javascript. I thought I might be able to do the same using C#. Here's my code:

[ComVisible(true)]
[Guid(/* replaced */)]
[ClassInterface(ClassInterfaceType.None)]
public class SimpleBHO: IObjectWithSite
{
    private WebBrowser webBrowser;

    void webBrowser_DocumentComplete(object pDisp, ref object URL)
    {
        var document2 = webBrowser.Document as IHTMLDocument2;
        var document3 = webBrowser.Document as IHTMLDocument3;

        // trying to add a '<style>' element to the header. this does not work.
        var style = document2.createElement("style");
        style.innerHTML = ".foo { background-color: red; }";// this line is the culprit!
        style.setAttribute("type", "text/css");
        var headCollection = document3.getElementsByTagName("head");
        var head = headCollection.item(0, 0) as IHTMLDOMNode;
        var result = head.appendChild(style as IHTMLDOMNode);

        // trying to repace an element in the body. this part works if
        // adding style succeeds.
        var journalCollection = document3.getElementsByName("elem_id");
        var journal = journalCollection.item(0, 0) as IHTMLElement;
        journal.innerHTML = "<div class=\"foo\">Replaced!</div>";

        // trying to execute some JavaScript. this part works as well if
        // adding style succeeds.
        document2.parentWindow.execScript("alert('Hi!')");
    }

    int IObjectWithSite.SetSite(object site)
    {
        if (site != null)
        {
            webBrowser = (WebBrowser)site;
            webBrowser.DocumentComplete += new DWebBrowserEvents2_DocumentCompleteEventHandler(webBrowser_DocumentComplete);
        }
        else
        {
            webBrowser.DocumentComplete -= new DWebBrowserEvents2_DocumentCompleteEventHandler(webBrowser_DocumentComplete);
            webBrowser = null;
        }
        return 0;
    }

    /* some code (e.g.: IObjectWithSite.SetSite) omitted to improve clarity */
}

If I just comment out the following line...

style.innerHTML = ".foo { background-color: red; }";

... the rest of the code executes perfectly (The element #elem_id is replaced and the JavaScript I injected is executed).

What am I doing wrong when trying to inject the stylesheet? Is this even possible?

EDIT: I found out that the site I'm trying to inject CSS requests Document Mode 5, and when Compatibility view is disabled, my code works perfectly. But how do I make it to work even when compatibility view is enabled?


Solution

  • After lot of experimenting, I found out that only failsafe way to inject stylesheets to inject them using JavaScript, which is executed with IHTMLWindow2.execScript().

    I used following JavaScript:

    var style = document.createElement('style');
    document.getElementsByTagName('head')[0].appendChild(style);
    var sheet = style.styleSheet || style.sheet;
    
    if (sheet.insertRule) {
        sheet.insertRule('.foo { background-color: red; }', 0);
    } else if (sheet.addRule) {
        sheet.addRule('.foo', 'background-color: red;', 0);
    }
    

    The above JavaScript was executed in the following fashion:

    // This code is written inside a BHO written in C#
    var document2 = webBrowser.Document as IHTMLDocument2;
    document2.parentWindow.execScript(@"
        /* Here, we have the same JavaScript mentioned above */
        var style = docu....
        ...
        }");
    document2.parentWindow.execScript("alert('Hi!')");