如何在Webextension中使用正确的权限运行跨域XHR请求?

时间:2016-05-26 18:22:01

标签: javascript google-chrome-extension xmlhttprequest cross-domain firefox-webextensions

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

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

我阅读了很多有关此问题的内容,并且文档说,如果在Webextension清单中设置了权限,则不需要Access-Control-Allow-Origin标头。这里引用了Mozilla Doc

  

使用权限密钥为您的分机申请特殊权限。   [...]密钥可以包含三种权限:[...]主机   权限[...]主机权限被指定为匹配模式,   每个模式标识一组扩展名为的URL   请求额外的特权。额外的权限包括: XHR访问   到那些起源 [...]

我的manifest.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"
    }
  ]
}

在这里,我在权限密钥中有&#34; *:// * / *&#34; ,这意味着,每个网络资源都应具有权限,并且跨域错误应该是不会发生?或者我错了?任何人都可以告诉我,为什么我会得到错误或我如何避免它?

我的backgroundscript.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 方法来访问网站内容。我还尝试使用dataType jsonp $。ajax ,但在这种情况下,我获得了一个无限访问链,并且脚本尝试无限次加载资源。我根本不理解,为什么会发生这种情况,也许是因为我使用chrome.webRequest.onBeforeRequest监听器,如果发生新的连接,将会访问该监听器,在这种情况下它会进入无限循环?

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

  

包含HTTP请求正文数据。 [...] 1. Firefox不支持&#34; requestBody&#34;选项。

  1. 此解决方案将是最好的=&gt;但它不可用
  2. 我尝试了$ .get with permission pattern =&gt;我得到Cross-Origin错误
  3. 我用jsonp和同样的权限模式=&gt;尝试了$ .ajax我得到无限循环
  4. 所以问题再次出现:如何在没有跨域错误的情况下访问不同域的文件内容,其中域名是打开的(模式如&#34; *:/ / * / *&#34;?)

1 个答案:

答案 0 :(得分:2)

最后,我可以使用以下代码修复我的问题:contentcript.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 on true。这是后台脚本消息监听器的通知,等待响应。如果缺少此内容,则带有网站内容的响应消息将永远不会在后台脚本中提供。

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

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