3

目标:我想开发一个 Firefox Webextension(类似于 Chrome 扩展),在加载之前检测 HTML 和 JavaScript 文件。如果这些文件中有特定内容,将被阻止,否则允许通过。

问题:无法收集具有不同域的文件内容,因为它们抛出“跨域”错误,因为缺少 Access-Control-Allow-Origin 标头。

我读了很多关于这个问题的东西,文档说,如果在 Webextension 清单中设置了权限,则不需要 Access-Control-Allow-Origin 标头。这里引用Mozilla Doc

使用权限键为您的扩展请求特殊权限。[...] 密钥可以包含三种权限: [...] 主机权限 [...] 主机权限被指定为匹配模式,每个模式标识一组 URL,扩展程序正在为其请求额外权限. 额外的特权包括:XHR 访问 这些来源[...]

我的清单.json:

{
  [...],    
  "permissions": [
    "tabs",
    "*://*/*",
    "webRequest",
    "webRequestBlocking",
    "<all_urls>"
  ],    
  "background": {
    "scripts": ["backgroundscript.js"]
  },    
  "content_scripts": [
    {
      "matches": ["*://*/*"],
      "js": ["/lib/jquery-2.2.4.min.js", "/contentscript.js"],
      "run_at": "document_start"
    }
  ]
}

在这里,我在权限键中有“*://*/*”,这意味着每个网络资源都应该有权限并且不应该发生跨域错误?还是我错了?谁能告诉我,为什么我会收到错误或如何避免它?

我的背景脚本.js:

chrome.webRequest.onBeforeRequest.addListener(
    logURL,
    {urls: ["<all_urls>"], types: ["main_frame", "script"]},
    ["blocking"]
);

function logURL(requestDetails) {
    chrome.tabs.sendMessage(
        requestDetails.tabId,
        {action: "getContentByURL", url: requestDetails.url, type: requestDetails.type},
        function(response) {
            console.log(response);
        }
    );
    if(requestDetails.type == 'script') {
        // here will be the conditions, based on the content of the files,
        // if they will be canceled or allowed to pass
        // actually, there is just a dummy "false"
        return {cancel: false};
    }
}

我的 contentscript.js:

chrome.runtime.onMessage.addListener(
    function(message, sender, sendResponse) {
        var contentAll = [];
        if(message.action == 'getContentByURL') {
            var pageContent = getContentByURL(message.url);
            contentAll.push(pageContent);
            sendResponse({"content" : contentAll});
        }
    }
);
function getContentByURL(url) {
    $(document).ready(function() {
        $.get(url, function(data) {
            console.log(data);
        });
    });
}

在 contentscript.js 中,我使用 jQuery $.get方法来访问网站内容。我还尝试了 $.ajax和 dataType jsonp,但在这种情况下,我得到了一个无限的访问链,并且脚本尝试无限次加载资源。我完全不明白,为什么会这样,可能是因为我使用了 chrome.webRequest.onBeforeRequest 监听器,如果出现新的连接就会被访问,在这种情况下它会陷入死循环?

在我阅读的Mozilla Doc中,chrome.webRequest.onBeforeRequest 有一个参数 requestBody:

包含 HTTP 请求正文数据。[...] 1. Firefox 不支持“requestBody”选项。

  1. 这个解决方案将是最好的 => 但它不可用
  2. 我用权限模式尝试了 $.get => 我得到了 Cross-Origin 错误
  3. 我用 jsonp 和相同的权限模式尝试了 $.ajax => 我得到了无限循环

所以问题又来了:我如何访问不同域的文件内容而不会出现跨域错误,其中域名是打开的(模式如“*://*/*”)?

4

1 回答 1

4

最后,我可以在 contentscript.js 中使用以下代码解决我的问题:

chrome.runtime.onMessage.addListener(
    function(message, sender, sendResponse) {
        if(message.action == 'getContentByURL') {
            getContentByURL(message.url, function(result) {
                sendResponse({"content" : result});
            });
        } else {
            sendResponse('error');
        }
        return true;
    }
);

function getContentByURL(url, callback) {
    var req = new XMLHttpRequest();
    if(req) {
        req.open('GET', url, true);
        req.onreadystatechange =  function() {
            if (req.readyState == 4) {
                callback(req.responseText);
            }
        };
        req.send();
    }
}

一项重要的更改是使用 XMLHttpRequest() 对象而不是 jQuery 方法。在我的情况下,这是我提出的问题的解决方案。我之前试过这个,但错过了 req.onreadystatechange 的检查,所以我做错了。我也用 req.onload 尝试过,这也适用于我!

为了让示例运行,还有两个重要的点。

首先,我必须通过回调将内容(req.responseText)返回,这样我就可以将响应从内容脚本发送回后台脚本。

其次,因为响应消息是异步的,所以我必须将 return 设置为 true。这是后台脚本消息侦听器的通知,以等待响应。如果缺少此内容,则带有网站内容的响应消息将永远不会在后台脚本中传递。

最后,这导致了一个“一般”问题,这不是问题的直接部分。backgroundscript.js 中的 chrome.webRequest.onBeforeRequest 需要对“取消”返回值(true/false)进行同步处理,以便做出阻止加载的 url 的决策。但是要加载内容,总是需要异步请求,所以这个问题是不是可以修复?如果我找到解决方案,我会更新这个答案。

我希望这个答案也能帮助其他人。

于 2016-05-27T23:24:57.143 回答