內容指令碼是在網頁內容中執行的檔案,只要使用標準文件物件模型 (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 } |
boolean | 選用。指令碼是否應插入 about:blank 頁框中,且父項或開啟器頁框與 matches 中宣告的其中一個模式相符。預設值為 false 。 |
排除相符項目和 glob
在資訊清單註冊作業中加入下列欄位即可自訂指定頁面比對功能。
名稱 | 類型 | 說明 |
---|---|---|
exclude_matches {: #Exclude_matches } |
選用。排除原本會插入這個內容指令碼的網頁。如要進一步瞭解這些字串的語法,請參閱比對模式。 | |
include_globs {: #include_globs } |
選用。將在 matches 後套用,僅納入與這個 glob 相符的網址。意圖模擬 @include Greasemonkey 關鍵字。 |
|
exclude_globs {: #Exclude_globs } |
選用。套用在 matches 之後,用於排除與這個 glob 相符的網址。意圖模擬「Greasemonkey」關鍵字。@exclude |
如果網址符合任何 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:// .example.com/foo /
但「不」符合以下條件:
- http:// .example.com/foo/bar
- http:// 範例 .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"]
}
],
...
}
執行時間
JavaScript 檔案插入網頁中時,是由 run_at
欄位控制。偏好欄位和預設欄位為 "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 } |
boolean | 選用。預設值為 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);