从Chrome扩展内容脚本访问iframe内容

45
我正在开发一个插件,对界面进行一些转换。但我一直得到“unsafe javascript attempt to access frame with url.... Domains, protocols and ports must match”(典型的跨站点问题)的错误提示。
作为一个扩展程序,它应该可以访问 iframe 的内容。请参考http://code.google.com/chrome/extensions/content_scripts.html
有没有人知道如何访问 iframe 的内容,以便可以捕获它们?

3
你想要做什么?在大多数情况下,你可以在清单文件的"content_scripts"部分中添加"all_frames":true,并在内容脚本中编写特定于页面的逻辑。 - Rob W
1
@RobW 添加 all_frames:true 将使 content_script 在所有内部框架中运行一次。这将大大复杂化它的逻辑。我只想通过 content_script 访问内部框架的内容。(例如:content_script --> target_page --> inner_content) - fmsf
不可能的:内容脚本无法访问页面的任何window对象(包括框架)。使用"all_frames": false注入一个内容脚本,其中设置一个标志。然后,注入一个带有"all_frames": true的脚本,检查此标志的存在。如果该标志不存在,则假定内容脚本正在框架中运行。然后,您可以将特定于框架的逻辑应用于它,并使用消息传递将获取的任何数据传递给主内容脚本。 - Rob W
好的,谢谢。但是你本可以在回答中加入这个内容来标记它,我已经在考虑按照你说的方式去做了,只是希望存在直接访问。 - fmsf
我不确定你想要什么。对于深度嵌套的 iframe,建议的解决方案行不通。我会发布一个简明的示例。 - Rob W
@robW 非常感谢,这对我正在尝试做的事情非常有帮助。非常感谢。 - John Ballinger
2个回答

54

通常情况下,没有直接访问不同源窗口对象的方法。如果您想在不同框架的内容脚本之间进行安全通信,必须向后台页发送消息,然后再将消息发送回选项卡。

这里是一个例子:

manifest.json的一部分:

"background": {"scripts":["bg.js"]},
"content_scripts": [
    {"js": ["main.js"], "matches": ["<all_urls>"]},
    {"js": ["sub.js"], "matches": ["<all_urls>"], "all_frames":true}
]

main.js:

var isTop = true;
chrome.runtime.onMessage.addListener(function(details) {
    alert('Message from frame: ' + details.data);
});

sub.js:

if (!window.isTop) { // true  or  undefined
    // do something...
    var data = 'test';
    // Send message to top frame, for example:
    chrome.runtime.sendMessage({sendBack:true, data:data});
}

后台脚本 'bg.js':

chrome.runtime.onMessage.addListener(function(message, sender) {
    if (message.sendBack) {
        chrome.tabs.sendMessage(sender.tab.id, message.data);
    }
});

另一种方法是在bg.js中使用chrome.tabs.executeScript来触发主要内容脚本中的函数。

相关文档


3
您的说法“没有办法访问页面的任何窗口对象”似乎并不完全准确。我刚创建了一个基本扩展程序,我能够访问window.frames并且完全DOM访问同源下的frame。也许自从您写答案以来情况已经发生了变化?这似乎仍在开发中且没有得到充分记录。请参见:https://code.google.com/p/chromium/issues/detail?id=20773 - kzahel
1
@kzahel 在撰写本文时,所有 iwindow.frames[i] 均为未定义。这个问题确实已经修复了一段时间。然而,同源策略仍然适用,因此答案仍然有效,至少对于所问的问题(即 OP 遇到跨域框架访问问题)。 - Rob W
@stt 感谢您提醒我答案中的错误。虽然审核人员拒绝了我的修改建议,但我已经对答案进行了编辑。 - Rob W
@RobW 谢谢,我已经搞定了 :) - theonlygusti
@RobW,非常感谢您,您的回答非常有用。 - Kostiantyn Ivashchenko
显示剩余3条评论

18

我知道这是一个老问题,但我最近花了半天时间才解决它。 通常创建一个iframe的代码看起来像这样:

var iframe = document.createElement('iframe');
iframe.src = chrome.extension.getURL('iframe-content-page.html');

该框架的来源与页面不同,您将无法获取其DOM。但是,如果您仅为CSS隔离创建iframe,则可以用另一种方式实现:


该框架与页面的起源不同,无法获得其DOM。但如果您只是为了进行CSS隔离而创建iframe,则有另一种方法可行:
var iframe = document.createElement('iframe');
document.getElementById("iframe-parent").appendChild(iframe);
iframe.contentDocument.write(getFrameHtml('html/iframe-content-page.html'));
.......
function getFrameHtml(htmlFileName) {
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open("GET", chrome.extension.getURL(html/htmlFileName), false);
    xmlhttp.send();

    return xmlhttp.responseText;
}
.......
"web_accessible_resources": [   
    "html/htmlFileName.html",
    "styles/*",
    "fonts/*"
]

之后,您可以使用iframe.contentDocument访问iframe的DOM


我还没有尝试过这个,只是想知道它是否只返回iframe的HTML代码,还是给你一个实际的对该iframe DOM的引用。 - Chris - Jr
它将是对DOM的实际引用 - 您可以获取/设置输入值,隐藏div等。 - Eugene Titarchuk
聪明的方法。刚刚投了赞成票。我还想知道如果我们想要相反地访问,该怎么办。例如:在 iframe 中访问父级(当前选项卡)dom? - wp student
不,这只是因为我创建了一个没有src的iframe,所以它的域名与父页面相同。 - Eugene Titarchuk
@EugeneTitarchuk 谢谢!在将Bootstrap注入到Gmail扩展程序中时,这正是您所需要的。 - dikirill
这是一个非常有帮助的方法。 - tarunsachdeva

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接