介紹
瀏覽器外掛本質上就是利用前端的html\css\javascript等技術,借用瀏覽器對外提供的API,實現各種不同的功能
外掛組成
首先先看一下我們要開發的外掛的各個檔案組成
manifest.json :我們在專案的根檔案必須要有一個命名為manifest.json的檔案,它是整個外掛的功能入口,用來告訴瀏覽器外掛的一些基本資訊,及需要載入和執行的資原始檔等,裡面包含一些必填項:
- name 外掛的名字
- version 外掛版本
- manifest_version manifest的版本
- description 外掛的描述(選填)
{
"name": "hello extension",
"description": "Base Level Extension",
"version": "1.0",
"manifest_version": 2,
}
複製程式碼
- icons 在擴充套件程式管理介面,呈現的圖示,可以設定不同的尺寸的選項,用來適配不同的場景
{
"icons": {
"16": "images/extension_icon16.png",
"32": "images/extension_icon32.png",
"48": "images/extension_icon48.png",
"128": "images/extension_icon128.png"
},
}
複製程式碼
- background 在擴充套件程式被開啟時,會首先觸發執行background 選項中指定的js資源,並且一直存在於擴充套件程式的整個生命週期,直到擴充套件程式被關閉或者刪除,background一直被用來做任務和狀態的控制管理工作
// scripts 子選項指定了需要執行的js檔案的路徑及檔名
{
"background": {
"scripts":["background.js"],
"persistent": false
}
}
複製程式碼
background.js 通常最外層使用監聽事件chrome.runtime.onInstalled.addListener(()=>{})
初始化外掛,監聽外掛安裝成功後,會觸發對應的邏輯開始工作
- permissions 屬性值為一個陣列,申請chrome API的許可權,只有在這個選項中申請了才能使用(比如通過 XMLHttpRequest 跨域請求資料、訪問瀏覽器選項卡(tabs)、獲取當前活動選項卡(activeTab)、瀏覽器通知(notifications)、設定外掛啟用規則(declarativeContent)、類似localStorage的儲存(storage)等)
{
"permissions": [
"http://xxx.com/api"
"tabs",
"activeTab",
"notifications",
"declarativeContent",
"storage"
],
}
複製程式碼
與使用者實現UI互動的功能主要有兩個:browser_action、page_action 兩者選擇其中一個
- browser_action 提供的功能面向所有網站,外掛圖示一直是啟用的狀態
{ "browser_action": { "default_popup": "popup.html" // 指定click外掛圖示時,展示的頁面 "default_icon": "images/icon.png", // 指定瀏覽器工具欄展示的外掛的圖示 "default_title": "display tooltip" // 當滑鼠懸浮在外掛圖片上方時,展示的文字,叫tooltip;如果沒有這個配置選項,則懸浮時顯示menifest.json中的name選項值 } } 複製程式碼
- page_action 只針對對應目標網站提供的功能,外掛會在background頁設定啟用外掛的規則,只有滿足條件的網頁的外掛才是啟用的,其他網站外掛是不可用的,外掛的icon是灰的
在
background script
裡,chrome.runtime.onInstalled.addListener(()=>{})
初始化中使用chrome.declarativeContent
定義外掛被啟用的規則{ "page_action":{ "default_popup": "popup.html", // 指定click外掛圖示時,展示的頁面 "default_icon": "hello_extension.png" // 指定瀏覽器工具欄展示的外掛的圖示 "default_title": "display tooltip" }, } 複製程式碼
chrome.runtime.onInstalled.addListener(function() { // Replace all rules ... chrome.declarativeContent.onPageChanged.removeRules(undefined, function() { // With a new rule ... chrome.declarativeContent.onPageChanged.addRules([ { // That fires when a page's URL contains a 'g' ... conditions: [ new chrome.declarativeContent.PageStateMatcher({ pageUrl: { urlContains: 'g' }, //url的內容中包含字母g的,外掛才會被啟用 }) ], // And shows the extension's page action. actions: [ new chrome.declarativeContent.ShowPageAction() ] } ]); }); }); 複製程式碼
popup.html
popup.html 是使用browser_action或者page_action 指定的default_popup選項,表示點選外掛圖示會觸發的html資源,其與普通的html頁面的區別就是不能使用內聯script,其他都一樣 it can contain links to stylesheets and script tags, but does not allow inline JavaScript.
Content Script 對當前web頁面注入擴充套件程式程式碼,也就是在當前web頁面的上下文環境中,可以訪問頁面的DOM資訊並且修改DOM,具有操作Chrome API的許可權,並且可以把資訊傳遞給父級外掛
- content script工作在一個獨立的空間,它使得不會訪問和呼叫web page或者其他content script中定義的變數和函式,兩者唯一共享的是DOM
- Content Script與Page action 或Browser action通訊的方式是通過傳遞Message的方式,正確的機制應該是:
- Page action 或Browser action 中的js使用
chrome.tabs.sendMessage()
釋出一個請求資訊,傳遞出去當前的tab頁id及其他資訊 - 注意:從Page action 或Browser action釋出訊息到Content Script必須用
chrome.tabs.sendMessage()
,從Content Script釋出簡單的資訊到Page action 或Browser action 可以使用chrome.runtime.sendMessage()
chrome.tabs.sendMessage( tabs[0].id, //當前啟用的tab頁id {greeting: "hello"}, //需要傳遞的資訊 function(response) { //用來接收反饋的回撥函式 console.log(response.farewell); }); 複製程式碼
- Content Script完成初始化,設定
chrome.runtime.onMessage().addListener()
監聽事件,等待另一側請求資訊的釋出
chrome.runtime.onMessage.addListener( function(request, sender, sendResponse) { console.log(sender.tab ? "from a content script:" + sender.tab.url : "from the extension"); if (request.greeting == "hello") sendResponse({farewell: "goodbye"}); }); 複製程式碼
- Content Script將資訊通過Message釋出訂閱的方式,釋出出去
- Page action 或Browser action 中的js
chrome.tabs.sendMessage()
最後一個引數是一個回撥函式,用來接收另一側反饋回來的資料,最後同步到UI上
- Page action 或Browser action 中的js使用
chrome.tabs.sendMessage()
複製程式碼
-
Content script有兩種注入方式:程式設計式動態注入、宣告式注入
-
程式設計式動態注入:獲取
activeTab
許可權後,使用chrome.tabs.executeScript()
來注入js程式碼片段,使用chrome.tabs.insertCSS()
注入css程式碼片段//manifest.json { "name": "My extension", ... "permissions": [ "activeTab" ], } 複製程式碼
在Page action 或Browser action 中的js中實行程式設計式動態注入content script,其訪問和更改的就是web page上的DOM屬性
```js
chrome.runtime.onMessage.addListener(
function(message, callback) {
if (message == “changeColor”){
chrome.tabs.executeScript({
code: 'document.body.style.backgroundColor="orange"'
});
}
});
// 你也可以注入一個檔案
chrome.runtime.onMessage.addListener(
function(message, callback) {
if (message == “runContentScript”){
chrome.tabs.executeScript({
file: 'contentScript.js'
});
}
});
複製程式碼
- 宣告式注入,在manifest.json中使用content_scripts來定義
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["http://*.nytimes.com/*"],
"css": ["myStyles.css"],
"js": ["contentScript.js"]
}
],
...
}
複製程式碼
- matches:必填 陣列,用來匹配注入Content Script的頁面地址。可以使用萬用字元來指代:'*'指代任意長度字元;'?'指代單個字元
- css:選填 陣列,在頁面的任何DOM建立和展示之前,按照陣列中定義的順序,依次注入
- js:選填 陣列,按照陣列中定義的順序,依次注入
- run_at:Content Script注入的時機
- 如果是document_start, 檔案將在所有CSS載入完畢,但是沒有建立DOM並且沒有執行任何指令碼的時候注入
- 如果是document_end,則檔案將在建立完DOM之後,但還沒有載入類似於圖片或frame等的子資源前立刻注入
- 如果是document_idle,瀏覽器會在document_end和發出window.onload事件之間的某個時機注入。具體的時機取決與文件載入的複雜度,為加快頁面載入而優化
{
"content_scripts" : [{
"matches" : ["*://*/*"],
"js" : ["content.js", "jquery.js"],
"css" : ["style.css"],
"run_at" : "document_end"
}],
}
複製程式碼
-
options_page 設定外掛的選項頁面,配置了此選項後,在外掛上滑鼠右鍵時,會有一個‘選項’按鈕,點選後會進入options_page對應的頁面
{ "options_page":"options.html" } 複製程式碼
-
chrome_url_overrides 設定可替換的chrome預設頁面
- newtab:新建標籤時開啟的頁面
- bookmarks:書籤頁面
- history:歷史記錄
{ "chrome_url_overrides":{ "newtab": "newTab.html" } } 複製程式碼
常用到的Chrome API
chrome.tabs
-
chrome.tabs.create(object createProperties, function callback) 建立新的標籤。注: 無需請求manifest的標籤許可權,此方法也可以被使用。 Parameters createProperties ( object )
- windowId ( optional integer ) 建立新標籤的目標視窗。預設是當前視窗 。
- index ( optional integer ) 標籤在視窗中的位置。 值在零至標籤數量之間。
- url ( optional string ) 標籤導航的初始頁面。完整的URL 必須包含一個字首 (如 'www.google.com', 不能寫為 'www.google.com')。 相對 URL則與擴充套件所在的頁面相對, 預設值為新標籤頁面。
- selected ( optional boolean ) 標籤是否成為選中標籤。預設為true。
- pinned ( optional boolean ) 標籤是否被固定。預設值為false。
- callback ( optional function )
Callback function 回撥 引數 應該如下定義:
function(Tab tab) {...}; tab ( Tab ) 所建立的標籤的細節,包含新標籤的ID。
-
chrome.tabs.executeScript(integer tabId, object details, function callback) 向頁面注入JavaScript 指令碼執行 Parameters
- tabId ( optional integer ) 執行指令碼的標籤ID;預設為當前視窗所選中的標籤。
- details ( object )
要執行的指令碼內容,可選code或者file,但不能同時選兩者。
- code ( optional string ) 要執行的指令碼程式碼。
- file ( optional string ) 要執行的指令碼檔案。
- allFrames ( optional boolean ) true的時候,給所有frame執行指令碼。預設為false,只給頂級frame執行指令碼。
- callback ( optional function ) 所有指令碼執行後會被呼叫的回撥。
-
chrome.tabs.query(object queryInfo, function callback) 獲取經過特定過濾條件篩選的標籤頁資訊,如果沒有特定過濾條件則是獲取所有標籤頁資訊
-
queryInfo 為特定的屬性,用來對標籤頁進行過濾。 常用的屬性有:(所有屬性均為boolean值)
- active 標籤在他們的視窗中是否是啟用狀態
- currentWindow 標籤是否在當前視窗
chrome.tabs.query({ active:true, currentWindow:true },function(tabs){ //... }) 複製程式碼
-
callback 是獲取到特定標籤頁資訊後的回撥函式,引數為標籤的資訊
-
chrome.contextMenus 當前頁面,選中內容後右鍵展示的內容
- 在manifest.json中設定許可權
{"permissions": ["contextMenus", "storage"]}
- 放在background.js中,初始化之後的回撥函式中,使用
chrome.contextMenus.create({})
建立內容目錄chrome.runtime.onInstalled.addListener(function() { //... chrome.contextMenus.create({ "id": "sampleContextMenu", "title": "Sample Context Menu", "contexts": ["selection"] }); }); 複製程式碼
外掛安裝
- 進入擴充套件程式管理介面 chrome://extensions
- 開啟'開發者模式'
- 可以選擇'載入已解壓的擴充套件程式',將本地開發目錄上傳上去即可
- 可以使用'打包擴充套件程式',生成.ctx字尾的擴充套件程式,就可以釋出到google應用市場了
外掛除錯
注意:
- 每次重新整理頁面,都會執行browser_action或page_action 指定的popup.html資源
- background指定的js資源只在外掛安裝時執行,之後就一直存在程式中,不會重複執行
除錯步驟
- 在瀏覽器工具欄右鍵外掛圖示
- 點選'審查彈出內容',進入控制檯
- 切換到Source欄,即可看到browser_action或page_action 指定的資源,設定斷點
- 切換到Console欄,輸入location.reload(),發現原有的頁面重新進行了載入,同時browser_action或page_action 指定的資源也重新執行了一遍,並在設定的斷點處停下來,等待debug