本文介绍Chrome浏览器扩展的消息传递机制。
由于content scripts运行在Web页面的上下文中,属于Web页面的组成部分,而不是Google Chrome扩展程序。但是content scripts又往往需要与Google Chrome扩展程序的其他部分通信以共享数据。
这可以通过消息传递实现,通过彼此互相的消息的监听与反馈进行通信。一个消息可以包含任何有效的JSON对象,如null,boolean,number,string,array,object。
1) 一次性请求与响应模式
对于一次性请求与响应模式,chrome.runtime.sendMessage(obj, function(response){})是从content scripts发生请求消息给Google Chrome扩展程序页面。
从Google Chrome扩展程序页面发送请求消息给content scripts的时候,需要给出当前tab的ID。
1 chrome.tabs.query(
2 {active: true, currentWindow: true},
3 function(tabs) {
4 chrome.tabs.sendMessage(
5 tabs[0].id,
6 {greeting: "hello"},
7 function(response) {
8 console.log(response.farewell);
9 });
10 });
监听消息时,需要注册要监听的消息。
1 chrome.runtime.onMessage.addListener(
2 function(request, sender, sendResponse) {
3 console.log(sender.tab ?
4 "from a content script:" + sender.tab.url :
5 "from the extension");
6 if (request.greeting == "hello")//判断是否为要处理的消息
7 sendResponse({farewell: "goodbye"});
8 });
注意:如果为同一个消息注册了多个监听器,则只有第一个监听器能够调用sendResponse()方法,其他的监听器将被忽略。
2) 保持长期连接的模式
对于保持长期连接的模式,在content scripts与Chrome扩展程序页面之间建立通道(可以为通道命名),可以处理多个消息。在通道的两端分别拥有一个chrome.runtime.Port对象,用以收发消息。
在content scripts主动建立通道如下:
1 var port = chrome.runtime.connect({name: "yisheng"});//通道名称
2 port.postMessage({joke: "Knock knock"});//发送消息
3 port.onMessage.addListener(function(msg) {//监听消息
4 if (msg.question == "Who's there?")
5 port.postMessage({answer: "yisheng"});
6 else if (msg.question == "Madame who?")
7 port.postMessage({answer: "Madame... Bovary"});
8 });
在Google Chrome扩展程序页面主动建立通道如下:
1 chrome.tabs.query(
2 {active: true, currentWindow: true},
3 function(tabs) {
4 var port = chrome.tabs.connect(//建立通道
5 tabs[0].id,
6 {name: "yisheng"}//通道名称
7 );
8 });
在content scripts或Google Chrome扩展程序页面,监听建立连接的请求如下:
1 chrome.runtime.onConnect.addListener(function(port) {
2 console.assert(port.name == "yisheng");
3 port.onMessage.addListener(function(msg) {
4 if (msg.joke == "Knock knock")
5 port.postMessage({question: "Who's there?"});
6 else if (msg.answer == "Madame")
7 port.postMessage({question: "Madame who?"});
8 else if (msg.answer == "Madame... Bovary")
9 port.postMessage({question: "I don't get it."});
10 });
11 });
在content scripts或Google Chrome扩展程序页面的任一端,调用chrome.runtime.Port.disconnect()则关闭连接,同时出发disconnect事件。这时,只有另一端监听chrome.runtime.Port.onDisconnect事件,则可以知道连接关闭。
3) Google Chrome扩展程序之间消息模式
还可以在不同的Google Chrome扩展程序之间发送消息,只要知道Google Chrome扩展程序的ID。这使得Google Chrome扩展程序可以发布服务为其他扩展程序所用。
这种Google Chrome扩展程序之间的消息也分为一次性请求与响应模式和保持长期连接的模式。
Google Chrome扩展程序监听调用其服务的消息如下:
1 //一次性请求与响应模式:
2 chrome.runtime.onMessageExternal.addListener(
3 function(request, sender, sendResponse) {
4 if (sender.id == blacklistedExtension)//黑名单
5 return; // don't allow this extension access
6 else if (request.getTargetData)
7 sendResponse({targetData: targetData});
8 else if (request.activateLasers) {
9 var success = activateLasers();
10 sendResponse({activateLasers: success});
11 }
12 });
13
14 //保持长期连接的模式:
15 chrome.runtime.onConnectExternal.addListener(function(port) {
16 port.onMessage.addListener(function(msg) {
17 // See other examples for sample onMessage handlers.
18 });
19 });
发送调用服务的消息如下:
1 // The ID of the extension we want to talk to.
2 var laserExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";
3
4 // Make a simple request:
5 chrome.runtime.sendMessage(laserExtensionId, {getTargetData: true},
6 function(response) {
7 if (targetInRange(response.targetData))
8 chrome.runtime.sendMessage(laserExtensionId, {activateLasers: true});
9 });
10
11 // Start a long-running conversation:
12 var port = chrome.runtime.connect(laserExtensionId);
13 port.postMessage(...);
4) Google Chrome扩展程序接收指定的Web页面发送的消息
Google Chrome扩展程序可以与一些指定地点Web页面直接收发消息。
首先,在Google Chrome扩展程序的manifest.json文件设置可以通信的Web页面范围:
1 "externally_connectable": {
2 "matches": ["*://*.example.com/*"]
3 }
其次,在Google Chrome扩展程序中监听Web页面的消息如下:
1 chrome.runtime.onMessageExternal.addListener(
2 function(request, sender, sendResponse) {
3 if (sender.url == blacklistedWebsite)
4 return; // don't allow this web page access
5 if (request.openUrlInEditor)
6 openUrl(request.openUrlInEditor);
7 });
最后,在指定的Web页面中,发送消息如下:
1 // The ID of the extension we want to talk to.
2 var editorExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";
3
4 // Make a simple request:
5 chrome.runtime.sendMessage(editorExtensionId, {openUrlInEditor: url},
6 function(response) {
7 if (!response.success)
8 handleError(url);
9 });