Accessing iframe from chrome extension

前端 未结 1 1791
说谎
说谎 2020-12-01 07:10

I\'m developing a chrome extension and bumped into a big problem.

I\'m using content scripts to inject my javascript code on a web site. The web site has an iframe.

相关标签:
1条回答
  • 2020-12-01 07:29

    To understand why your code does not work, I include a fragment of my previous answer:

    Content scripts do not have any access to a page's global window object. For content scripts, the following applies:

    • The window variable does not refer to the page's global object. Instead, it refers to a new context, a "layer" over the page. The page's DOM is fully accessible. #execution-environment

    Given a document consisting of   <iframe id="frameName" src="http://domain/"></iframe>:

    • Access to the contents of a frame is restricted by the Same origin policy of the page; the permissions of your extension does not relax the policy.
    • frames[0] and frames['frameName'], (normally referring to the the frame's containing global window object) is undefined.
    • var iframe = document.getElementById('frameName');
      • iframe.contentDocument returns a document object of the containing frame, because content scripts have access to the DOM of a page. This property is null when the Same origin policy applies.
      • iframe.contentDocument.defaultView (refers to the window object associated with the document) is undefined.
      • iframe.contentWindow is undefined.

    Solution for same-origin frames

    In your case, either of the following will work:

    // jQuery:
    $("#iframe1").contents()[0].execCommand( ... );
    
    // VanillaJS
    document.getElementById("iframe1").contentDocument.execCommand( ... );
    
    // "Unlock" contentWindow property by injecting code in context of page
    var s = document.createElement('script');
    s.textContent = 'document.getElementById("iframe1").contentWindow.document.execCommand( ... );';
    document.head.appendChild(s);
    

    Generic solution

    The generic solution is using "all_frames": true in the manifest file, and use something like this:

    if (window != top) {
        parent.postMessage({fromExtension:true}, '*');
        addEventListener('message', function(event) {
            if (event.data && event.data.inserHTML) {
                document.execCommand('insertHTML', false, event.data.insertHTML);
            }
        });
    } else {
        var test_html = 'test string';
        // Explanation of injection at https://stackoverflow.com/a/9517879/938089 :
        // Run code in the context of the page, so that the `contentWindow`
        //  property becomes accessible
        var script = document.createElement('script');
        script.textContent = '(' + function(s_html) {
            addEventListener('message', function(event) {
                if (event.data.fromExtension === true) {
                    var iframe = document.getElementById('iframe1');
                    if (iframe && (iframe.contentWindow === event.source)) {
                        // Window recognised, post message back
                        iframe.contentWindow.postMessage({insertHTML: s_html}, '*');
                    }
                }
            });
        } + ')(' + JSON.stringify(test_html) + ');';
        (document.head||document.documentElement).appendChild(script);
        script.parentNode.removeChild(script);
    }
    

    This demo is for educational purposes only, do not use this demo in a real extension. Why? Because it uses postMessage to pass messages around. These events can also be generated by the client, which causes a security leak (XSS: arbitrary HTML injection).

    The alternative to postMessage is Chrome's message API. For a demo, see this answer. You won't be able to compare the window objects though. What you can do is to rely the window.name property. The window.name property is automatically set to the value of the iframe's name attribute (just once, when the iframe is loaded).

    0 讨论(0)
提交回复
热议问题