拡張機能の一般的な事例の1つは Web ページを変更することです。例えば、ページのスタイルを変更、特定の DOM ノードを隠す、別の DOM ノードをページに挿入する、といいでしょう。

WebExtensions API での実現方法は2つあります:

  • 手動で定義する: URLに一致するパターンを定義し、そのURLが一致するページにスクリプトを読み込まれるようにします。
  • 自動で行う: JavaScript API を使い、特定のタブでホストされているページにスクリプトを読み込まれるようにします。

どちらの方法のスクリプトも content scripts と呼ばれ、拡張機能を構成する他のスクリプトとは異なります:

  • WebExtension API の一部のサブセットのみにアクセスできます。
  • それらは読み込まれた Web ページに直接アクセスできます。
  • messaging API を使い、拡張機能の残りの部分と対話できます。

In this article we'll look at both methods of loading a script.

この記事ではスクリプトを読み込むそれぞれの方法について説明します。

URL パターンにマッチしたページを変更する

まず始めに、 "modify-page" という新しいディレクトリを作成します。このディレクトリで "manifest.json" というファイルを作成し、以下のように記述します。

{

  "manifest_version": 2,
  "name": "modify-page",
  "version": "1.0",

  "content_scripts": [
    {
      "matches": ["https://developer.mozilla.org/*"],
      "js": ["page-eater.js"]
    }
  ]

}

content_scripts キーはURLパターンと一致するページにスクリプトを読み込む方法です。この場合、 content_scripts https://developer.mozilla.org/ 以下のすべてのページで "page-eater.js" というスクリプトをロードするようにブラウザに指示します。

content_scripts"js" プロパティ は配列なので、マッチしてるページに複数のスクリプトを挿入することができます。これを行うと、ページによってロードされるいくつかのスクリプトと同じように、ページは同じスコープを共有し、配列にリストされている順序でロードされます。

content_scripts キーでは "css" プロパティで CSS スタイルシートを挿入することもできます。

次に、 "page-eater.js" というファイルを "modify-page" ディレクトリ内に作り、以下のように記述します。

document.body.textContent = "";

var header = document.createElement('h1');
header.textContent = "This page has been eaten";
document.body.appendChild(header);

拡張機能をインストール して https://developer.mozilla.org/ を訪れてみましょう。

このビデオでは addons.mozilla.org で動作するコンテンツスクリプトを示していますが、現在このサイトではコンテンツスクリプトはブロックされています。

自動でページを変更する

ユーザーがあなたに質問してきたとき、まだページを処理している場合どうしたらいいですか? この例を更新して、ユーザーがコンテキストメニュー項目をクリックしたときにコンテンツスクリプトを挿入しましょう。

始めに、 "manifest.json" を以下のように更新してください。

{

  "manifest_version": 2,
  "name": "modify-page",
  "version": "1.0",

  "permissions": [
    "activeTab",
    "contextMenus"
  ],

  "background": {
    "scripts": ["background.js"]
  }

}

これは content_scripts キーを削除し、新たに2つのキーを追加しました。

  • permissions: スクリプトをページに挿入するには、変更するページへの権限が必要です。 activeTab パーミッションは現在アクティブなタブへの一時的な権限を取得する方法です。コンテキストメニューに項目を追加するには contextMenus パーミッションも必要となります。
  • background: "バックグラウンドスクリプト" という "background.js" を永続的に読み込み、ここでコンテキストメニューを設定し、コンテンツスクリプトを挿入します。

このファイルを作りましょう。"background.js" というファイルを "modify-page" ディレクトリ内に作り以下のように記述します。

browser.contextMenus.create({
  id: "eat-page",
  title: "Eat this page"
});

browser.contextMenus.onClicked.addListener(function(info, tab) {
  if (info.menuItemId == "eat-page") {
    browser.tabs.executeScript({
      file: "page-eater.js"
    });
  }
});

