如何 运行 cross-domain 在 Webextension 中以正确的权限请求 XHR?
How to run cross-domain XHR request with correct permissions in a Webextension?
目标:我想开发一个 Firefox Web 扩展(类似于 Chrome 扩展),它检测 HTML 和 JavaScript 文件,在加载之前。如果那些文件中有特定的内容,它们将被阻止,否则允许它们通过。
问题: 无法收集具有不同域的文件内容,因为它们会抛出 "Cross-Origin" 错误,因为 Access-Control-Allow-Origin header 不见了。
我读了很多关于这个问题的资料,文档说,如果在 Webextension 清单中设置了权限,则不需要 Access-Control-Allow-Origin header。这里引用 Mozilla Doc:
Use the permissions key to request special powers for your extension.
[...] The key can contain three kinds of permissions: [...] host
permissions [...] Host permissions are specified as match patterns,
and each pattern identifies a group of URLs for which the extension is
requesting extra privileges. The extra privileges include: XHR access
to those origins [...]
我的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"
}
]
}
在这里,我在权限键中有 "*://*/*",这意味着每个网络资源都应该有权限和 Cross-Origin 错误不应该发生?还是我错了?任何人都可以告诉我,为什么我会收到错误或如何避免它?
我的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方法访问网站内容。我还尝试了 $.ajax 和数据类型 jsonp,但在这种情况下,我得到了一个无限访问链,并且脚本尝试无限次加载资源。完全不明白,为什么会这样,可能是因为我使用了chrome.webRequest.onBeforeRequest监听器,如果有新的连接就会访问,这样就进入了死循环?
在我读到的 Mozilla Doc 中,chrome.webRequest.onBeforeRequest 有一个参数,requestBody:
Contains the HTTP request body data. [...] 1. Firefox does not support the "requestBody" option.
- 这个解决方案是最好的 => 但它不可用
- 我尝试使用权限模式 $.get => 我得到 Cross-Origin 错误
- 我用 jsonp 和相同的权限模式尝试了 $.ajax => 我得到了无限循环
所以问题又来了:如何在没有Cross-Origin错误的情况下访问不同域的文件内容,其中域名是开放的(模式如"*://*/*")?
最后我可以在 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。这是后台脚本消息监听器的通知,等待响应。如果缺少此项,带有网站内容的响应消息将永远不会在后台脚本中传递。
最后,这导致了一个 "general" 问题,它不是问题的直接部分。 backgroundscript.js 中的 chrome.webRequest.onBeforeRequest 需要对 "cancel" return 值 (true/false) 进行同步处理,以决定阻止加载的 url。但是要加载内容,总是需要一个异步请求,所以这个问题不是可以解决的吗?如果我找到解决方案,我将更新此答案。
希望这个回答对其他人也有帮助。
目标:我想开发一个 Firefox Web 扩展(类似于 Chrome 扩展),它检测 HTML 和 JavaScript 文件,在加载之前。如果那些文件中有特定的内容,它们将被阻止,否则允许它们通过。
问题: 无法收集具有不同域的文件内容,因为它们会抛出 "Cross-Origin" 错误,因为 Access-Control-Allow-Origin header 不见了。
我读了很多关于这个问题的资料,文档说,如果在 Webextension 清单中设置了权限,则不需要 Access-Control-Allow-Origin header。这里引用 Mozilla Doc:
Use the permissions key to request special powers for your extension. [...] The key can contain three kinds of permissions: [...] host permissions [...] Host permissions are specified as match patterns, and each pattern identifies a group of URLs for which the extension is requesting extra privileges. The extra privileges include: XHR access to those origins [...]
我的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"
}
]
}
在这里,我在权限键中有 "*://*/*",这意味着每个网络资源都应该有权限和 Cross-Origin 错误不应该发生?还是我错了?任何人都可以告诉我,为什么我会收到错误或如何避免它?
我的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方法访问网站内容。我还尝试了 $.ajax 和数据类型 jsonp,但在这种情况下,我得到了一个无限访问链,并且脚本尝试无限次加载资源。完全不明白,为什么会这样,可能是因为我使用了chrome.webRequest.onBeforeRequest监听器,如果有新的连接就会访问,这样就进入了死循环?
在我读到的 Mozilla Doc 中,chrome.webRequest.onBeforeRequest 有一个参数,requestBody:
Contains the HTTP request body data. [...] 1. Firefox does not support the "requestBody" option.
- 这个解决方案是最好的 => 但它不可用
- 我尝试使用权限模式 $.get => 我得到 Cross-Origin 错误
- 我用 jsonp 和相同的权限模式尝试了 $.ajax => 我得到了无限循环
所以问题又来了:如何在没有Cross-Origin错误的情况下访问不同域的文件内容,其中域名是开放的(模式如"*://*/*")?
最后我可以在 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。这是后台脚本消息监听器的通知,等待响应。如果缺少此项,带有网站内容的响应消息将永远不会在后台脚本中传递。
最后,这导致了一个 "general" 问题,它不是问题的直接部分。 backgroundscript.js 中的 chrome.webRequest.onBeforeRequest 需要对 "cancel" return 值 (true/false) 进行同步处理,以决定阻止加载的 url。但是要加载内容,总是需要一个异步请求,所以这个问题不是可以解决的吗?如果我找到解决方案,我将更新此答案。
希望这个回答对其他人也有帮助。