在扩展程序页

时间:2017-06-08 01:09:43

标签: google-chrome google-chrome-extension

摘要:我需要找到一种方法来完成程序化注入与使用content_scripts>完全相同的行为。清单上matches "all_frames": true iframe。为什么?因为这是我发现在扩展页面中注入optional_permissions内容而没有跨源错误的唯一方法。

我正在使用Chrome扩展程序转到optional_permissions,而我却走到了尽头。

我想要的是什么:

将此行为移至content_scripts,以便将来能够添加更多主机。使用当前代码,在matches>上添加一个新主机content_scripts Chrome已禁用该扩展程序。

对于移动,我删除了清单中的"optional_permissions": ["*://*/"],,并添加了chrome.permissions.request。然后,我成功实现了一个对话框,询问用户iframe的新权限。

正如我之前所说,问题是如何在扩展页面中注入allFrames: true的内容。

我尝试了什么:

  1. chrome.declarativeContent.RequestContentScript(提及here)与iframe。如果我直接输入网址,我只能看到脚本正在运行,当url中设置该网址时没有任何反应。
  2. chrome.tabs.onUpdatedundefinediframe的扩展程序页面。此外,未检测到allFrames: true网址。
  3. 加载第一个iframe后,立即使用Cannot access contents of the page. Extension manifest must request permission to access the respective host.致电chrome.tabs.executeScript。通过这样做,我得到一个例外chrome-extension://,“相应的主机”是content_scripts,如果你想将它添加到权限中,它就不是有效的主机。
  4. 我迷路了。我找不到一种方法来模拟与matches>相同的行为。 webNavigationprogrammatic injection

    注意:使用frameId API不是一个选项,因为扩展程序是实时的,并且它有数千个用户。因此,我无法将executeScript属性用于executeScript。因此,chrome-extension的唯一选择是注入所有帧,但PhoneAuthProvider.getInstance().verifyPhoneNumber( phoneNum, 60, TimeUnit.SECONDS, this, mCallbacks 主机问题不允许我继续。

    更新:我能够完成我想要的但只能在HTTP主机上完成。我使用chrome.tabs.executeScript(选项3)。

    问题仍然是如何在扩展页面上进行此项工作。

1 个答案:

答案 0 :(得分:2)

您无法在任何扩展程序页面中运行内容脚本,包括您自己的。

如果您想在扩展程序页面的子框架中运行代码,则必须使用frameId。无论有没有webNavigation,有两种方法可以做到这一点。

我已将所有代码片段放在此答案中(使用一些按钮调用各个代码段)并在https://robwu.nl/s/optional_permissions-script-subframe.zip分享它 要试用它,请下载并解压缩zip文件,在chrome:// extensions处加载扩展程序,然后单击扩展按钮以打开测试页。

请求可选权限

由于目标是以optional permissions编程运行脚本,因此您需要请求权限。我的例子将使用example.com。 如果您也想使用webNavigation API,也请在权限请求中包含其权限。

chrome.permissions.request({
    // permissions: ['webNavigation'], // uncomment if you want this.
    origins: ['*://*.example.com/*'],
}, function(granted) {
    alert('Permission was ' + (granted ? '' : 'not ') + 'granted!');
});

在子帧中注入脚本

一旦有了标签ID和frameId,就可以轻松地在特定框架中注入脚本。由于tabId要求,此方法只适用于选项卡中的帧,而不适用于browserAction / pageAction弹出窗口或后台页面中的帧!

为了证明代码执行成功,下面的示例将在tabId和frameId已知后调用下一个injectInFrame函数。

function injectInFrame(tabId, frameId) {
    chrome.tabs.executeScript(tabId, {
        frameId,
        code: 'document.body.textContent = "The document content replaced with content at " + new Date().toLocaleString();',
    });
}

如果您不仅要在特定框架中运行代码,而是要在该框架的所有子框架中运行代码,只需将allFrames: true添加到chrome.tabs.executeScript来电。

选项1:使用webNavigation查找frameId

如果您想知道其他脚本中的当前tabId(例如后台脚本),请使用chrome.tabs.getCurrent查找脚本运行的选项卡的ID(或chrome.tabs.query{active:true,currentWindow:true}

之后,使用chrome.webNavigation.getAllFrames查询选项卡中的所有帧。识别框架的主要方法是通过页面的URL,因此如果框架页面重定向到其他位置,或者如果有多个框架具有相同的URL,则会出现问题。这是一个例子:

// Assuming that you already have a frame in your document,
// i.e. <iframe src="https://example.com"></iframe>
chrome.tabs.getCurrent(function(tab) {
    chrome.webNavigation.getAllFrames({
        tabId: tab.id,
    }, function(frames) {
        for (var frame of frames) {
            if (frame.url === 'https://example.com/') {
                injectInFrame(tab.id, frame.frameId);
                break;
            }
        }
    });
});

选项2:在框架中使用帮助页面来查找frameId

webNavigation选项看起来很简单但有两个主要缺点:

  • 它需要webNavigation权限(导致“阅读您的浏览历史记录”权限警告)
  • 如果有多个具有相同URL的帧,则帧的标识可能会失败。

另一种方法是首先打开一个发送扩展消息的扩展页面,然后在chrome.runtime.onMessage监听器的第二个参数中找到的元数据中找到frameId(和选项卡ID) 。此代码比其他选项更复杂,但它更可靠,不需要任何其他权限。

framehelper.html

<script src="framehelper.js"></script>

framehelper.js

var parentOrigin = location.ancestorOrigins[location.ancestorOrigins.length - 1];
if (parentOrigin === location.origin) {
    // Only send a message if the frame was opened by ourselves.
    chrome.runtime.sendMessage(location.hash.slice(1));
}

要在您的扩展程序页面中运行的代码:

chrome.runtime.onMessage.addListener(frameMessageListener);
var randomMessage = 'Random message: ' + Math.random();

var f = document.createElement('iframe');
f.src = chrome.runtime.getURL('framehelper.html') + '#' + randomMessage;
document.body.appendChild(f);

function frameMessageListener(msg, sender) {
    if (msg !== randomMessage) return;
    var tabId = sender.tab.id;
    var frameId = sender.frameId;

    chrome.runtime.onMessage.removeListener(frameMessageListener);
    // Note: This will cause the script to be run on the first load.
    // If the frame redirects elsewhere, then the injection can seemingly fail.
    f.addEventListener('load', function onload() {
        f.removeEventListener('load', onload);
        injectInFrame(tabId, frameId);
    });
    f.src = 'https://example.com';
}