跳至主要内容

[Chrome Extension] Content Script

keywords: chrome.runtime.getURL()

Content Script @ Chrome Developer

Content scripts 是用來在瀏覽器網頁上執行的檔案,透過 content script 可以取得網頁內容,改變 DOM,並且傳送資訊到擴充套件。

⚠️ content script 並非可以直接使用所有 chrome 提供的 API,其中可以直接使用 i18n, storage, runtime 裡的 runtime.connect, runtime.getManifest,runtime.getURL, runtime.id, runtime.onConnect, runtime.onMessage, runtime.sendMessage

隔離的空間(Isolated Worlds)

透過 Content Script 和原本網頁上的 Script 都可以操作該頁面上的 DOM,但 Content Script 和網頁上的 Script 實際上是兩個獨立的執行環境,因此沒辦法透過 content script 去執行在網頁上所定義好的方法;反之亦然,網頁上的 JavaScript 程式也沒辦法使用到 content script 內的程式。也因此,頁面上使用到的函式庫和 content script 中使用到的函式庫即使版本不同也不會衝突:

content script

注入程式 Inject Scripts

keywords: chrome.tabs.executeScript([<tabId>], { code, file })

Content script 可以透過 programmaticallydeclaratively 的方式注入,前者是在某個事件後注入 contentScript,後者則是在 manifest.json 中定義好規則後,會自動注入。

Inject Programmatically

要注入 programmatic content script 需要在 manifest.json 中定義 activeTab 的權限(permission),如此可以在當前的 active tab 執行 content script 而不需要指定 cross-origin permissions

// manifest.json
{
"name": "My extension",
...
"permissions": [
"activeTab"
],
...
}

如此將可以透過 chrome.tabs.executeScript() 來注入 content script,注入的方式可以直接撰寫某一段程式碼(code),或者執行某一支檔案(file

//  執行程式碼 chrome.tabs.executeScript({ code: 'alert()' });
// 執行檔案 chrome.tabs.executeScript({ file: 'filePath.js' });

chrome.runtime.onMessage.addListener((message, callback) => {
if (message == 'executeCode') {
// 執行一段程式碼
chrome.tabs.executeScript({
code: 'document.body.style.backgroundColor="orange"',
});
} else if (message == 'executeFile') {
// 執行一支檔案
chrome.tabs.executeScript({
file: 'contentScript.js',
});
}
});

Inject Declaratively

透過 declarative injection 可以自動在特定頁面執行執行 content scriptdeclarative injection 是透過在 manifest.json 中去註冊 content_scripts 的欄位,它可以包含 JavaScript, CSS 檔或同時兩者,所有要自動執行的 content_scripts 都必須指定 match patterns

match patterns 撰寫參考 @ Chrome Developer

// 如果有要使用 declarative content script(到特定頁面自動載入 contentScript)
{
"name": "UXTesting Extension",
"content_scripts": [
{
"matches": ["http://*.nytimes.com/*"], // <all_urls>
"exclude_matches": ["*://*/*business*"],
"include_globs": ["*nytimes.com/???s/*"],
"exclude_globs": ["*science*"],
"run_at": "document_idle", // 決定 JavaScript 注入的時間,預設是 document_idle
"css": ["myStyles.css"],
"js": ["contentScript.js"]
}
]
}

Run Time

Content Script 的 JavaScript 檔案被注入到網頁的時間可以透過 manifest.json 中的 run_at 欄位來控制,建議使用預設的 document_idle,但如果有需要也可以指定成 document_startdocument_end

執行時間的說明可參考:run time @ Chrome Developer

Content Script 和 Web Page 間的溝通

由於 Content Script 和 Web Page 是屬於各自隔離的環境(isolated worlds),若有需要彼此溝通則需要使用 window.postMessage 的方式:

// content script
var port = chrome.runtime.connect();

window.addEventListener(
'message',
function (event) {
// We only accept messages from ourselves
if (event.source != window) return;

if (event.data.type && event.data.type == 'FROM_PAGE') {
console.log('Content script received: ' + event.data.text);
port.postMessage(event.data.text);
}
},
false,
);
// webpage
document.getElementById('theButton').addEventListener(
'click',
function () {
window.postMessage({ type: 'FROM_PAGE', text: 'Hello from the webpage!' }, '*');
},
false,
);

其他

透過 chrome.runtime.getURL() 可以取得擴充套件內部檔案的 URL:

// 取得 chrome extension 內的檔案,並且寫入網頁的 DOM

// <extensionDir>/images/logo@16.png:
var imgURL = chrome.runtime.getURL('images/logo@16.png');
document.getElementById('someImage').src = imgURL;