Give Chrome extension access to iframe contents

2019-08-09 16:48发布

问题:

I've made a Chrome Extension that loads my New Tab page with an <iframe> of my YouTube subscription page (I got around the X-frame-options issue) and want to isolate a specific element of the document, #content to be exact (that being the subscription feed). Whether that be subtracting everything that is :not(#content) or cutting #content out and putting it into another <div> are both viable. Here's my code:

Background.html

<html> 
    <head>   
        <script type="text/javascript" src="jquery-2.2.0.min.js"></script>
        <script type="text/javascript" src="script.js"></script>
        <script type="text/javascript" src="window.js"></script>
    </head>
    <body>
        <iframe id="left"  name="left"></iframe>
        <div id="canvas"></div>
    </body>
</html>

Window.js

$(document).ready(function(){
    var $left = $('#left');    
    $left.on('load', function() {
        $(this).contents().find('#content').clone().appendTo('#canvas');
    });    
    $left.attr('src', 'https://www.youtube.com/feed/subscriptions');    
});

Script.js

chrome.webRequest.onHeadersReceived.addListener(
    function(info) {
        var headers = info.responseHeaders;
        for (var i = headers.length - 1; i >= 0; --i) {
            var header = headers[i].name.toLowerCase();
            if (header == 'x-frame-options' || header == 'frame-options') {
                headers.splice(i, 1); // Remove header
            }
        }
        return {
            responseHeaders: headers
        };
    }, {
        urls: ['*://*/*'], // Pattern to match all http(s) pages
        types: ['sub_frame']
    }, ['blocking', 'responseHeaders']
);

Manifest.json

{
    "manifest_version": 2,
    "version": "1.0",

    "background": "background.html",

    "chrome_url_overrides" : {
        "newtab": "background.html"
    },
    "permissions": [
        "background",
        "contextMenus",
        "webRequest",
        "webRequestBlocking",
        "tabs",
        "<all_urls>"
    ]   
}

As it is right now, YouTube loads, but I can't get a hold of the document in the <iframe>, as it's logging an error of:

Uncaught SecurityError: Failed to read the 'contentDocument' property from 
'HTMLIFrameElement': Blocked a frame with origin "chrome-extension://ID" from 
accessing a frame with origin "https://www.youtube.com".  The frame requesting 
access has a protocol of "chrome-extension", the frame being accessed has a 
protocol of "https". Protocols must match.

I'm trying to get that div#contentisolated for ease of navigation from my new tab page. I've seen solutions that propose using "all_frames":true in the manifest.json file, but that doesn't seem to solve it. Any ideas? Thanks!

回答1:

From the description here and chrome extension related questions you asked these days, I'd like to suggest you learn more from Official Guide, since it seems you are confused by Event page and content scripts ( you should know all_frames only apply to content scripts).

Take a look at Same-origin Policy and you should know Chrome extension doesn't allow you to access contentDocument of the iframe.

Since your purpose is to extract the #content part from youtube then append that to your canvas in new tab page, why not

  1. just send a ajax request to youtube
  2. parse the returned html response and extract the #content part
  3. then append it to canvas

The code looks like:

Manifest.json

{
    "name": "Your extension name",
    "manifest_version": 2,
    "version": "1.0",
    "chrome_url_overrides": {
        "newtab": "newtab.html"
    }, 
    "permissions": [
        "https://www.youtube.com/feed/subscriptions/*"
    ]
}

newtab.html

<!DOCTYPE html>
<html> 
    <head>   
    </head>
    <body>
        <div id="canvas"></div>
        <script type="text/javascript" src="jquery.js"></script>
        <script type="text/javascript" src="script.js"></script>
    </body>
</html>

script.js

$.post("https://www.youtube.com/feed/subscriptions", function(html) {
   $(html).find("#content").appendTo('#canvas');
});

Please be aware that the original image src in youtube page looks like src="//...", you should add the right domain before // in your chrome extension. And youtube has many scripts to load, you also need to deal with that part of logic.

Last by not least, I think the best way is to find some public 3rd party api from youtube, I didn't use it but it seems Youtube | Google Developer may help.