目标:我想开发一个Firefox Web扩展(类似于Chrome扩展),它可以在加载HTML和JavaScript文件之前检测它们。如果这些文件中有特定的内容,它们将被阻止,否则它们将被允许通过。
问题:无法收集具有不同域的文件内容,因为它们引发了"跨原点"错误,因为缺少Access Control Allow Origin标头。
我读了很多关于这个问题的文章,文档中说,如果在Webextension清单中设置了权限,那么就不需要Access Control Allow Origin标头。这里引用Mozilla文档:
使用权限键为您的扩展请求特殊权限。[…]密钥可以包含三种权限:[…]主机permissions[…]主机权限被指定为匹配模式,并且每个模式标识扩展所针对的一组URL请求额外特权。额外特权包括:XHR访问到这些来源[…]
My 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"
}
]
}
这里,我在权限键中有"*://*/*">,这意味着每个web资源都应该有权限,而不应该发生跨源错误?或者我错了?有人能告诉我,我为什么会出错,或者我如何避免它吗?
我的后台脚本.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方法来访问网站内容。我还使用dataTypejsonp尝试了$.ajax,但在这种情况下,我得到了一个无限访问链,脚本尝试无限次加载资源。我一点也不明白,为什么会发生这种情况,也许是因为我使用了chrome.webRequest.onBeforeRequest Listener,如果出现新的连接,就会访问它,在这种情况下,它会进入无休止的循环?
在我读到的Mozilla文档中,chrome.webRequest.onBeforeRequest有一个参数,requestBody:
包含HTTP请求正文数据。[…]1。Firefox不支持"requestBody"选项。
- 此解决方案是最好的=>,但不可用
- 我尝试了$.get,权限模式=>我得到了跨原点错误
- 我用jsonp和相同的权限模式尝试了$.ajax=>我得到了无尽的循环
那么问题又来了:在域名打开的情况下,我如何访问不同域的文件内容而不会出现跨源错误(模式如"*://*/*")?
最后,我可以在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。但是要加载内容,总是需要一个异步请求,所以这个问题不能解决吗?如果我找到了解决方案,我会更新这个答案。
我希望这个答案也能帮助其他人。