コンテンツ スクリプトは、ウェブページのコンテキストで実行されるファイルです。開発者は、標準のドキュメント オブジェクト モデル(DOM)を使用して、ブラウザがアクセスしたウェブページの詳細を読み取って変更を加え、親拡張機能に情報を渡すことができます。
コンテンツ スクリプトの機能を理解する
コンテンツ スクリプトは、拡張機能とメッセージを交換することで、親拡張機能が使用する Chrome API にアクセスできます。また、chrome.runtime.getURL()
を使用して拡張機能のファイルの URL にアクセスし、その結果を他の 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 権限を指定します。これにより、アクティブなサイトのホストへの安全なアクセスと、tabs 権限への一時的なアクセス権が付与されます。クロスオリジン権限を指定せずに、現在のアクティブなタブでコンテンツ スクリプトを実行できるようになります。
{
"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 {: #試合 } |
必須: このコンテンツ スクリプトを挿入するページを指定します。文字列の構文について詳しくは一致パターンを、URL を除外する方法については一致パターンと glob をご覧ください。 | |
css {: #css } |
省略可。一致するページに挿入する CSS ファイルのリストです。ページの DOM の作成または表示の前に、この配列に出現する順序で挿入されます。 | |
js {: #js } |
省略可。一致するページに挿入する JavaScript ファイルのリストです。これらは、この配列に表示される順序で挿入されます。 | |
match_about_blank {: #match_about_blank } |
boolean | 省略可。親フレームまたはオープナー フレームが matches で宣言されたパターンのいずれかに一致する about:blank フレームにスクリプトを挿入する必要があるかどうか。デフォルトは false です。 |
一致と glob を除外する
指定したページの照合は、マニフェスト登録に次のフィールドを含めることでカスタマイズできます。
名前 | 型 | 説明 |
---|---|---|
exclude_matches {: #exclude_matches } |
省略可。このコンテンツのスクリプトが挿入されるページは除外されます。これらの文字列の構文の詳細については、一致パターンをご覧ください。 | |
include_globs {: #include_globs } |
省略可。この glob にも一致する URL のみを含めるために matches より後に適用されます。@include Greasemonkey キーワードをエミュレートすることを目的としています。 |
|
exclude_globs {: #exclude_globs } |
省略可。この glob に一致する URL を除外するために、matches の後に適用されます。@exclude Greasemonkey キーワードをエミュレートすることを目的としています。 |
URL が 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 文字列は、「ワイルドカード」のアスタリスクや疑問符を含む URL です。アスタリスク * は、空の文字列を含む任意の長さの任意の文字列に一致しますが、疑問符の ?任意の 1 文字に一致します。
たとえば、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"]
}
],
...
}
適切な範囲を実現するために、これらのうちの 1 つ、すべて、または一部を含めることができます。
{
"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 } |
string | 推奨:可能な限り "document_idle" を使用してください。ブラウザは、 "document_end" と windowonload イベントが発生した直後にスクリプトを挿入する時間を選択します。挿入の正確なタイミングは、ドキュメントの複雑さと読み込みにかかる時間によって異なり、ページの読み込み速度に合わせて最適化されます。"document_idle" で実行されるコンテンツ スクリプトは、window.onload イベントをリッスンする必要はなく、DOM の完了後に実行されます。window.onload の後にスクリプトを実行する必要がある場合、拡張機能は document.readyState プロパティを使用して、onload がすでに呼び出されているかどうかを確認できます。 |
document_start {: #document_start } |
string | スクリプトは、css からのファイルの後、他の DOM の構築や他のスクリプトの実行前に挿入されます。 |
document_end {: #document_end } |
string | スクリプトは、DOM の完了直後、画像やフレームなどのサブリソースの読み込み前に挿入されます。 |
フレームを指定する
この拡張機能では、"all_frames"
フィールドを使用して、指定した URL 要件に一致するすべてのフレームに JavaScript ファイルと CSS ファイルを挿入するか、タブの一番上のフレームのみに挿入するかを指定できます。
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["http://*.nytimes.com/*"],
"all_frames": true,
"js": ["contentScript.js"]
}
],
...
}
名前 | 型 | 説明 |
---|---|---|
all_frames {: #all_frames } |
boolean | 省略可。デフォルトは 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 を実行するなど、コンテンツ スクリプトが別のウェブサイトからコンテンツを受け取る場合は、挿入する前にコンテンツのクロスサイト スクリプティング攻撃をフィルタリングしてください。"man-in-the-middle"を回避するため、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);