コンテンツ スクリプト

コンテンツ スクリプトは、ウェブページのコンテキストで実行されるファイルです。標準のドキュメント オブジェクト モデル(DOM)を使用すると、ブラウザがアクセスしたウェブページの詳細を読み取り、変更を加え、親拡張機能に情報を渡すことができます。

コンテンツ スクリプトの機能について

コンテンツ スクリプトは、拡張機能とメッセージを交換することで、親拡張機能で使用されている Chrome API にアクセスできます。拡張機能のファイルの URL には、 chrome.runtime.getURL() に変換し、結果を他の URL と同じように使用します。

// 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 } 文字列の配列 必須。このコンテンツ スクリプトを挿入するページを指定します。これらの文字列の構文の詳細については一致パターンを、URL を除外する方法については一致パターンと glob をご覧ください。
css {: #css } 文字列の配列 省略可。一致するページに挿入される CSS ファイルのリストです。これらは、この配列に出現する順序で挿入され、その前にページの DOM が構築または表示されます。
js {: #js } 文字列の配列 省略可。一致するページに挿入される JavaScript ファイルのリスト。これらは、この配列に表示されている順序で挿入されます。
match_about_blank {: #match_about_blank } ブール値 省略可。親フレームまたはオープン フレームが matches で宣言されたパターンのいずれかに一致する about:blank フレームにスクリプトを挿入するかどうか。デフォルトは false です。

一致と glob を除外する

指定されたページ マッチングは、マニフェスト登録に次のフィールドを含めることでカスタマイズできます。

名前 説明
exclude_matches {: #exclude_matches } 文字列の配列 省略可。このコンテンツ スクリプトが挿入されるページは除外されます。これらの文字列の構文の詳細については、一致パターンをご覧ください。
include_globs {: #include_globs } 文字列の配列 省略可。matches の後に適用され、このグロブにも一致する URL のみが含まれます。@include Greasemonkey キーワードをエミュレートすることを目的としています。
exclude_globs {: #exclude_globs } 文字列の配列 省略可。matches の後に適用され、このグロブに一致する URL を除外します。@exclude Greasemonkey キーワードをエミュレートすることを目的としています。

コンテンツ スクリプトは、URL が matches パターンと include_globs パターンのいずれかに一致し、exclude_matches パターンまたは exclude_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 文字列は、「ワイルドカード」を含む可能性のある URL です。使用しないでください。アスタリスク * は、空の文字列を含む任意の長さの文字列に一致します。疑問符 ? は、次と一致 使用できます。

たとえば、グロブ http:// ??? .example.com/foo/ * は、次のいずれかに一致します。

  • http:// www .example.com/foo /bar
  • http:// .example.com/foo /

ただし、以下には一致しません

  • http:// my .example.com/foo/bar
  • http:// example .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:// history .nytimes.comhttp://.nytimes.com/ history にコンテンツ スクリプトを挿入しますが、http:// science .nytimes.comhttp://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 ファイルを使用するかどうかを拡張機能で指定できます。 指定された URL 要件に一致するすべてのフレームに挿入されるか、 タブ

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "all_frames": true,
      "js": ["contentScript.js"]
    }
  ],
  ...
}
名前 説明
all_frames {: #all_frames } ブール値 省略可。デフォルトは false で、最上位のフレームのみが照合されます。

true を指定すると、タブの最上位のフレームでなくても、すべてのフレームに挿入されます。各フレームは個別に URL 要件についてチェックされ、URL 要件を満たしていない場合は子フレームに挿入されません。

エンベディング ページとの通信

コンテンツ スクリプトの実行環境と、そのスクリプトをホストするページの実行環境は互いに分離されていますが、ページの 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 経由でのみ行ってください。 &quot;man-in-the-middle&quot;攻撃。

悪意のあるウェブページをフィルタするようにしてください。たとえば、次のパターンは危険です。

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