內容指令碼是在網頁環境中執行的檔案,這些指令碼可使用標準文件物件模型 (DOM),讀取瀏覽器造訪的網頁詳細資料、進行變更,並將資訊傳遞至上層擴充功能。
瞭解內容指令碼功能
內容指令碼可以與擴充功能交換訊息,存取父項擴充功能使用的 Chrome API。他們也可以使用 chrome.runtime.getURL() 存取擴充功能檔案的網址,並將結果當做其他網址使用。
// Code for displaying EXTENSION_DIR/images/myimage.png:
var imgURL = chrome.runtime.getURL("images/myimage.png");
document.getElementById("someImage").src = imgURL;
此外,內容指令碼可以直接存取下列 Chrome API:
內容指令碼無法直接存取其他 API。
在獨立世界中工作
內容指令碼位於獨立世界中,因此內容指令碼可以變更 JavaScript 環境,不會與網頁或其他內容指令碼發生衝突。
擴充功能可能會在網頁中執行,程式碼類似於下列範例。
<html>
<button id="mybutton">click me</button>
<script>
var greeting = "hello, ";
var button = document.getElementById("mybutton");
button.person_name = "Bob";
button.addEventListener("click", function() {
alert(greeting + button.person_name + ".");
}, false);
</script>
</html>
該擴充功能可能會插入下列內容指令碼。
var greeting = "hola, ";
var button = document.getElementById("mybutton");
button.person_name = "Roberto";
button.addEventListener("click", function() {
alert(greeting + button.person_name + ".");
}, false);
按下按鈕時,系統會顯示這兩則快訊。
隔離世界不允許內容指令碼、擴充功能和網頁存取彼此建立的任何變數或函式。這也讓內容指令碼能夠啟用網頁不應存取的功能。
插入指令碼
以程式輔助方式插入
如果內容指令碼需要在特定情況下執行,請使用程式輔助插入功能。
如要插入程式輔助內容指令碼,請在資訊清單中提供 activeTab 權限。 這項權限可安全存取有效網站的主機,並暫時存取分頁,讓內容指令碼在目前有效的分頁上執行,而不必指定跨來源權限。
{
"name": "My extension",
...
"permissions": [
"activeTab"
],
...
}
內容指令碼可以程式碼形式插入。
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'
});
}
});
以宣告方式插入
如果要在指定網頁上自動執行內容指令碼,請使用宣告式插入。
以宣告方式插入的指令碼會登錄在資訊清單的 "content_scripts" 欄位中。
可包含 JavaScript 檔案、CSS 檔案或兩者。所有自動執行的內容指令碼都必須指定相符模式。
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["http://*.nytimes.com/*"],
"css": ["myStyles.css"],
"js": ["contentScript.js"]
}
],
...
}
| 名稱 | 類型 | 說明 |
|---|---|---|
matches {: #matches } |
必要。指定要將這個內容指令碼注入哪些網頁。如要進一步瞭解這些字串的語法,請參閱「比對模式」;如要瞭解如何排除網址,請參閱「比對模式和 glob」。 | |
css {: #css } |
選填。要插入相符網頁的 CSS 檔案清單。系統會按照這個陣列中的順序注入這些項目,然後才會建構或顯示網頁的任何 DOM。 | |
js {: #js } |
選填。要注入相符網頁的 JavaScript 檔案清單。系統會按照這個陣列中的順序插入這些項目。 | |
match_about_blank {: #match_about_blank } |
布林值 | 選填。指令碼是否應注入 about:blank 頁框,且父項或開啟器頁框符合 matches 中宣告的其中一個模式。預設為 false。 |
排除相符項目和 Glob
如要在資訊清單註冊中加入下列欄位,即可自訂指定的網頁比對。
| 名稱 | 類型 | 說明 |
|---|---|---|
exclude_matches {: #exclude_matches } |
選填。排除這個內容指令碼原本會注入的網頁。如要進一步瞭解這些字串的語法,請參閱「比對模式」。 | |
include_globs {: #include_globs } |
選填。在 matches 之後套用,只納入也符合這個 glob 的網址。旨在模擬 @include Greasemonkey 關鍵字。 |
|
exclude_globs {: #exclude_globs } |
選填。套用 matches 後,排除符合這個 glob 的網址。旨在模擬 @excludeGreasemonkey 關鍵字。 |
如果網頁網址符合任何 matches 模式和任何 include_globs 模式,且不符合 exclude_matches 或 exclude_globs 模式,系統就會將內容指令碼插入網頁。
由於 matches 屬性為必填,因此 exclude_matches、include_globs 和 exclude_globs 只能用於限制受影響的頁面。
下列擴充功能會將內容指令碼插入 http://www.nytimes.com/ health,但不會插入 http://www.nytimes.com/ business。
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["http://*.nytimes.com/*"],
"exclude_matches": ["*://*/*business*"],
"js": ["contentScript.js"]
}
],
...
}
Glob 屬性遵循的語法與比對模式不同,也更具彈性。可接受的 glob 字串是網址,其中可能包含「萬用字元」星號和問號。星號 * 可比對任何長度的字串 (包括空白字串),問號 ? 比對任何單一字元。
舉例來說,glob http:// ??? .example.com/foo/ * 會比對出下列任一結果:
- http:// www .example.com/foo /bar
- http:// the .example.com/foo /
但不符合下列條件:
- http:// my .example.com/foo/bar
- http:// example .com/foo/
- http://www.example.com/foo
這個擴充功能會將內容指令碼插入 http:/www.nytimes.com/ arts /index.html 和 http://www.nytimes.com/ jobs /index.html,但不會插入 http://www.nytimes.com/ sports /index.html。
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["http://*.nytimes.com/*"],
"include_globs": ["*nytimes.com/???s/*"],
"js": ["contentScript.js"]
}
],
...
}
這個擴充功能會將內容指令碼插入 http:// history .nytimes.com 和 http://.nytimes.com/ history,但不會插入 http:// science .nytimes.com 或 http://www.nytimes.com/ science。
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["http://*.nytimes.com/*"],
"exclude_globs": ["*science*"],
"js": ["contentScript.js"]
}
],
...
}
您可以納入其中一項、全部或部分項目,以達到正確的範圍。
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["http://*.nytimes.com/*"],
"exclude_matches": ["*://*/*business*"],
"include_globs": ["*nytimes.com/???s/*"],
"exclude_globs": ["*science*"],
"js": ["contentScript.js"]
}
],
...
}
執行時間
run_at 欄位可控制 JavaScript 檔案插入網頁的時機。首選和預設欄位為 "document_idle",但視需要也可以指定為 "document_start" 或 "document_end"。
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["http://*.nytimes.com/*"],
"run_at": "document_idle",
"js": ["contentScript.js"]
}
],
...
}
| 名稱 | 類型 | 說明 |
|---|---|---|
document_idle {: #document_idle } |
字串 | 建議使用。請盡可能使用 "document_idle"。瀏覽器會選擇在 "document_end" 和 windowonload 事件觸發後立即插入指令碼。實際注入時間取決於文件的複雜程度和載入時間,並會針對載入網頁速度進行最佳化。在 "document_idle" 執行的內容指令碼不需要監聽 window.onload 事件,保證會在 DOM 完成後執行。如果指令碼必須在 window.onload 之後執行,擴充功能可以使用 document.readyState 屬性檢查 onload 是否已觸發。 |
document_start {: #document_start } |
字串 | 指令碼會在 css 中的任何檔案之後插入,但會在建構任何其他 DOM 或執行任何其他指令碼之前插入。 |
document_end {: #document_end } |
字串 | 指令碼會在 DOM 完成後立即插入,但會在圖片和影格等子資源載入前插入。 |
指定影格
擴充功能可透過 "all_frames" 欄位指定是否要將 JavaScript 和 CSS 檔案插入符合指定網址條件的所有頁框,或只插入分頁中的最頂層頁框。
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["http://*.nytimes.com/*"],
"all_frames": true,
"js": ["contentScript.js"]
}
],
...
}
| 名稱 | 類型 | 說明 |
|---|---|---|
all_frames {: #all_frames } |
布林值 | 選填。預設值為 false,表示只會比對頂端影格。如果指定 true,系統會將指令碼插入所有影格,即使影格不是分頁中的頂端影格也一樣。系統會針對每個頁框獨立檢查網址規定,如果網址不符合規定,系統就不會將指令碼插入子頁框。 |
與嵌入網頁通訊
雖然內容指令碼和主機網頁的執行環境彼此隔離,但兩者共用網頁的 DOM 存取權。如果網頁想與內容指令碼通訊,或透過內容指令碼與擴充功能通訊,就必須透過共用的 DOM 進行。
舉例來說,您可以使用 window.postMessage 達成此目的:
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);
document.getElementById("theButton").addEventListener("click",
function() {
window.postMessage({ type: "FROM_PAGE", text: "Hello from the webpage!" }, "*");
}, false);
非擴充功能網頁 (例如 example.html) 會自行發布訊息。這封郵件會遭到內容指令碼攔截及檢查,然後發布至擴充功能程序。這樣一來,網頁就能與擴充程序建立通訊管道。您可以使用類似方式還原。
保障資料安全
雖然獨立世界提供一層保護,但使用內容指令碼可能會在擴充功能和網頁中產生安全漏洞。如果內容指令碼從其他網站接收內容 (例如發出 XMLHttpRequest),請務必先過濾內容,防範跨網站指令碼攻擊,再注入內容。請務必透過 HTTPS 通訊,以免遭到"man-in-the-middle"攻擊。
請務必篩選出惡意網頁。舉例來說,下列模式很危險:
var data = document.getElementById("json-data")
// WARNING! Might be evaluating an evil script!
var parsed = eval("(" + data + ")")
var elmt_id = ...
// WARNING! elmt_id might be "); ... evil script ... //"!
window.setTimeout("animate(" + elmt_id + ")", 200);
請改用不會執行指令碼的較安全 API:
var data = document.getElementById("json-data")
// JSON.parse does not evaluate the attacker's scripts.
var parsed = JSON.parse(data);
var elmt_id = ...
// The closure form of setTimeout does not evaluate scripts.
window.setTimeout(function() {
animate(elmt_id);
}, 200);