コンテンツ スクリプトは、ウェブページのコンテキストで実行されるファイルです。標準のドキュメント オブジェクト モデル(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 権限を指定します。これにより、アクティブなサイトのホストへの安全なアクセスと、タブへの一時的なアクセス権が付与され、クロスオリジン アクセス権を指定せずに、現在のアクティブなタブでコンテンツ スクリプトを実行できるようになります。
{
"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 を除外する方法については、一致パターンとグロブをご覧ください。 | |
css {: #css } |
省略可。一致するページに挿入する CSS ファイルのリスト。これらの要素は、ページの DOM が作成または表示される前に、この配列に表示される順序で挿入されます。 | |
js {: #js } |
省略可。一致するページに挿入する JavaScript ファイルのリスト。これらは、この配列に表示されている順序で挿入されます。 | |
match_about_blank {: #match_about_blank } |
ブール値 | 省略可。親フレームまたはオープン フレームが matches で宣言されたパターンのいずれかに一致する about:blank フレームにスクリプトを挿入するかどうか。デフォルトは false です。 |
一致とグロブを除外する
指定されたページの一致は、マニフェスト登録に次のフィールドを含めることでカスタマイズできます。
名前 | 型 | 説明 |
---|---|---|
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_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 プロパティの構文は、一致パターンとは異なり、より柔軟です。使用できるグロブ文字列は、「ワイルドカード」のアスタリスクと疑問符を含む URL です。アスタリスク * は、空の文字列を含む任意の長さの文字列に一致します。疑問符 ? は、任意の 1 文字に一致します。
たとえば、グロブ 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"]
}
],
...
}
実行日時
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 の作成など)、コンテンツを挿入する前に、コンテンツのクロスサイト スクリプティング攻撃をフィルタするように注意してください。"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);