为什么 Firefox 不允许您在主框架中执行脚本?

Why doesn't Firefox allow you to execute a script in the main frame?

我正在使用基于 Chrome 的 Firefox 新的 webExtensions 框架编写一个 webextension。所有示例扩展都在 Nightly 构建中工作,所以这就是我正在测试的地方。我想要做的是 运行 内容页面加载时的脚本。这是我的 background.js 页面:

background.js

"use strict";

function onCompletedFunc(details) {
    var script = 'console.log("ok");';
    console.log("Details are %o", details);
    chrome.tabs.executeScript(details['tabId'], {
        code: script,
        runAt: 'document_end'
    });
};
chrome.webRequest.onCompleted.addListener(onCompletedFunc, 
                                     {'urls': ['<all_urls>']}, 
                                     ['responseHeaders']);
//Does not work:
/*
chrome.webRequest.onCompleted.addListener(onCompletedFunc, 
                                     {'urls': ['<all_urls>'], 'types':["main_frame"]}, 
                                     ['responseHeaders']);
*/

这适用于 Nightly。我从后台页面获得详细信息列表 ("Details are ..."),在内容页面的控制台上获得几行 "ok",它加载的每个资源对应一行。它 运行 在 Chrome 中相同(清单文件略有不同)。

我想要的是第二个变体,它在页面上只出现一次运行。这在 Chrome 中工作正常。在 Nightly 中,它显示来自主框架的一条 "Details" 消息,但内容页面的控制台中没有任何显示。

我确定这是时间问题,但这里有什么问题?


manifest.json(删除 "applications":{...} 以便与 Chrome 一起使用)

{

  "description": "",
  "manifest_version": 2,
  "name": "execute_script",
  "version": "1.0",

  "applications": {
    "gecko": {
      "id": "user-agent-rewriter@mozilla.org",
      "strict_min_version": "45.0"
    }
  },

  "permissions": [
    "webRequest", "webRequestBlocking", "<all_urls>" 
  ],

  "background": {
    "scripts": ["background.js"]
  }

}

您的第一个代码段(没有 types)之所以有效,是因为您不仅收到了对 main_frame 的请求,而且还收到了另一个子资源。如果您想注入脚本,webRequest.onCompleted 不是正确的事件。无法保证选项卡中的页面与您收到请求的页面相匹配。

如果要无条件运行编码,只需在清单文件中声明内容脚本即可。有关详细信息,请参阅 content script documentation.

如果编程注入是必须的,请使用 chrome.webNavigation.onCommitted 事件而不是 webRequest 事件。通过此事件,您知道选项卡现在显示对给定 URL 的响应。例如:

chrome.webNavigation.onCommitted.addListener(function(details) {
    // Example: Only run in main frame and URLs containing "example"
    if (details.frameId === 0 && details.url.includes('example')) {
        chrome.tabs.executeScript(details.tabId, {
            code: 'console.log("Injected at " + document.URL);',
            runAt: 'document_end'
        });
    }
});
// filter (the second parameter, not used above) is supported as of Firefox 50.

如果您使用 webNavigation.onCommitted 事件,您 必须 考虑到 API 是非阻塞的。有可能(但概率很低)发生以下情况:

  1. 用户开始请求。
  2. webNavigation.onCompleted 触发。
  3. 您调用 tabs.executeScript.
  4. 与此同时该选项卡导航到其他网站。
  5. tabs.executeScript 请求到达选项卡,同时切换到另一个 URL。
  6. 糟糕,您刚刚在页面中注入了一个与您预期不同的脚本。

如果 URL 不匹配是不可取的,那么在继续内容脚本逻辑之前检查 document.URL or location 是否具有预期值。