在折腾小工具的时候有产生了奇怪的需求,Chrome 插件需要监听任意网页中某个变量的变化,或是访问其中的值。但默认注入的 content.js 和原网页 Javascript 脚本并不运行在一个相同的环境中,无法相互访问。
搜索和自己尝试了如下的解决方法,现罗列如下。
访问到目标变量
这个很简单。虽然默认注入的content.js不能访问到原网页的变量,但可以访问和修改Dom,所以再注入一个脚本就好。
1 | function loadScript(url) { |
在inject.js中便是和原网页一样的javascript环境了,但是问题来了,怎样才能将在此环境下的变量发送到后台插件中呢?
建立原网页作用域与插件后台页面的双工通信
目标很明确,在inject.js中建立与后台background.js的双工通信,这样我们可以实时将网页中值的变化发送到插件中进行分析……
不过阻碍也很明显,由于chrome的安全策略,inject.js和后台插件虽然能直接连通,但受域名限制,而content.js则可以很轻易连通。所以如果打通inject.js与content.js就好了。
外部服务器中转
inject.js和background.js连上相同的websocket服务器中转,问题解决。不过还要多加一个外部服务器。
inject.js直连后台插件
的确,Chrome提供了直连的方法。
首先在manifest.json
中申明externally_connectable
,需要申明域名限制。正当我兴高采烈地输入*://*/*
全匹配后,发现出错,chrome文档如是说:
This will expose the messaging API to any page which matches the URL patterns you specify. The URL pattern must contain at least a second-level domain - that is, hostname patterns like ““, “.com”, “.co.uk”, and “.appspot.com” are prohibited.
没有办法做到让所有域名发起的连接都与manifest.json
匹配,但对于认可的域名,只需要在inject.js中调用如下API,
1 | var ExtensionID = 'nnhoaecbdmfokhcnldeiadllnjeebhcb'; |
inject.js注入Dom事件
这是网上一个比较通行的办法,可以约定一个Dom元素,再使用MutationObserver
监控这个Dom元素即可。
不过修改Dom比较重量级,不是很喜欢这种做法。
轮询localStorage等公共可访问变量
两个脚本均可访问到localStorage,location等变量,所以一方修改一方轮询也是一种办法,不过如果变量发生了多次修改,每次的修改事件就很难及时发出。
SharedWorker转发
最终试验成功了一种还比较满意的方法,可由 content.js 创建一个 SharedWorker的函数,并将其转换为 Blob ,再使用URL.createObjectURL
创建为共享链接存入localStorage , inject.js 读取后此链接后,两个脚本同时连上同一个 SharedWorker,由 worker 转发消息,可以很好双向通信。
1 | // background.js |
使用window.postMessage
经网友提醒,参考Chrome开发文档中针对content script通信的说明。使用window.postMessage可以很好进行双向通信。