このスクリプトでは context menu itemを作成し、特定の id とタイトルを指定します。(コンテキストメニューに表示するテキスト) 次に、イベントリスナーを設定して、ユーザーがコンテキストメニュー項目をクリックしたときに、それが eat-page 項目であるかどうかをチェックします。それが正しければ、 tabs.executeScript() API を利用して、 "page-eater.js" を挿入します。この API はオプションでタブIDを引数として取ります、よってタブIDは省略されています。つまり、スクリプトは現在アクティブなタブに挿入されています。

この時点で拡張機能は以下のようになっています。

modify-page/
    background.js
    manifest.json
    page-eater.js

拡張機能を再読み込みして、ページを開きます (任意のページ)  コンテキストメニューを有効化し、 "Eat this page" を選択します。

このビデオでは addons.mozilla.org で動作するコンテンツスクリプトを示していますが、現在このサイトではコンテンツスクリプトはブロックされています。

Messaging

Content scripts と background scripts はお互いの状態に直接アクセスすることはできません。しかし、メッセージを送ることによる対話をすることができます。一方のエンドはメッセージリスナーを設定し、もう一方のエンドはメッセージを送信します。 次の表は、各側面に関連するAPIをまとめたものです。

  In content script In background script
Send a message browser.runtime.sendMessage() browser.tabs.sendMessage()
Receive a message browser.runtime.onMessage browser.runtime.onMessage

例を更新して、バックグラウンドスクリプトからメッセージを送信する方法を示します。

始めに "background.js" を編集して、次のようにします。

browser.contextMenus.create({
  id: "eat-page",
  title: "Eat this page"
});

function messageTab(tabs) {
  browser.tabs.sendMessage(tabs[0].id, {
    replacement: "Message from the extension!"
  });
}

function onExecuted(result) {
    var querying = browser.tabs.query({
        active: true,
        currentWindow: true
    });
    querying.then(messageTab);
}

browser.contextMenus.onClicked.addListener(function(info, tab) {
  if (info.menuItemId == "eat-page") {
    let executing = browser.tabs.executeScript({
      file: "page-eater.js"
    });
    executing.then(onExecuted);
  }
});

次に、 "page-eater.js" を挿入し、 tabs.query() を使用し、現在アクティブなタブを取得し、 tabs.sendMessage() を使用し、そのタブにロードされたコンテンツスクリプトにメッセージを送信します。 メッセージにはペイロード {replacement: "Message from the extension!"} があります。

次に "page-eater.js" を次のように更新します。

function eatPageReceiver(request, sender, sendResponse) {
  document.body.textContent = "";
  var header = document.createElement('h1');
  header.textContent = request.replacement;
  document.body.appendChild(header);
}
browser.runtime.onMessage.addListener(eatPageReceiver);

今すぐページを処理する代わりに、コンテンツスクリプトは runtime.onMessageを使ってメッセージを取得します。 メッセージが到着すると、コンテンツスクリプトは前と同じコードを実行しますが、置換テキストは request.replacement から取得されます。

tabs.executeScript() は非同期関数であり、リスナーが "page-eater.js" に追加された後にのみメッセージを送信するために、 "page-eater.js" を実行した後に呼び出される onExecuted を使用します。

Ctrl+Shift+J (Mac では Cmd+Shift+J) を押します。もしくは web-ext run --bcBrowser Console を開きバックグラウンドスクリプトの console.log を見ます。 または、 Add-on Debugger  を使用して、ブレークポイントを設定することもできます。 現在、web-ext から 直接 Add-on Debugger を起動する 方法はありません。

コンテンツスクリプトからバックグラウンドページにメッセージを戻したい場合は、 runtime.sendMessage() の代わりに tabs.sendMessage() を使用します

例:

browser.runtime.sendMessage({
    title: "from page-eater.js"
});
これらの例はすべて JavaScript を注入します。 tabs.insertCSS() 関数を使用してプログラムで CSS を挿入することもできます。

関連情報

 

ドキュメントのタグと貢献者

タグ: 
このページの貢献者: WhiteHawk-taka
最終更新者: WhiteHawk-taka,