請與上一篇一起服用。要真的弄懂擴充功能的事件系統,請先對擴充功能的組成有清楚的概念。如果你覺得自己在理解上有困難,是時後回到前面複習一下:Chrome Extension 開發與實作 04-名詞定義:架構的組成部份
Chrome允許擴充功能接受其他擴充功能傳送的訊息,你可以提供一個公用的API,讓其他擴充功能使用。
相關API: runtime.sendMessage 以及 runtime.connect
與擴充功能的其他部份並無差別(請參考上一篇),記得使用擴充功能的ID作為傳入extensionId
參數,以指定發送對象。
一次性請求
chrome.runtime.sendMessage(string extensionId, any message, object options, function responseCallback)
長時間連接
chrome.runtime.connect(string extensionId, object connectInfo)
相關API:runtime.onMessageExternal 以及 runtime.onConnectExternal
一次性接收
chrome.runtime.onMessageExternal.addListener(function callback)
長時間連接的接受:同樣的回調中會提供runtime.port物件供你發送及接收訊息
chrome.runtime.onConnectExternal.addListener(function callback)
內容腳本不能存取外部訊息,如果有此需求需經由內部的事件腳本將訊息傳遞給內部的內容腳本。
載入擴充功能A,實作頁面按鈕,並且一開始不啟用此按鈕。
載入擴充功能B,此功能實作瀏覽器按鈕,按下按鈕打開彈出視窗,視窗中有一顆按鈕。
var toggle = false;
//可以列出禁止防問的黑名單ID
var blockList = [];
chrome.runtime.onMessageExternal.addListener(function(message, sender, sendResponse) {
console.log(sender);
//如果訪問在黑名單,就不作任何動作
if (blockList.indexOf(sender.id) != -1) {
return;
}
if (message.name != "切換頁面按鈕") {
return;
}
//如果按鈕是啟用的狀態則開,否則關
if (!toggle) {
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
chrome.pageAction.show(tabs[0].id);
toggle = !toggle;
});
} else {
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
chrome.pageAction.hide(tabs[0].id);
toggle = !toggle;
});
}
sendResponse("來自擴充功能A的訊息:操作完成");
});
document.addEventListener('DOMContentLoaded', function(dcle) {
var dButton = document.getElementById("button");
//注意擴充功能A的ID在開始狀態是會變的,如果你要在你的地端跑這個範例
//請自行在擴充功能管理頁面查看ID
//一旦正式發佈你的擴充功能,你可以得到一組固定ID
var extensionID = "fhggngpimllbngfbjdbiplgmhdppiiop";
//點擊按鈕,向擴充功能A發動訊息
dButton.addEventListener('click', function(e) {
console.log("click");
chrome.runtime.sendMessage(
extensionID,
{ name: "切換頁面按鈕" },
function(response) {
console.log(response);
});
});
});
注意:擴充功能A的ID在是會變的,如果你要在你的地端跑這個範例,請自行在擴充功能管理頁面查看ID, 一旦正式發佈你的擴充功能,你可以得到一組固定ID
右下擴充功能A的事件頁面,右上擴充功能B的彈出視窗腳本
完整範例在:Github
就像上面跨擴充功能溝通的例子雷同,事實上擴充功能也能接收並且響應來自普通網頁的訊息,要能讓網站可以用使用Chrome的訊息API,首先你得在設定檔中列出允許傳遞訊息的網域。
"externally_connectable": {
"matches": ["*://*.example.com/*"]
}
網址的匹配表執式必需至少包含一個二級域名,也就是禁止使用“”、“.com”、“.co.uk”以及“.appspot.com”之類的主機名作為匹配表達式。
{
"manifest_version": 2,
"name": "訊息範例-頁面腳本向事件腳本發送訊息",
"description": "頁面腳本向事件腳本發送訊息",
"version": "2.0",
"page_action": {
"default_title": "頁面腳本向事件腳本發送訊息",
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"background": {
"scripts": ["event.js"],
"persistent": false
},
"externally_connectable": {
"matches": ["*://localhost/*"]
},
"permissions": ["tabs"]
}
var toggle = false;
//可以列出禁止防問的黑名單網址
var blockList = [];
chrome.runtime.onMessageExternal.addListener(function(message, sender, sendResponse) {
console.log(sender);
//如果訪問在黑名單,就不作任何動作
if (blockList.indexOf(sender.url) != -1) {
return;
}
if (message.name != "切換頁面按鈕") {
return;
}
//如果按鈕是啟用的狀態則開,否則關
if (!toggle) {
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
chrome.pageAction.show(tabs[0].id);
toggle = !toggle;
});
} else {
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
chrome.pageAction.hide(tabs[0].id);
toggle = !toggle;
});
}
sendResponse("來自擴充功能的訊息:操作完成");
});
<!DOCTYPE html>
<html itemscope itemtype="http://schema.org/Article" ng-app="markup" debug="false">
<!--page default Information-->
<!--html head-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="edge,chrome=1">
<title>首頁</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/flat-ui/2.3.0/css/flat-ui.css">
</head>
<!--html body-->
<body class="l-index">
<section class="index">
<p style="padding:100px;"><a id="button" href="#" class="btn btn-primary btn-large btn-block">切換擴充功能的頁面按鈕</a></p>
</section>
</body>
<script src="../js/page.js"></script>
</html>
var dButton = document.getElementById("button");
var extensionID = "ampdcffgccbnfionpljcpncpcjfbhbag";
//點擊按鈕,向擴充功能A發動訊息
dButton.addEventListener('click', function(e) {
console.log("click");
chrome.runtime.sendMessage(
extensionID,
{ name: "切換頁面按鈕" },
function(response) {
console.log(response);
});
});
完整範例:Github
前面已經提過,內容腳本沒辦法存取runtime.onMessageExternal 以及 runtime.onConnectExternal API,因此內容腳本當然沒辦法像上面的案例一樣使用這個途勁與網頁的腳本溝通。
作為替代方案,內容腳本可以使用標準的網頁Javascript API。(注意:非chrome *API)
相關API:Window.postMessage
window.postMessage(message, targetOrigin, [transfer]);
下面示範內容腳本在頁面插入一個按鈕,當使用者按下按鈕時,對頁面腳本發送訊息
//region {variables and functions}
var consoleGreeting = "Hello World!";
var targetOrigin = window.location.origin;
var message = "Test message X";
function createButton() {
var button = document.createElement("button");
button.style.width = "70px";
button.style.height = "40px";
button.style.position = "fixed";
button.style.top = "10px";
button.style.right = "10px";
button.innerText = "Send Message";
document.body.appendChild(button);
return button;
}
//end-region
//region {calls}
console.log(consoleGreeting);
var button = createButton();
button.addEventListener("click",function() {
console.log("Button clicked!");
window.postMessage(message,targetOrigin);
});
訊息的接收:
相關API:Window.addEventListener
window.addEventListener("message", listener[, options]);
下面示範一個網頁腳本接收來自頁面腳本的訊息
//region {variables and functions}
var sendMessageButtonID = "send_message";
var greeting = "Hello World!";
//var targetOrigin = window.location.origin;
//var message = "Test message Y";
//end-region
//region {calls}
console.log(greeting);
document.addEventListener("DOMContentLoaded",function(dcle) {
var buttonID = document.getElementById(sendMessageButtonID);
buttonID.addEventListener("click",function(ce) {
//window.postMessage(message,targetOrigin);
});
});
window.addEventListener("message",function(me) {
console.log("message: " + me.data);
});
//end-region
本來想作範例,但我想了一下覺得這種使用情景實在不多,這裡就不示範了。
如果大家對這個段落的範例有興趣,這裡使用的是電子書中的範例,完整的範例參考第三章節的CSandWS範例
要小心跨站腳本攻擊,避免使用以下API:
chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
// 警告:可能會執行惡意腳本
var resp = eval("(" + response.farewell + ")");
});
chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
// 警告:可能會插入惡意腳本
document.getElementById("resp").innerHTML = response.farewell;
});
首選以下API:
chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
// JSON.parse 不會攻擊者的執行腳本。
var resp = JSON.parse(response.farewell);
});
chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
// innerText 不會插入攻擊者的html (因為可能會包含惡章腳本)。
document.getElementById("resp").innerText = response.farewell;
});
不要混肴chrome extension API提供的事件監聽系統,跟JS的DOM事件監聽系統。 寫法不同,用法不同:
Chrome Etension 的 事件監聽API:(chrome.* API)
chrome.commands.onCommand.addListener(function(command) {
chrome.browserAction.setIcon(
details,
function() {/**/}
);
});
chrome.tabs.onUpdated.addListener(function(tabId,changeInfo,tab) {
console.log(tabId);
chrome.pageAction.show(tabId);
});
JS的DOM物件事件監聽API: (Javascript API)
el.addEventListener("click", modifyText, false);
網頁腳本
也能使用Chrome.runtime API 對擴充功能發起訊息,你必需在安裝檔設定網域。不能使用
runtime.onMessageExternal 以及 runtime.onConnectExternal API,作為替代方案,你可以使用標準的網頁訊息API與載入頁面的腳本溝通。訊息系統真的是繞的我蠻暈的,每次寫訊息相關的系統都覺得自己人格分裂的應該不只我一人。