我有一个 chrome 扩展程序,它以特殊方式监视浏览器,将一些数据发送到网络服务器。在当前配置中,这是本地主机。因此内容脚本包含这样的代码:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(data)...
xhr.open('GET', url, true);
xhr.send();
其中 url 参数是 'http://localhost/ctrl?params' (或 http://127.0.0.1/ctrl?params - 没关系)。
清单文件包含跨站点请求的所有必要权限。
该扩展在大多数站点上工作正常,但在一个站点上我收到错误:
XMLHttpRequest 无法加载 http://localhost/ctrl?params。 Access-Control-Allow-Origin 不允许来源 http://www.thissite.com。
我已经尝试了此处建议的几种权限(*://*/*
、http://*/*
和
),但没有人帮助解决该问题。
那么,问题是这个特定网站可能出了什么问题(显然可能还有其他网站有类似的不当行为,我想知道其本质),以及如何修复该错误?
I have a chrome extension which monitors the browser in a special way, sending some data to a web-server. In the current configuration this is the localhost. So the content script contains a code like this:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(data)...
xhr.open('GET', url, true);
xhr.send();
where url parameter is 'http://localhost/ctrl?params' (or http://127.0.0.1/ctrl?params - it doesn't matter).
Manifest-file contains all necessary permissions for cross-site requests.
The extension works fine on most sites, but on one site I get the error:
XMLHttpRequest cannot load http://localhost/ctrl?params. Origin http://www.thissite.com is not allowed by Access-Control-Allow-Origin.
I've tried several permissions which are proposed here (*://*/*
, http://*/*
, and <all_urls>
), but no one helped to solve the problem.
So, the question is what can be wrong with this specific site (apparently there may be another sites with similar misbehaviour, and I'd like to know the nature of this), and how to fix the error?
发布评论
评论(2)
(tl;dr:在答案末尾看到两种可能的解决方法)
这是发生的一系列事件,这会导致您看到的行为:
标签:
<前>(函数() {
var e = document.createElement('script');
e.type = '文本/javascript';
e.src = 文档.位置.协议 +
'//connect.facebook.net/en_US/all.js';
e.async = true;
document.getElementById('fb-root').appendChild(e);
}());
前>
DOMContentLoaded
事件就会触发。由于您的内容脚本使用"run_at" : "document_end"
,因此此时它会被注入并运行。load
事件触发后完成大部分工作):load
事件处理程序,并添加了以下代码片段:<前>(函数() {
var oldonload=window.onload;
窗口.onload=函数(){
// 运行新的加载代码
如果(旧加载){
if(typeof oldonload=='字符串') {
评估(旧加载);
} 别的 {
旧加载();
}
}
};
})();
(这是第一个关键部分)由于您的脚本设置了
onload
属性,因此oldonload
是您脚本的加载处理程序。load
事件处理程序被触发。load
处理程序运行,它运行自己的代码,然后调用oldonload
。 (这是第二个关键部分)由于页面正在调用您的load
处理程序,因此它不会在脚本的 孤立的世界,但位于页面的“主世界”。只有脚本的隔离世界具有跨域XMLHttpRequest
访问,因此请求失败。要查看此简化测试用例,请参阅此页面 (模仿 http://www.wix.com),加载 此脚本(模仿 Facebook Connect)。我还提供了 内容脚本 的简化版本和扩展清单。
您的
load
处理程序最终在“主世界”中运行这一事实很可能是 Chrome 错误 87520(该错误具有安全隐患,因此您可能看不到它)。有两种方法可以解决此问题:
document_idle
,文档加载后),然后让您的代码内联运行。window.onload
属性来添加load
事件处理程序,而是使用window.addEventListener('load', func)
。这样,您的事件处理程序对于 Facebook Connect 将不可见,因此它将在内容脚本的隔离世界中运行。(tl;dr: see two possible workarounds at the end of the answer)
This is the series of events that happens, which leads to the behavior that you see:
<script>
tag that asynchronously loads the Facebook Connect script:DOMContentLoaded
event fires. Since your content script uses"run_at" : "document_end"
, it gets injected and run at this time.load
event fires):load
event handler, which it adds with this snippet:(this is the first key part) Since your script set the
onload
property,oldonload
is your script's load handler.load
event handler fires.load
handler is run, which run its own code, and then invokesoldonload
. (this is the second key part) Since the page is invoking yourload
handler, it's not running it in your script's isolated world, but in the page's "main world". Only the script's isolated world has cross-originXMLHttpRequest
access, so the request fails.To see a simplified test case of this, see this page (which mimics http://www.wix.com), which loads this script (which mimics Facebook Connect). I've also put up simplified versions of the content script and extension manifest.
The fact that your
load
handler ends up running in the "main world" is most likely a manifestation of Chrome bug 87520 (the bug has security implications, so you might not be able to see it).There are two ways to work around this:
"run_at" : "document_end"
and aload
event handler, you can use the default running time (document_idle
, after the document loads) and just have your code run inline.load
event handler by setting thewindow.onload
property, usewindow.addEventListener('load', func)
. That way your event handler will not be visible to the Facebook Connect, so it'll get run in the content script's isolated world.您看到的访问控制源问题可能体现在响应的标头中(超出您的控制范围),而不是请求的标头(在您的控制范围内)。
Access-Control-Allow-Origin 是 CORS 的策略,在标头中设置。例如,使用 PHP,您可以使用如下所示的一组标头来启用 CORS:
如果听起来像这样,如果服务器在此标头中设置特定来源,则您的 Chrome 扩展程序 遵循指令仅允许来自该域的跨域 (POST?) 请求。
The access control origin issue you're seeing is likely manifest in the headers for the response (out of your control), rather than the request (under your control).
Access-Control-Allow-Origin is a policy for CORS, set in the header. Using PHP, for example, you use a set of headers like the following to enable CORS:
If sounds like that if the server is setting a specific origin in this header, then your Chrome extension is following the directive to allow cross-domain (POST?) requests from only that domain.