內容指令碼

內容指令碼是在網頁環境下執行的檔案,使用標準文件 物件模型 (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 相符的網址。旨在模擬 @exclude Greasemonkey 關鍵字。

如果網址符合任何 matches 模式和任何 include_globs 模式,且不符合 exclude_matchesexclude_globs 模式,系統就會將內容指令碼插入網頁。

由於 matches 屬性為必填,exclude_matchesinclude_globsexclude_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:// 我的 .example.com/foo/bar
  • http:// 範例 .com/foo/
  • http://www.example.com/foo

這個擴充功能會將內容指令碼插入 http:/www.nytimes.com/arts/index.htmlhttp://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:// 記錄 .nytimes.comhttp://.nytimes.com/ history 而非 http:// Science .nytimes.comhttp://www.nytimes.com/ 科學

{
  "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 進行通訊,以免遭受「中間人」攻擊。

請務必篩除惡意網頁。舉例來說,以下模式屬於危險模式:

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);