[Chrome Extension] API 筆記
keywords: Chrome Extension
- 🔖 Develop Extensions:列出許多開發 Extension 時會用到的 API。
- 🔖 Manifest file format:列出所有 manifest 中可用的 API。
Manifest
Manifest file format @ Chrome Extension
// manifest.json
{
"name": "PJCHENder Extension",
"version": "1.0",
"description": "PJCHENder tester client tool for recording.",
// Extension 要正常運作一定需要的權限
"permissions": [
"storage",
"declarativeContent",
"alarms",
"notifications",
"activeTab", // 取得當前 active tab 的存取權限
"tabs", // 新增、修改還重新排序頁籤
"http://localhost:3000/*" // 具有符合該網址 tab 的存取權限(可以對它使用 executeScript)
],
// 使用 host permission 除了可以 read 和 query tab 中的資料外,還可 inject scripts
"host_permissions": ["https://developer.chrome.com/*"],
// 使用者可以自己選擇要不要提供
"optional_permissions": ["topSites", "http://www.developer.chrome.com/*"],
"background": {
"scripts": ["background.js"],
"persistent": false
},
// 如果要從 webpack 傳送 message 到 extension 內
"externally_connectable": {
"matches": ["http://localhost:3000/*"]
},
// 如果要讓 webpage 可以存取 extension 內的資源
"web_accessible_resources": ["images/*"],
// extension 在 toolbar 上的 icon
"browser_action": {
"default_icon": {
"16": "images/logo@16.png",
"32": "images/logo@32.png",
"48": "images/logo@48.png",
"128": "images/logo@128.png"
},
"default_title": "__MSG_tooltip__", // 搭配 i18n 使用 __MSG_foobar__
"default_popup": "popup.html"
},
// extension 的選項頁
"options_page": "options.html",
"icons": {
"16": "images/logo@16.png",
"32": "images/logo@32.png",
"48": "images/logo@48.png",
"128": "images/logo@128.png"
},
// 搭配 i18n
"default_locale": "en",
// 如果有要使用 declarative content script(到特定頁面自動載入 contentScript)
"content_scripts": [
{
"matches": ["http://*.nytimes.com/*"],
"exclude_matches": ["*://*/*business*"],
"include_globs": ["*nytimes.com/???s/*"],
"exclude_globs": ["*science*"],
"run_at": "document_idle", // 決定 JavaScript 注入的時間,預設是 document_idle
"css": ["myStyles.css"],
"js": ["contentScript.js"]
}
],
"content_security_policy": "default-src 'none'; style-src 'self'; script-src 'self'; connect-src https://maps.googleapis.com; img-src https://maps.googleapis.com",
"manifest_version": 2
}
Manifest File Format @ Chrome Developer
Permission
- 常用的權限項目 Permissions @ Chrome Developer
- Declare Permissions and Warn Users @ Chrome Developer
使用下述功能前,需要先在 manifest.json
中開啟權限:
// manifest.json
{
...
"permissions": [
"https://*/*",
"http://*/*",
"activeTab",
"declarativeContent",
"storage",
"notifications",
"alarms",
],
...
}
notifications
keywords: notifications.create()
, notifications.onButtonClicked.addListener()
// background.js
// 推播 notification
chrome.notifications.create({
type: 'basic',
iconUrl: stayHydratedPNG,
title: 'Time to Hydrate',
message: "Everyday I'm Guru'!",
// notification 上的按鈕
buttons: [{ title: 'Keep it Flowing.' }],
priority: 0,
});
// 監聽 notification 上的 button 如果被點擊
chrome.notifications.onButtonClicked.addListener(function () {
// ...
});
alarms:鬧鐘與計時器
keywords: alarms.create()
, alarms.clearAll()
, alarms.onAlarm.addListener()
// popup.js
chrome.alarms.create({ delayInMinutes: minutes }); // 設定鬧鐘時間
chrome.alarms.clearAll(); // 取消所有計時器
// background.js
// 計時器時間到時發送推播
chrome.alarms.onAlarm.addListener(function() {
chrome.notifications.create(...);
});
storage
keywords: storage.sync.get()
, storage.sync.set()
// 把特定的 key-value 存在 chrome storage 中
chrome.storage.sync.set({ color: '#3aa757' }, function () {
console.log('The color is green.');
});
// 把特定的 key-value 從 chrome storage 中取出
// get 的內容可以是字串或陣列
chrome.storage.sync.get(['color', 'name'], function (data) {
console.log('data.color', data.color);
});
// 列出所有儲存在 chrome.storage 的內容
chrome.storage.sync.get(null, function (data) {
console.info(data);
});
// 移除 chrome.storage 的內容
// remove 的內容可以是字串或陣列
chrome.storage.sync.remove('color', function (data) {
console.info(data);
});
// 清除 chrome.storage 的所有內容
chrome.storage.local.set({ variable: variableInformation });
declarativeContents
keywords: declarativeContents, declarativeContent.onPageChanged
, declarativeContent.PageStateMatcher()
- declarativeContent @ Chrome Extension
- chrome.events @ Chrome Extension
透過 Declarative Content API 可以在不需要 host permission 或注入 content script 的情況下,根據頁面的 URL 或 CSS 來決定要不要顯示擴充套件的 page action。如果要讓使用者在點擊 page action 能夠和頁面進行互動的話,需要允許 activeTab 的權限。
如果你需要其他更精確的控制或需要在特定頁籤改變其外觀的話,則需要繼續使用 pageAction API。
chrome.declarativeContent.onPageChanged.addRules()
作為一個 declarative API,它讓你可以在 onPageChanged
事件註冊一些規則,當這些規則符合時就執行特定的動作(例如 ShowPageAction
和 SetIcon
)。
用來添加規則:
// background.js
chrome.declarativeContent.onPageChanged.addRules([
{
// 用來設定哪些條件下要做哪些事
// 當使用者頁面的 URL 匹配到下面的項目時...
conditions: [
new chrome.declarativeContent.PageStateMatcher({
pageUrl: {
schemes: ['https', 'http'],
},
}),
],
// 條件符合時要做的行為
actions: [new chrome.declarativeContent.ShowPageAction()],
},
]);
匹配使用者當前瀏覽頁面的 URL - PageStateMatcher
用 chrome.declarativeContent.PageStateMatcher
來匹配使用者當前瀏覽頁面的 URL:
// background.js
new chrome.declarativeContent.PageStateMatcher({
pageUrl: {
schemes: ['https', 'http'], // http 或 https 開頭的 URL 即可
hostEquals: 'developer.chrome.com', // 選定 host
},
});
tab / activeTab
chrome.tabs @ Chrome Developer
keywords: activeTab
, tabs.query
, tabs.executeScript
當允許 activeTab 後將可以
- 使用
tabs.executeScript
或tabs.insertCSS
- 透過
tabs.Tab
物件取得該 tab 的 URL, title, favicon - 透過 webRequest API 干涉該 tab 的網絡請求,可以讓擴充套件暫時取的該 tab 的 host 權限
使用
activeTab
的意思是有存取當前 active 頁籤(網頁)的權限,但若要executeScript
的網頁不是當前「active tab」,仍需要在 permission 中定義欲存取網頁的網址。
tabs.query
有 activeTab 的權限後可以在特定的頁籤(tab)上執行 JavaScript:
// popup.html
// 找出所有符合條件的 chrome 頁籤
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
// 在該頁籤上執行特定程式
chrome.tabs.executeScript(tabs[0].id, {
code: `document.body.style.backgroundColor='${color}';`,
});
});
tabs.executeScript
tabs.executeScript
的第一個參數是 tabId,不填寫時預設是 active tab:
// 在 active tab 上執行 JavaScript 檔案
chrome.tabs.executeScript({ file: 'logic.js' });
// 在 active tab 上執行 JavaScript 語法
chrome.tabs.executeScript({
code: 'document.body.style.backgroundColor="orange"',
});
contextMenus
keywords: chrome.contextMenus
chrome.runtime.onInstalled.addListener(function () {
chrome.contextMenus.create({
id: 'sampleContextMenu',
title: 'Sample Context Menu',
contexts: ['selection'],
});
});
bookmarks
keywords: chrome.bookmarks
desktopCapture
keywords: chrome.desktopCapture.chooseDesktopMedia
chrome.desktopCapture @ Chrome Developer
可以使用的 desktop capture source type 為:
const desktopCaptureSourceType = ['screen', 'window', 'tab', 'audio'];
chrome.desktopCapture.chooseDesktopMedia
// 可以使用的 desktop capture source type
const desktopCaptureSourceType = ['screen', 'window', 'tab', 'audio'];
chrome.desktopCapture.chooseDesktopMedia(
desktopCaptureSourceType,
tabs.Tab, // target tab (optional)
callback <string streamId, object options>
)
- 在
callback
中取得的streamId
可以用在getUserMedia()
API,用來產生使用者所選的 media source 對應的 media stream。 - 若使用者拒絕授權,則
streamId
將為空值。 - 如果使用者沒有選擇任何 source(或選擇取消),那麼該 callback 的 streamId 會是空值。這個 streamId 只能使用一次,且一段時間未使用的話將會過期。
Background Script / Background Page
Background Scripts @ Chrome Extension Manage Events with Background Scripts
Manage Events
擴充套件是以事件為主的程式,可以用來改變或增強 Chrome 瀏覽器的使用體驗。事件是由瀏覽器驅動,例如瀏覽到一個新頁面、移除書籤、關閉頁籤等等,擴充功能可以透過 Background Script 來監控這些事件,並給予相對應的回應。
Background Page 會在需要的時後被載入,並且在閒置時(idle)卸載(unloaded),一些例子如下:
- 擴充套件第一次被安裝或更新版本時
- Background Page 在監聽一個事件,並且事件被觸發(dispatch)時。
- Content Script 或其他的套件傳送訊息時。
- 擴充套件中其他的畫面(例如,popup)呼叫
runtime.getBackgroundPage
時。
Background Scripts 一旦載入後,只要有在做事(例如 ,呼叫 Chrome API 或發送網絡請求)Background Page 就會持續運行,此外,除非所有可視頁面(visible views)和所有溝通的連接埠(message ports)都關閉時,Background Page 才會卸載(unload)。要注意的是,開啟頁面並不一定會使得 Event Page 被加載,但可以避免在加載後被關閉。
有效的 Background Scripts 會處於閒置(dormant)的狀態,除非有事件要它們監聽,並作出對應的反應後,再重新進入卸載(unload)。
註冊 Background Scripts
Background Scripts 必須先在 manifest.json
內被註冊:
// manifest.json
{
"name": "Awesome Test Extension",
"background": {
"scripts": [
"backgroundContextMenus.js",
"backgroundOmniBox.js",
"backgroundOauth.js"
],
"persistent": false
}
}
❗ 注意:主要當 Background Script 需要使用到 chrome.webRequest API 時,才需要將
persistent
設為true
。
註冊與監聽事件
keywords: addListener
, removeListener
可以在 Background Scripts 中透過 addListener
來註冊事件:
// background.js
// 當 runtime 被 installed 時 ...
chrome.runtime.onInstalled.addListener(function () {
// 要做的事...
});
事件監聽器(listeners)時一定要在頁面開始時就以「同步(synchronously)」的方式註冊,像下面的 chrome.bookmarks.onCreated.addListener
這樣:
// background.js
chrome.runtime.onInstalled.addListener(function () {
// ...
});
// 這將會在 bookmark 被建立時觸發
chrome.bookmarks.onCreated.addListener(function () {
// do something
});
不能用非同步(asynchronously)的方式註冊事件,否則可能不會被觸發:
// badBackground.js
// 錯誤示範
chrome.runtime.onInstalled.addListener(function () {
// 錯誤!事件應該要頁面開始時就以同步的方式註冊
chrome.bookmarks.onCreated.addListener(function () {
// do something
});
});
若想要移除事件監聽器,可以使用 removeListener
:
// background.js
chrome.runtime.onMessage.addListener(function (message, sender, reply) {
chrome.runtime.onMessage.removeListener(event);
});
具有過濾功能的事件 API(filter event)
可以使用具有「過濾事件(filter event)」功能的 API,來監聽擴充功能在意的情況:
// background.js
chrome.webNavigation.onCompleted.addListener(
function () {
alert('This is my favorite website!');
},
{ url: [{ urlMatches: 'https://www.google.com/' }] },
);
卸載 Background Scripts (Unload)
重要的資料都應該要週期性的保存下來,避免擴充功能如果爆掉的時候,因為沒有接收到 onSuspend
事件而遺失重要資料。這可以透過 storage API 達到。
如果一個擴充功能有使用 message passing 需要確保所有的 ports 都要關掉,除非所有 message ports 都關閉,否則 background script 不會被卸載。監聽 runtime.Port.onDisconnect 事件可以讓你檢視開啟過的 ports 是否已被關閉,如果需要關閉的話可以參考 runtime.Port.disconnect。
當沒有該擴充功能需要處理的作業時,Background Scripts 會自動卸載(unload),如果有任何需要在最後手動清理了內容,可以使用 runtime.onSuspend
的事件:
// background.js
chrome.runtime.onSuspend.addListener(function () {
console.log('Unloading.');
chrome.browserAction.setBadgeText({ text: '' });
});
Debug
若要偵錯 background scripts 的內容,在「開發人員模式」下可以點選「背景頁面」:
Content Script
Manifest
// manifest.json
{
content_scripts: [
{
matches: ['http://*/*'],
js: ['mappy_content_script.js'],
},
],
},
Design User Interface
Browser Action: 一直處於 active 狀態
keywords: chrome.browserAction.setBadgeText()
, chrome.browserAction.setBadgeBackgroundColor()
如果擴充功能適合用在瀏覽器大部分的情境下,那麼可以使用 browser_action
。Browser action 可以包含 icon、 tooltip、badge、popup。
manifest.json
先在 manifest.json
中註冊 browser_action
:
// manifest.json
{
"name": "My Awesome browser_action Extension",
"browser_action": {
"default_icon": { // icon
"16": "images/icon16.png",
"32": "images/icon32.png"
},
"default_title": "Google Mail", // tooltip
"default_popup": "popup.html" // optional
},
}