這個模組會產生一個建構函式 PageMod,此函式會建立一個新的 page modification (簡稱"mod")( MOD 數位電視 開玩笑的啦 哈哈:P <-某人餓瘋了)。
Page mod 只能在頁面已經被載入或重新載入時才可以修改頁面內容。換句話說,如果 add-on 在使用者瀏覽器開啟的情況下被載入,則使用者必須重新載入所有要讓 mod 修改的頁面。
若要停止某個 page mod 的作用,呼叫 page mod 的 destroy 方法。
跟所有其他模組相同,若要和網頁內容作互動,page-mod 使用在 content process 中執行並定義 messaging API的content scripts,以在 content script 和 main add-on code 之間做構通。
建立一個 PageMod ,add-on 開發者必須提供:
- 基於網頁URL的一組規則(rule)來想要的要面。每個規則以 match-pattern syntax 來指定。
- 要在想處理的頁面中執行的 content scripts
- 為 onAttach 設定一個值:這個值是一個將在符合 ruleset(=a set of rules)的網頁被載入時被呼叫的函式。這麼做是為了在 add-on code和contentscript 之間建立溝通通道。
以下的add-on中,當一個符合 ruleset 的頁面被載入時,會顯示一個提示視窗:
var pageMod = require("page-mod"); pageMod.PageMod({ include: "*.org", contentScript: 'window.alert("Page matches ruleset");' });如果指定一個值"ready" 或 "end"(而不是"start")給 contentScriptWhen,則 content script 可以和 DOM 本身互動(因為"ready"的時機為載入所有 DOM 架構之後):
var pageMod = require("page-mod"); pageMod.PageMod({ include: "*.org", contentScriptWhen: 'end', contentScript: 'document.body.innerHTML = ' + ' "<h1>Page matches ruleset</h1>";' });上方例子中,當開啟或重新整理網址中包含"*.org"的網頁時,會將該網頁的網頁主體內容(<body>)改變為"<h1>Page matches ruleset</h1>"。
使用 contentScriptFile
此文大部分的例子都是以 string 來定義 content script,並使用 contentScript 選項(option)來指定給 page mods。
然而我們也可以在 data 目錄下建立獨立的 content script 檔案,然後使用 self 模組來取得指向該檔案的 URL,接著將其指定給 page-mod 的 contentScriptFile 屬性。
舉例來說,如果我們將 content script 命名為"myScript.js"並儲存在 data 目錄之下,我們可以使用以下的程式碼來指定到這個檔案:
var data = require("self").data; var pageMod = require("page-mod"); pageMod.PageMod({ include: "*.org", contentScriptWhen: 'end', contentScriptFile: data.url("myScript.js") });
Communicating With Content Scripts
當符合規則的頁面被載入時,PageMod 會呼叫 add-on 程式碼中提供 給onAttach 的函式。PageMod 提供一個變數,worker object,給這個函式。
我們可以將 worker 視為 add-on 中的 add-on code 和附加在此 page 的 content script 之間的溝通通道結束。換句話說就是,當 content script 附加到符合規則的網頁中時,會呼叫定義在 onAttach 選項中的函式。
因此 add-on 可以藉由呼叫 worker.postMessage(),將訊息傳送給 content script;藉由註冊監聽 worker.on() 的函式,從 content script 接收訊息。
要注意的是,如果多個符合規則的頁面被同時載入,那每個頁面自己的 content script 副本會被載入其自己的 execution context。在這種情況下,onAttach 會為每個載入的頁面呼叫一次,然後 add-on 程式碼會有個別頁面的獨立 worker:
由以下例子證明:
var pageMod = require("page-mod"); var tabs = require("tabs"); var workers = []; pageMod.PageMod({ include: ["http://www.mozilla*"], contentScriptWhen: 'end', contentScript: "onMessage = function onMessage(message) {" + " window.alert(message);};", onAttach: function onAttach(worker) { if (workers.push(worker) == 3) { workers[0].postMessage("The first worker!"); workers[1].postMessage("The second worker!"); workers[2].postMessage("The third worker!"); } } }); tabs.open("http://www.mozilla.com"); tabs.open("http://www.mozilla.org"); tabs.open("http://www.mozilla-europe.org");P.S.上例中用到 Tabs 模組,不了解 Tabs 模組請看這篇。
在這裡我們指定了一個 ruleset 來配對任何以 "http://www.mozilla" 開頭的URL。當某頁面符合條件時,我們加入一個 supplied worker 到 array 中,然後當 array 中有三個 workers 時,我們輪流送出一個訊息給每個 worker,告訴他們要被附加的順序。最後worker在提示框( alert box )中顯示訊息。
這顯示了,不同的頁面在不同的 context 中執行,且每個 context 有自己的和 add-on script 溝通的通道( communication channel )。
值得注意的是,雖然每個 execution context 有不同的 worker,但 worker 是可以在單一 execution context 相關的所有 content scripts 之中分享的。以下的例子中,我們傳送兩個 content scripts 給 PageMod:這兩個 content scripts 會分享一個worker實體。
在範例中,每個 content scripts 在 add-on script 中以使用全域函式 postMessage 送出訊息,以辨識身分。在 onAttach 函式中,add-on 程式碼註冊監聽函式,讓頁面被附加 content script 時在 console 中紀錄 log:
var pageMod = require("page-mod"); var data = require("self").data; var tabs = require("tabs"); pageMod.PageMod({ include: ["http://www.mozilla*"], contentScriptWhen: 'end', contentScript: ["self.postMessage('Content script 1 is attached to '+ " + "document.URL);", "self.postMessage('Content script 2 is attached to '+ " + "document.URL);"], onAttach: function onAttach(worker) { console.log("Attaching content scripts") worker.on('message', function(data) { console.log(data); }); } }); tabs.open("http://www.mozilla.com");程式碼中第 15 行的 Console 用法請見這裡。
這個add-on的主控台輸出(console output)為:
info: Attaching content scripts
info: Content script 1 is attached to http://www.mozilla.com/en-US/
info: Content script 2 is attached to http://www.mozilla.com/en-US/
Mapping workers to tabs
worker 物件有一個 tab 屬性,這個屬性會回傳與該 worker 關聯的分頁( tab )。我們可以以此來存取與某特定網頁關聯的 tab API:
var pageMod = require("page-mod"); var tabs = require("tabs"); pageMod.PageMod({ include: ["*"], onAttach: function onAttach(worker) { console.log(worker.tab.title); } });
Attaching content scripts to tabs
前面我們已經看過 page mod API 如何透過 URL 來附加 content script 給 page。然而有時候我們並不在乎URL:我們只想要在某特定分頁( tab )中執行需要的 script (而不是本文前面的例子,是對符合 URL 條件的網頁做處理)。
舉例來說,我們可能想要當使用者點擊 widget 時,在目前的分頁中執行一段 script:來阻擋特定內容、改變字體樣式、或顯示網頁的 DOM 結構。
使用 tab 物件的 attach 方法,我們可以附加一組 content scripts 給特定分頁,這些 scripts 會立刻執行。
下面的 add-on 建立一個 widget,其被點擊時,所有在當前分頁中載入的頁面中的 div 元素會被標示出來( highlight ):
var widgets = require("widget"); var tabs = require("tabs"); var widget = widgets.Widget({ id: "div-show", label: "Show divs", contentURL: "http://www.mozilla.org/favicon.ico", onClick: function() { tabs.activeTab.attach({ contentScript: 'var divs = document.getElementsByTagName("div");' + 'for (var i = 0; i < divs.length; ++i) {' + 'divs[i].setAttribute("style", "border: solid red 1px;");' + '}' }); } });Destroying Workers 當 workers 的關聯頁面被關閉時,其會產生一個 detach 事件:也就是說,當分頁會關閉或分頁的位置被改變時。如果我們保存包含屬於某 page mod 的 worker 的清單,我們可以使用 detach 事件來移除無效的 workers。 例如,我們保存附加在某 page mod 的 workers 清單:
var workers = []; var pageMod = require("page-mod").PageMod({ include: ['*'], contentScriptWhen: 'ready', contentScriptFile: data.url('pagemod.js'), onAttach: function(worker) { workers.push(worker); } });我們可以藉由監聽 detach 事件來移除已經無效的 workers:
var workers = []; function detachWorker(worker, workerArray) { var index = workerArray.indexOf(worker); if(index != -1) { workerArray.splice(index, 1); } } var pageMod = require("page-mod").PageMod({ include: ['*'], contentScriptWhen: 'ready', contentScriptFile: data.url('pagemod.js'), onAttach: function(worker) { workers.push(worker); worker.on('detach', function () { detachWorker(this, workers); }); } });了解更多 Page-mod 類別的詳細建構子、方法、屬性、事件,請參考這裡。
沒有留言:
張貼留言