我是新手,以浏览器扩展的发展,我知道浏览器扩展的概念改变了页面和代码注入到它。
有没有办法这个方向可以转身? 我写的提供了一组想用我的分机可以检测其存在的API,和网站的延伸,如果它存在,网站可以打电话给我的API的方法,如var extension = Extenion(foo, bar)
。 这是可能的Chrome,Firefox和Safari?
例:
谷歌创建了一个新的扩展名为BeautifierExtension。 它拥有一套API,作为JS对象。
用户进入reddit.com。 Reddit.com检测BeautifierExtension并通过调用调用API beautifer = Beautifier();
见#2 - 通常是检测匹配的网站和改变页面的扩展。 我很有兴趣知道的是#2是否是可能的。
由于Chrome推出externally_connectable
,这是很容易在Chrome做。 首先,请指定允许的域manifest.json
文件:
"externally_connectable": {
"matches": ["*://*.example.com/*"]
}
使用chrome.runtime.sendMessage
从页面发送一条消息:
chrome.runtime.sendMessage(editorExtensionId, {openUrlInEditor: url},
function(response) {
// ...
});
最后,在听取你的背景页chrome.runtime.onMessageExternal
:
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
// verify `sender.url`, read `request` object, reply with `sednResponse(...)`...
});
如果您没有访问externally_connectable
支持,原来的答复如下:
我会在Chrome为中心的层面来回答,虽然这里描述的(,长时间运行的后台脚本,消息传递网页脚本注射)的原则适用于几乎所有的浏览器扩展框架。
从一个高的水平,你想要做的是注入的内容脚本到每一个网页,它增加了一个API,以网页访问。 当网站调用API,该API触发内容脚本做一些事情,比如将消息发送到后台页面和/或结果发送回内容脚本,通过异步回调。
这里的主要困难是,这是“注入”到一个网页内容的脚本不能直接更改JavaScript 执行环境页面。 它们共享DOM,所以事件和变化,以DOM结构内容脚本和网页之间共享,但是函数和变量不共享。 例子:
DOM操作:如果内容脚本将<div>
元素的网页,如预期,将工作。 无论内容脚本和页面将看到新<div>
事件:如果一个内容脚本设置了一个事件侦听器,例如,对于点击的元素,当事件发生时,监听器会成功点火。 如果页面设置了从内容脚本发射自定义事件侦听器,他们将成功地在内容脚本触发这些事件的接收。
功能:如果内容脚本定义一个新的全局函数foo()
正如你可能建立一个新的API时尝试)。 该页无法看到或执行foo
,因为foo
只存在于内容脚本的执行环境,而不是在页面的环境。
那么,你怎么能建立一个适当的API? 答案来自于许多步骤:
在一个低的水平,使你的API 基于事件 。 该网页火灾的自定义DOM事件与dispatchEvent
和内容脚本监听他们addEventListener
,采取行动在收到时。 下面是其中一个网页,可以使用的扩展存储数据以它简单的基于事件的存储API:
content_script.js(在扩展):
// an object used to store things passed in from the API internalStorage = {}; // listen for myStoreEvent fired from the page with key/value pair data document.addEventListener('myStoreEvent', function(event) { var dataFromPage = event.detail; internalStorage[dataFromPage.key] = dataFromPage.value });
非扩展的网页,使用基于事件的API:
function sendDataToExtension(key, value) { var dataObj = {"key":key, "value":value}; var storeEvent = new CustomEvent('myStoreEvent', {"detail":dataObj}); document.dispatchEvent(storeEvent); } sendDataToExtension("hello", "world");
正如你所看到的,普通的网页射击的内容脚本可以看到并作出反应,因为它们共享DOM事件。 该事件有附加的数据,在添加CustomEvent
构造 。 我在这里的例子是简单得可怜-可以很明显的做你的内容脚本更一旦它从页面的数据(最有可能将它传递给后台页面进行进一步的处理)。
然而,这只是成功的一半。 在我上面的例子中,普通网页必须创造sendDataToExtension
本身。 创建和射击自定义事件相当冗长(我的代码占用3线和相对简单)。 你不想强迫一个网站写的神秘事件触发代码只是为了用你的API。 该解决方案是一个有点讨厌黑客:追加<script>
标签来共享DOM它增加了事件发射代码的主网页的执行环境。
内部content_script.js:
// inject a script from the extension's files // into the execution environment of the main page var s = document.createElement('script'); s.src = chrome.extension.getURL("myapi.js"); document.documentElement.appendChild(s);
中定义的所有功能myapi.js
将成为主要的页面访问。 (如果您正在使用"manifest_version":2
,你需要包括myapi.js
在你的清单的列表web_accessible_resources
)。
myapi.js:
function sendDataToExtension(key, value) { var dataObj = {"key":key, "value":value}; var storeEvent = new CustomEvent('myStoreEvent', {"detail":dataObj}); document.dispatchEvent(storeEvent); }
现在普通的网页可以简单地这样做:
sendDataToExtension("hello", "world");
有一个进一步的皱纹对我们的API过程: myapi.js
脚本将不提供确切的加载时间。 相反,它会被载入页面加载时间后一段时间。 因此,普通网页需要知道什么时候可以安全地调用您的API。 你可以有解决这个myapi.js
触发一个“API准备就绪”事件,你的页面侦听。
myapi.js:
function sendDataToExtension(key, value) { // as above } // since this script is running, myapi.js has loaded, so let the page know var customAPILoaded = new CustomEvent('customAPILoaded'); document.dispatchEvent(customAPILoaded);
使用API 普通网页 :
document.addEventListener('customAPILoaded', function() { sendDataToExtension("hello", "world"); // all API interaction goes in here, now that the API is loaded... });
对脚本可用在加载时的问题的另一个解决方案是设置run_at
在舱单内容脚本的财产"document_start"
是这样的:
manifest.json的:
"content_scripts": [ { "matches": ["https://example.com/*"], "js": [ "myapi.js" ], "run_at": "document_start" } ],
摘自文档 :
在“document_start”的情况下,将文件从CSS的任何文件后注射,但建造任何其他DOM之前或任何其他脚本运行。
对于一些contentscripts可能比拥有“API加载”事件更合适,更省力的。
为了结果发送回页面,您需要提供异步回调函数。 有没有办法同步从API返回的结果,因为事件激发/收听本质上是异步的(即网站端API函数终止内容脚本曾经与API请求获取事件之前)。
myapi.js:
function getDataFromExtension(key, callback) { var reqId = Math.random().toString(); // unique ID for this request var dataObj = {"key":key, "reqId":reqId}; var fetchEvent = new CustomEvent('myFetchEvent', {"detail":dataObj}); document.dispatchEvent(fetchEvent); // get ready for a reply from the content script document.addEventListener('fetchResponse', function respListener(event) { var data = event.detail; // check if this response is for this request if(data.reqId == reqId) { callback(data.value); document.removeEventListener('fetchResponse', respListener); } } }
content_script.js(在扩展):
// listen for myFetchEvent fired from the page with key // then fire a fetchResponse event with the reply document.addEventListener('myStoreEvent', function(event) { var dataFromPage = event.detail; var responseData = {"value":internalStorage[dataFromPage.key], "reqId":data.reqId}; var fetchResponse = new CustomEvent('fetchResponse', {"detail":responseData}); document.dispatchEvent(fetchResponse); });
普通网页:
document.addEventListener('customAPILoaded', function() { getDataFromExtension("hello", function(val) { alert("extension says " + val); }); });
该reqId
是在必要的情况下你有多个请求了一次,所以它们不会读错的响应。
我认为这就是一切! 因此,不适合胆小的心脏,可能不值得的,当你考虑到其他的扩展也可以绑定监听到你的活动窃听页面如何使用您的API。 我只知道,这一切都是因为我做了做验证的概念加密API为学校项目(以及随后得知与它相关的重大安全隐患)。
总之:内容脚本可以监听从一个普通的网页自定义事件,脚本也可以注入与功能的脚本文件,可以更容易的网页火灾的事件。 内容脚本可以传递消息到一个背景页,其然后存储,变换,或从消息发送数据。