国際化 - Mozilla

国際化
===

WebExtensions API には、拡張機能を国際化するとても簡単なモジュール — i18n があります。この記事ではその機能を見てみて、どのように動作するのかの実例を提供します。WebExtensions API を使った拡張機能用の i18n システムは、i18n.js のような、よくある i10n 用 JavaScript ライブラリーと同様です。

**メモ:** この記事の見本の拡張機能— notify-link-clicks-i18n — は GitHub で入手できます。以下の節を進んでいくのに合わせてコードを追ってください。

国際化拡張機能の中身
----------

国際化された拡張機能は、他の拡張機能と同じ機能 — バックグラウンドスクリプト、コンテンツスクリプトなど — を含む他、さまざまなロケールに切り替えられるような追加の部分もあります。これは下記のディレクトリーツリーのように要約されます:

* extension-root-directory/
    + \_locales
        - en
            * messages.json
                + 英語メッセージ (文字列)
        - de
            * messages.json
                + ドイツ語メッセージ (文字列)
        - etc.
    + manifest.json
        - ロケール依存のメタデータ
    + myJavascript.js
        - ブラウザーロケールや、ロケール固有メッセージなどを取得する JavaScript
    + myStyles.css
        - ロケール依存の CSS

それぞれの新機能を順に見ていきましょう— 以下の節は拡張機能を国際化するときのそれぞれのステップを表しています。

\_locales 内にローカライズ済み文字列を提供する
----------------------------

言語のサブタグを探すには、Language subtag lookup page の検索ツールを使います。注意点としては言語の名前の英語で探す必要があります。

すべての i18n システムはサポートする全ロケールに翻訳済みの文字列が要ります。拡張機能では、これは拡張機能のルート内に置かれる `_locales` と呼ばれるディレクトリーに入っています。各ロケールはそれぞれの文字列 (メッセージとも呼ばれる) を `messages.json` というファイル内に入れていて、これは `_locales` のサブディレクトリー内に置かれており、そこはロケール言語の言語サブタグを使った名前になっています。

サブタグには基本的な言語に加えて、地域の変化形があるのに注意してください。ここで言語と変化形は伝統的に "en-US" のようにハイフンで区切られます。しかし、`_locales` 内のディレクトリーでは、**区切り文字はアンダースコアでなければならず**、 "en\_US" となります。

例えば、見本のアプリでは "en" (英語), "de" (ドイツ語), "nl" (オランダ語), "ja" (日本語) のフォルダーがあります。それぞれの中に `messages.json` ファイルがあります。

このファイル (\_locales/en/messages.json) の構造を見てみます。

```json
{
  "extensionName": {
    "message": "Notify link clicks i18n",
    "description": "Name of the extension."
  },

  "extensionDescription": {
    "message": "Shows a notification when the user clicks on links.",
    "description": "Description of the extension."
  },

  "notificationTitle": {
    "message": "Click notification",
    "description": "Title of the click notification."
  },

  "notificationContent": {
    "message": "You clicked $URL$.",
    "description": "Tells the user which link they clicked.",
    "placeholders": {
      "url": {
        "content": "$1",
        "example": "https://developer.mozilla.org"
      }
    }
  }
}
```

このファイルは標準の JSON — メンバーがそれぞれに `message` と `description`. を含む名前付きオブジェクトです。すべての項目が文字列です; `$URL$` はプレースホルダーで、拡張機能から呼ばれる `notificationContent` メンバーに置き換えられます。マニフェスト内のローカライズ済み文字列を取得するの節でその方法を学びます。

**メモ:** `messages.json` ファイルの中身についての詳しい情報はロケール固有のメッセージリファレンスにあります。

manifest.json を国際化する
--------------------

manifest.json の国際化を実行するにはいくつかの異なるタスクがあります。

### マニフェスト内のローカライズ済み文字列を取得する

manifest.json にはユーザーに表示される文字列が入っています、例えば拡張機能の名前や説明です。この文字列を国際化して messages.json に適切な訳を置くと、現在のロケールなどに基づき、正しく翻訳された文字列がユーザーに表示されます。

文字列を国際化するには、このように指定します。

```json
"name": "\_\_MSG\_extensionName\_\_",
"description": "\_\_MSG\_extensionDescription\_\_",
```

ここで、単なる固定文字列ではなく、ブラウザーのロケールに依存しないメッセージ文字列を取得します。

このようなメッセージ文字列を呼び出すには、次のように指定します。

1. 2 つのアンダースコアに続いて
2. "MSG" という文字列に続いて
3. 1 つのアンダースコアに続いて
4. `messages.json` で定義した呼び出しのメッセージ名に続いて
5. 2 つのアンダースコア
```

__MSG_ + messageName + __
```

### デフォルトロケールを指定する

manifest.json にて指定すべきフィールドは default\_locale です。

```json
"default\_locale": "en"
```

これは拡張機能がブラウザーの現在のロケールに対するロケール文字列を含んでいない場合のデフォルトロケールを指定します。ブラウザーロケールで利用できないあらゆるメッセージ文字列が代わりのデフォルトロケールとして引受られます。ブラウザーが文字列を選ぶ方法に関して意識すべき詳細な点があります — ローカライズ済みの文字列の選択 を見てください。

ロケールに依存しない CSS
--------------

拡張機能の CSS ファイルからもローカライズされた文字列を取得できます。例えば、ロケールに依存しない CSS を組み立てるなら、このようにします:

```css
header {
  background-image: url(../images/\_\_MSG\_extensionName\_\_/header.png);
}
```

事前定義されたメッセージを使ってこんな状況を扱う方がより良いものの、これは便利です。

JavaScript からメッセージ文字列を取得する
--------------------------

それで、メッセージ文字列とマニフェストがセットアップできました。今は単に JavaScript からメッセージ文字列を呼び出して、拡張機能ができるだけ多くの正しい言語を話すようにします。実際の i18n API はとてもシンプルで、そこには 4 つのメソッドがあります。

* たぶん一番多く `i18n.getMessage()` を使うでしょう。これは上記に触れられたとおり、特定の文字列を取得するのに使うメソッドです。これの使用例は下記で見ます。
* `i18n.getAcceptLanguages()` と`i18n.getUILanguage()` メソッドはロケールに応じて UI をカスタマイズする場合に使います。たぶんユーザーの好みの言語に応じて設定リストの上の方に表示したり、特定言語だけに関連する文化的情報を表示したり、ブラウザーロケールに従って日付表示を整形したりすると良いでしょう。
* `i18n.detectLanguage()` メソッドはユーザーが投稿したコンテンツの言語を特定したり、それを適切に整形したりするのに使います。

notify-link-clicks-i18n の見本の中で、バックグラウンドスクリプトに下記の行があります。

```js
let title = browser.i18n.getMessage("notificationTitle");
let content = browser.i18n.getMessage("notificationContent", message.url);
```

最初は単に `messages.json` ファイルからブラウザーの現ロケールのための `notificationTitle message` 項目を取得します。2 つ目は同様ですが、2 つ目の引数に URL を渡されます。これは何でしょう？これは `notificationContent message` 項目の中の `$URL$` プレースホルダーを置き換えるためにコンテンツを指定する方法です:

```json
"notificationContent": {
  "message": "You clicked $URL$.",
  "description": "Tells the user which link they clicked.",
  "placeholders": {
    "url" : {
      "content" : "$1",
      "example" : "https://developer.mozilla.org"
    }
  }
}
```

`"placeholders"` メンバーはプレースホルダーを定義し、ここから取得します。`"url"` プレースホルダーは $1、つまり 2 つ目の引数の `getMessage()`.の最初の値から取ってきたコンテンツを指定します。プレースホルダーは`"url"` と呼ばれるため、実際のメッセージ文字列の中で呼び出すのに `$URL$` を使います (なので `"name"` には `$NAME$`などを使う)。複数のプレースホルダーがある場合、`i18n.getMessage()` の 2 つ目の引数として渡す配列の中で提供できます — `messages.json`内部で`[a, b, c]` は`$1`, `$2`, `$3`, として使われる、など。

例をざっと見ましょう。 `en/messages.json` ファイル内のオリジナルの `notificationContent` メッセージ文字列はこうです
```

You clicked $URL$.
```

`https://developer.mozilla.org` を指すリンクがクリックされたとします。`i18n.getMessage()` を呼び出した後、2 つ目の引数のコンテンツは messages.json 内で `$1` として利用できて、これは `"url"` プレースホルダー内で定義された `$URL$` プレースホルダーを置換します。よって最終のメッセージ文字列はこうなります
```

You clicked https://developer.mozilla.org.
```

### 直接プレースホルダーを使う

メッセージ文字列の中に直接に変数 (`$1`, `$2`, `$3` など) を挿入できます。例えば上記の `"notificationContent"` メンバーをこう書き換えてみます。

```json
"notificationContent": {
  "message": "You clicked $1.",
  "description": "Tells the user which link they clicked."
}
```

これはややこしくなくて速いように見えますが、他の方法 (`"プレースホルダー"`を使う)が最も良いやり方です。これはプレースホルダー名 (例えば`"url"`) や例によって、プレースホルダーの目的を思い出せます — コードを書いた 1 週間後には、`$1`–`$8` が何を指すか忘れているでしょうが、プレースホルダー名が何を指すかならもっと思い出しやすいでしょう。

### ハードコーディングされた置き換え

プレースホルダーにハードコードされた文字列を入れることもできます、これでコード内の変数の代わりに、いつでも同じ値が使われます。例えば、

```json
"mdn\_banner": {
  "message": "For more information on web technologies, go to $MDN$.",
  "description": "Tell the user about MDN",
  "placeholders": {
    "mdn": {
      "content": "https://developer.mozilla.org/"
    }
  }
}
```

ここでは、`$1` といった変数から取るのではなく、プレースホルダーの content をハードコードしています。これはメッセージが複雑で、ファイル内で読みやすくするために色々な値に分割したい時に役立ちますし、さらにこの値はプログラム的にアクセスすることもできます。

それに加えて、個人やビジネス名といった翻訳したくない文字列を、このような置き換えから指定するのに使うことができます。

ローカライズ済みの文字列の選択
---------------

ロケールは `fr` や `en` のような言語コードによってのみ指定されるか、さらに `en_US` や `en_GB` といった地域コードつきで (これは同じ言語の地域的な変形を記述します) 認証されます。i18n に文字列を問い合わせた時、次のアルゴリズムで文字列を選択します。

1. 現在のロケールと全く同じ `messages.json` ファイルがあって、そこに文字列が入っている場合、それを返します
2. それ以外で、現在のロケールが地域つきで認証されて（例えば `en_US`）、そのロケールの地域になし版の `messages.json` ファイルがあって（例えば `en`）、そこに文字列が入っている場合、それを返します
3. それ以外で、`manifest.json` 内に定義された `default_locale` 用の `messages.json` ファイルがあって、そこに文字列が入っている場合、それを返します
4. それ以外は空文字を返します

下記の例を見てみます。

* extension-root-directory/
    + \_locales
        - en\_GB
            * messages.json
                + `{ "colorLocalized": { "message": "colour", "description": "Color." }, /* … */ }`en
            * messages.json
                + `{ "colorLocalized": { "message": "color", "description": "Color." }, /* … */ }`
        - fr
            * messages.json
                + `{ "colorLocalized": { "message": "couleur", "description": "Color." }, /* … */}`

`default_locale` が `fr` にセットされ、ブラウザーの現在のロケールが `en_GB` と仮定します:

* 拡張機能が `getMessage("colorLocalized")`を呼び出すと、 "colour" を返します
* `en_GB` に "colorLocalized" がなかったとしたら、 `getMessage("colorLocalized")` は "couleur" でなく "color" を返します
事前定義されたメッセージ
------------

i18n モジュールでは、事前定義されたメッセージを提供しており、これまで見てきたマニフェスト内のローカライズ済み文字列を取得するやロケールに依存しない CSSと同じ方法で呼び出すことができます。例えばこのようにします。
```

__MSG_extensionName__
```

事前定義されたメッセージでも全く同じ文法を使っていますが、メッセージ名の前に `@@` をつけるのが異なります。例えば、
```

__MSG_@@ui_locale__
```

下記の表は利用可能なさまざまな事前定義されたメッセージを示しています。

| メッセージ名 | 説明 |
| --- | --- |
| `@@extension_id` | 拡張機能の内部生成された UUID。この文字列は拡張機能内のリソースの URL を作るのに使います。ローカライズされた拡張機能でもこのメッセージを使用できます。
このメッセージをマニフェストファイル内で使用することはできません。

 もう一つの注意点として、この ID は `runtime.id` (en-US) から返される、manifest.json 内の
 applications
 キーを用いてセットされるアドオン ID とは異なっています。これはアドオンの URL 内にある生成された UUID です。つまりこの値を `runtime.sendMessage()` の `extensionId` 引数の値として使うことはできず、`runtime.MessageSender` オブジェクトの `id` プロパティの値のチェックに使うこともできません。
  |
| `@@ui_locale` | 現在のロケールで、この文字列をロケール固有の URL 生成に使うことができます。 |
| `@@bidi_dir` | 現在のロケールにおけるテキストの向きで、英語のような左書きの言語では "ltr" で、アラビア語のような右書きの言語では "rtl" となり、このいずれかです。 |
| `@@bidi_reversed_dir` | `@@bidi_dir` が "ltr" なら "rtl" で、そうでなれば "ltr" です。 |
| `@@bidi_start_edge` | `@@bidi_dir` が "ltr" なら "left" で、そうでなれば "right" です。 |
| `@@bidi_end_edge` | `@@bidi_dir` が "ltr" なら "right" で、そうでなれば "left" です。 |

前の例に戻って、次のように書いてみるともっと意味がわかります。

```css
header {
  background-image: url(../images/\_\_MSG\_@@ui\_locale\_\_/header.png);
}
```

サポートしているロケール (en, de など) に一致したディレクトリー内にロケール固有の画像を保管しているとすると、もっとわかり良いです。

CSS ファイル内で `@@bidi_*` メッセージを使った次の例を見てみましょう。

```css
body {
  direction: __MSG_@@bidi\_dir\_\_;
}

div#header {
  margin-bottom: 1.05em;
  overflow: hidden;
  padding-bottom: 1.5em;
  padding-__MSG_@@bidi\_start\_edge\_\_: 0;
  padding-__MSG_@@bidi\_end\_edge\_\_: 1.5em;
  position: relative;
}
```

英語のような左から右への言語では、上の事前定義されたメッセージを含んだ CSS 定義は、下記の最終コード行に変換されます。

```css
direction: ltr;
padding-left: 0;
padding-right: 1.5em;
```

アラビア語のような右から左への言語では、次のようになります。

```css
direction: rtl;
padding-right: 0;
padding-left: 1.5em;
```

拡張機能をテストする
----------

拡張機能のローカライズをテストするには、言語パックをインストールすることができる Firefox または Firefox Beta を使用します。

次に、テストしたい拡張機能で対応している各ロケールについて、 Firefox の UI 言語を切り替えるために、別の言語で Firefox を使用するための指示に従います。（設定を使用した場合、「言語」の下にある「代替言語の設定」を使用します。）

Firefoxがテスト言語で実行されたら、拡張機能を一時的にインストールします。拡張機能をインストールした後、 `about:debugging` で、拡張機能を正しく設定した場合、選択した言語で拡張機能がアイコン、名前、説明とともに掲載されているのが確認できます。ローカライズされた拡張機能の詳細は、 `about:addons` でも見ることができます。これで拡張機能の機能を使い、必要な翻訳が配置されていることを確認してください。

このプロセスを試したい場合は、notify-link-clicks-i18n 拡張機能を使用することができます。この例で対応している言語（ドイツ語、オランダ語、日本語）のいずれかを表示するように、 Firefox を設定してください。拡張機能を読み込んで、あるウェブサイトにアクセスします。リンクをクリックすると、リンクの URL を報告する通知の翻訳版が表示されます。


JavaScript API 群のブラウザーの互換性 - Mozilla

JavaScript API 群のブラウザーの互換性
==========================

action
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

alarms
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

bookmarks
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

browserAction
-------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

browserSettings
---------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

browsingData
------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

captivePortal
-------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

clipboard
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

commands
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

contentScripts
--------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

contextualIdentities
--------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

cookies
-------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

declarativeNetRequest
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

devtools
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

dns
---

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

dom
---

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

downloads
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

events
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

extension
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

extensionTypes
--------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

find
----

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

history
-------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

i18n
----

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

identity
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

idle
----

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

management
----------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

menus
-----

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

notifications
-------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

omnibox
-------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

pageAction
----------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

permissions
-----------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

pkcs11
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

privacy
-------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

proxy
-----

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

runtime
-------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

scripting
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

search
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

sessions
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

sidebarAction
-------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

storage
-------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

tabs
----

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

theme
-----

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

topSites
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

types
-----

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

userScripts
-----------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

webNavigation
-------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

webRequest
----------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

windows
-------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

**メモ:** Microsoft Edge の実装状況は Microsoft Corporation から提供されたものであり、ここでは Creative Commons Attribution 3.0 United States License に従っています。

関連情報
----

* Browser compatibility for manifest.json


Content Security Policy - Mozilla

Content Security Policy
=======================

WebExtension APIs で開発される拡張機能には、既定で適用される CSP(Content Security Policy の略) があります。これは <script> と <object> リソースから読み込まれるソースを制限し、危険な行動、例えば `eval()` の使用を非許可にします。

この記事では CSP とは何かと、デフォルトポリシーとは何で拡張機能にはどんな意味があるのかと、拡張機能が既定の CSP を変更する方法を簡単に説明します。

Content Security Policy (CSP) はウェブサイトが悪意のあるコンテンツを実行するのを防ぐのに役立つメカニズムです。ウェブサイトは サーバーから送られてくる HTTP ヘッダーを使って CSP を指定します。CSP は主に、スクリプトや組み込みプラグインといったさまざまな種類のコンテンツの合法なソースを特定することに関心を持っています。例えば、ウェブサイトは、ウェブサイト自身からの JavaScript だけを実行し、他のソースは受け付けないように指定できます。CSP はブラウザーに `eval()`のような、潜在的に危険な行動を禁止するよう指示することもできます。

ウェブサイトと同様に、拡張機能はさまざまなソースからコンテンツを読み込めます。例えば、ブラウザーアクションのポップアップは HTML 文書として指定できて、通常のウェブページのようにさまざまなソースからの JavaScript と CSS を入れることができます:

```html
<!doctype html>

<html>
  <head>
    <meta charset="utf-8" />
  </head>

  <body>
    <!--Some HTML content here-->

    <!--
 Include a third-party script.
 See also https://developer.mozilla.org/ja/docs/Web/Security/Subresource\_Integrity.
 -->
    <script
 src="https://code.jquery.com/jquery-2.2.4.js"
 integrity="sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI="
 crossorigin="anonymous"></script>

    <!-- Include my popup's own script-->
    <script src="popup.js"></script>
  </body>
</html>
```

ウェブサイトと比較して、拡張機能は追加の特権付き API にアクセスできるので、悪意のあるコードに感染した場合、リスクは大きくなります。このため:

* かなり厳密な CSP がデフォルトで適用されます。既定の content security policy を見てください
* 拡張機能のオーナーは `content_security_policy` manifest.json キーを使ってデフォルトのポリシーを変更できますが、許可できるポリシーには制限があります。`content_security_policy` を見てください。
既定の content security policy
---------------------------

拡張機能の既定の CSP は次のものです:
```

"script-src 'self'; object-src 'self';"
```

これは、`content_security_policy` の manifest.json key を使って明示的に CSP をセットしないあらゆる拡張機能にあてはまります。下記の結論になります:

* <script> と <object> リソースは拡張機能のローカルからのみ読み込みできる
* 拡張機能は JavaScript で文字列を評価 (eval) できない
* インライン JavaScript は実行されない
### スクリプトとオブジェクトリソースの場所

既定の CSP の下では、拡張機能のローカルにある <script> と <object> リソースだけを読み込みできます。例えば、拡張機能の文書内にこんな行があるとします:

```html
<script src="https://code.jquery.com/jquery-2.2.4.js"></script>
```

これは要求したリソースを読み込みません: 静かに失敗し、このリソースから取ってきたはずのいかなるオブジェクトも見つかりません。この解決方法が 2 つあります:

* リソースをダウンロードして、拡張機能にパッケージして、このリソース版を参照する
* 必要なリモートオリジンを許可するために `content_security_policy` を使う
### eval() とその仲間

既定の CSP の下で、拡張機能は JavaScript 内の文字列の評価を許可しません。つまり次のことは許可されません:

```js
eval("console.log('some output');");
```

```js
window.setTimeout("alert('Hello World!');", 500);
```

```js
var f = new Function("console.log('foo');");
```

### インライン JavaScript

既定の CSP ではインライン JavaScript は実行されません。これは `<script>` タグで直接置かれた JavaScript と、インラインイベントハンドラーの両方とも許可されず、つまり次のことは許可されません:

```html
<script>
 console.log("foo");
</script>
```

```html
<div onclick="console.log('click')">Click me!</div>
```

ページが読み込まれた時にスクリプトを実行するのに `<body onload="main()">` のようなコードを使っている場合、代わりに DOMContentLoaded か load をリッスンします。


ThunderbirdにおけるWebExtensionsによるアドイン開発 - Mozilla

ThunderbirdにおけるWebExtensionsによるアドイン開発
=====================================

 Thunderbird のアドイン開発は Firefox と同様の方法でコーディングすることが可能です。
 テキストエディタや、あなたの選んだコーディングツールで。

API の違い
-------

 両者とも Gecko ベースであるため、いくつかの違いはあるものの、Thunderbird では、Firefox で使える API の多くをサポートしています。
 詳細は、browser compatibility for manifest.json と browser support for JavaScript APIsを参照してください。

参考
--

* Browser support for JavaScript APIs
* Browser compatibility for manifest.json


2 つめの拡張機能 - Mozilla

2 つめの拡張機能
=========

初めての拡張機能を一通り読んでいる場合、既に拡張機能の書き方を知っていることと思います。この記事では、API の使い方をより詳しく説明するため、前回よりも少し複雑なアドオンを書いてみます。

この拡張機能では Firefox のツールバーにボタンを新しく追加します。ユーザーがこのボタンをクリックすると、動物を選択できるポップアップが表示されます。ユーザーが動物を選択すると、ウェブページのコンテンツが選択した動物の画像に置き換わります。

これらの機能を以下のように実装していきます。

* **ブラウザーアクション**（Firefox のツールバーに表示させるボタン）を定義
 このボタン用に次のものを用意します。
    + "beasts-32.png" というアイコン
    + ボタン押下時に開くポップアップ（HTML / CSS / JavaScript で記述）
* **拡張機能のアイコンを定義**（ファイル名: "beasts-48.png"）。これはアドオンマネージャーに表示されます。
* **ウェブページに読み込ませるコンテンツスクリプト "beastify.js" を作成**
 このスクリプトによってウェブページが実際に変更されます。
* **ウェブページを置き換える動物の画像をパッケージ化**
 画像を "web-accessible なリソース" とし、ウェブページから参照できるようにします。

拡張機能の全体的な構造は、このようにイメージしていただければと思います。

![manifest.json ファイルには、アイコン、ポップアップを含むブラウザーアクション、ウェブアクセシビリティリソースが記載されています。選択された beast javascript のポップアップリソースは、beastify スクリプトで呼び出されます。](/ja/docs/Mozilla/Add-ons/WebExtensions/Your_second_WebExtension/untitled-1.png)

この拡張機能はシンプルですが、WebExtensions API の基本的なコンセプトを多く含んでいます。

* ツールバーにボタンを追加する
* HTML / CSS / JavaScript を用いてポップアップのパネルを定義する
* ウェブページにコンテンツスクリプトを差し込む
* 拡張機能の中においてコンテンツスクリプトと他のスクリプトとを通信させる
* ウェブページで用いるリソースを拡張機能にパッケージ化する

ソースコード一式は GitHub で参照できます。

拡張機能を書く
-------

新しいディレクトリーを作成し、そのディレクトリーに移動します。

```bash
mkdir beastify
cd beastify
```

### manifest.json

それでは、"beastify" ディレクトリー配下に新しいファイル "manifest.json" を作成します。以下の内容を書き込んで保存してください。

```json
{
  "manifest\_version": 2,
  "name": "Beastify",
  "version": "1.0",

  "description": "Adds a browser action icon to the toolbar. Click the button to choose a beast. The active tab's body content is then replaced with a picture of the chosen beast. See https://developer.mozilla.org/ja/docs/Mozilla/Add-ons/WebExtensions/Examples#beastify",
  "homepage\_url": "https://github.com/mdn/webextensions-examples/tree/master/beastify",
  "icons": {
    "48": "icons/beasts-48.png"
  },

  "permissions": ["activeTab"],

  "browser\_action": {
    "default\_icon": "icons/beasts-32.png",
    "default\_title": "Beastify",
    "default\_popup": "popup/choose\_beast.html"
  },

  "web\_accessible\_resources": [
    "beasts/frog.jpg",
    "beasts/turtle.jpg",
    "beasts/snake.jpg"
  ]
}
```

* 最初の 3 つのキー `manifest_version`、`name`、`version` は必須であり、拡張機能の基本的なメタデータを指定します
* `description` と `homepage_url` は省略可能ですが、設定しておくことをお勧めします。これは拡張機能についての役立つ情報となります
* `icons` (en-US) は省略可能ですが、設定しておくことをお勧めします。この値は拡張機能のアイコンを指定するものであり、アイコンはアドオンマネージャーに表示されます
* `permissions` は拡張機能に必要なパーミッションのリストです。ここでは `activeTab` パーミッション を要請しています
* `browser_action` はツールバーのボタンを指定するものです。ここでは 3 つの情報を指定しています
    + `default_icon` ではボタンのアイコンを指定します（必須項目）
    + `default_title` ではツールチップの文字列を指定します（省略可）
    + `default_popup` ではユーザーがボタンをクリックした際に表示されるポップアップを指定します。今回はポップアップを表示させたいので、拡張機能に含める HTML ファイルを `defualt_popup` キーで指定しています
* `web_accessible_resources` はウェブページからアクセスしたいファイルのリストです。この拡張機能では、拡張機能に同梱した画像でウェブページを置き換えるため、画像をウェブページからアクセス可能にする必要があります

パスはすべて manifest.json 自身からの相対パスで指定することに注意します。

### アイコン

拡張機能にはアイコンを用意すると良いでしょう。このアイコンは、アドオンマネージャーで拡張機能のリスト横に表示されます（アドオンマネージャーは "about:addons" の URL から確認できます）。今回の manifest.json では "icons/beasts-48.png" を用意していると宣言しています。

"icons" ディレクトリーを作成して、そこにアイコンを "beasts-48.png" という名前で 保存します。必要であれば サンプルで使用しているアイコンを利用しても構いません（このアイコンは Aha-Soft's Free Retina iconset から引用したものであり、該当するライセンスの下で使用しています）。

アイコンを自分で用意する場合 48x48 ピクセルのサイズにする必要があります。高解像度のディスプレイに 96x96 ピクセルのアイコンを表示させたい場合は、manifest.json の `icons` オブジェクトに `96` というプロパティで設定してください。

```json
"icons": {
  "48": "icons/beasts-48.png",
  "96": "icons/beasts-96.png"
}
```

### ツールバーのボタン

ツールバーのボタンにもアイコンが必要です。今回の manifest.json では "icons/beasts-32.png" を用意していると宣言しています。

アイコンを "beasts-32.png" という名前で "icons" ディレクトリー内に保存します。必要であれば サンプルで使用しているアイコン を利用しても構いません（このアイコンは IconBeast Lite のアイコン集 から引用したものであり、該当する ライセンス の下で使用しています）。

ポップアップを使わない場合、ユーザーがボタンをクリックした際にはクリックイベントが拡張機能に向けて送出されます。ポップアップを使う場合にはクリックイベントは送出されず、代わりにポップアップが開きます。今回はポップアップが必要なので、次の項で作成しましょう。

### ポップアップ

今回のポップアップでは、ユーザーが 3 つの動物から 1 つ選択できる機能を持つこととします。

拡張機能のルートディレクトリー配下に "popup" というディレクトリーを新しく作成します。ここにはポップアップ用のコードを保管します。ポップアップは次の 3 ファイルから構成されます。

* **`choose_beast.html`** パネルのコンテンツを定義する
* **`choose_beast.css`** コンテンツのスタイルを指定する
* **`choose_beast.js`** アクティブなタブでコンテンツスクリプトを実行し、ユーザーの選択を処理する

```bash
mkdir popup
cd popup
touch choose_beast.html choose_beast.css choose_beast.js
```

#### choose\_beast.html

HTML ファイルは次のようになります。

```html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="choose\_beast.css" />
  </head>

  <body>
    <div id="popup-content">
      <button>Frog</button>
      <button>Turtle</button>
      <button>Snake</button>
      <button type="reset">Reset</button>
    </div>
    <div id="error-content" class="hidden">
      <p>Can't beastify this web page.</p>
      <p>Try a different page.</p>
    </div>
    <script src="choose\_beast.js"></script>
  </body>
</html>
```

`"popup-content"` という ID の `<div>` 要素があって、動物の選択をするボタンとリセットボタン入っています。`"error-content"` という ID の `<div>` 要素と `"hidden"` クラスもあります。それはポップアップの初期化に問題がある場合に使います。

通常のウェブページと同じように CSS と JS ファイルを読み込んでいることに注意してください。

#### choose\_beast.css

この CSS ではポップアップのサイズを固定し、3 つの選択肢がポップアップを埋めるようにし、基本的なスタイリングを施しています。また要素を `class="hidden"` で隠してもいます: つまり `<div id="error-content"...` は既定では隠れています。

```css
html,
body {
  width: 100px;
}

.hidden {
  display: none;
}

button {
  border: none;
  width: 100%;
  margin: 3% auto;
  padding: 4px;
  text-align: center;
  font-size: 1.5em;
  cursor: pointer;
  background-color: #e5f2f2;
}

button:hover {
  background-color: #cff2f2;
}

button[type="reset"] {
  background-color: #fbfbc9;
}

button[type="reset"]:hover {
  background-color: #eaea9d;
}
```

#### choose\_beast.js

これがポップアップ用の JavaScript です。

```js
/\*\*
 \* ページのすべてを隠す CSS、ただし
 \* "beastify-image" クラスを持つ要素は除く
 \*/
const hidePage = `body > :not(.beastify-image) {
 display: none;
 }`;

/\*\*
 \* ボタンクリックを待ち受けし、ページ内のコンテンツスクリプトに
 \* 適切なメッセージを送る
 \*/
function listenForClicks() {
  document.addEventListener("click", (e) => {
    /\*\*
 \* 動物の名前を受け取って、対応する画像の URL を取得する
 \*/
    function beastNameToURL(beastName) {
      switch (beastName) {
        case "Frog":
          return browser.runtime.getURL("beasts/frog.jpg");
        case "Snake":
          return browser.runtime.getURL("beasts/snake.jpg");
        case "Turtle":
          return browser.runtime.getURL("beasts/turtle.jpg");
      }
    }

    /\*\*
 \* アクティブなタブにページを隠す CSS を挿入して
 \* 動物の URL を取得し、
 \* アクティブなタブのコンテンツスクリプトに "beastify" メッセージを送る
 \*/
    function beastify(tabs) {
      browser.tabs.insertCSS({ code: hidePage }).then(() => {
        let url = beastNameToURL(e.target.textContent);
        browser.tabs.sendMessage(tabs[0].id, {
          command: "beastify",
          beastURL: url,
        });
      });
    }

    /\*\*
 \* アクティブなタブからページを隠す CSS を削除し、
 \* アクティブなタブのコンテンツスクリプトに "reset" メッセージを送る
 \*/
    function reset(tabs) {
      browser.tabs.removeCSS({ code: hidePage }).then(() => {
        browser.tabs.sendMessage(tabs[0].id, {
          command: "reset",
        });
      });
    }

    /\*\*
 \* ただコンソールにエラーをログ出力する
 \*/
    function reportError(error) {
      console.error(`Could not beastify: ${error}`);
    }

    /\*\*
 \* アクティブなタブを取得し、
 \* "beastify()" か "reset()" を適切に呼び出す
 \*/
    if (e.target.type === "reset") {
      browser.tabs
        .query({ active: true, currentWindow: true })
        .then(reset)
        .catch(reportError);
    } else {
      browser.tabs
        .query({ active: true, currentWindow: true })
        .then(beastify)
        .catch(reportError);
    }
  });
}

/\*\*
 \* スクリプトにエラーがあった。
 \* ポップアップのエラーメッセージを表示し、通常の UI を隠す。
 \*/
function reportExecuteScriptError(error) {
  document.querySelector("#popup-content").classList.add("hidden");
  document.querySelector("#error-content").classList.remove("hidden");
  console.error(`Failed to execute beastify content script: ${error.message}`);
}

/\*\*
 \* ポップアップを読み込んだ時、コンテンツスクリプトをアクティブなタブに挿入し、
 \* クリックハンドラーを追加する。
 \* スクリプトの挿入ができない場合、エラー処理をする。
 \*/
browser.tabs
  .executeScript({ file: "/content\_scripts/beastify.js" })
  .then(listenForClicks)
  .catch(reportExecuteScriptError);
```

開始するのは 96 行です。ポップアップスクリプトはポップアップが読み込まれ次第、アクティブなタブのコンテンツスクリプトを実行し、その手段は `browser.tabs.executeScript()` API です。コンテンツスクリプトの実行が成功したら、タブが閉じられるかユーザーが別のページに移動するまで、コンテンツスクリプトがページにロードされたままになります。

`browser.tabs.executeScript()` の呼び出しがよく失敗する理由は、コンテンツスクリプトをすべてのページでは実行できないことです。例えば、about:debugging のような権限のあるブラウザーページでは実行できませんし、addons.mozilla.org ドメイン内のページでも実行できません。失敗した場合、`reportExecuteScriptError()` は `<div id="popup-content">` を隠して、`<div id="error-content"...` を表示し、エラーを console にログ出力します。

コンテンツスクリプトの実行が成功したら、`listenForClicks()` を呼び出します。これはポップアップのクリックを待ち受けします。

* `class="reset"` の付いたボタンがクリックされたら、`reset()` を呼び出す
* その他のボタン（つまり beast ボタン）がクリックされたら `beastify()` を呼び出す

`beastify()` 関数は次の 3 つを行います。

* 特定の動物画像を指す URL をボタンクリックに割り当てる
* CSS を挿入してページの全コンテンツを隠し、その手段は `browser.tabs.insertCSS()` (en-US) API
* コンテンツスクリプトに "beastify" 送り、その手段は `browser.tabs.sendMessage()` (en-US) API。ページを動物化するか尋ねて、動物画像への URL を渡す

`reset()` 関数は動物化を取り消します。

* 追加した CSS を削除し、その手段は `browser.tabs.removeCSS()` (en-US) API
* コンテンツスクリプトにページをリセットする "reset" メッセージを送る
### コンテンツスクリプト

拡張機能のルートディレクトリー配下に "content\_scripts" というディレクトリーを新しく作成します。続いてそこに "beastify.js" という名前のファイルを作成し、下記のスクリプトを記述します。

```js
(() => {
  /\*\*
 \* グローバルなガード変数をチェック、設定する。
 \* コンテンツスクリプトが再び同じページに挿入された場合、
 \* 次は何もしない。
 \*/
  if (window.hasRun) {
    return;
  }
  window.hasRun = true;

  /\*\*
 \* 動物の画像の URL を受け取り、既存の動物をすべて削除し、次に
 \* 画像を指す IMG 要素の作成・スタイル適用を行い、
 \* 作成したノードをドキュメント内に挿入する
 \*/
  function insertBeast(beastURL) {
    removeExistingBeasts();
    const beastImage = document.createElement("img");
    beastImage.setAttribute("src", beastURL);
    beastImage.style.height = "100vh";
    beastImage.className = "beastify-image";
    document.body.appendChild(beastImage);
  }

  /\*\*
 \* ページからすべての動物を削除する
 \*/
  function removeExistingBeasts() {
    const existingBeasts = document.querySelectorAll(".beastify-image");
    for (const beast of existingBeasts) {
      beast.remove();
    }
  }

  /\*\*
 \* バックグラウンドスクリプトからのメッセージを待ち受けし、
 \* "beastify()" か "reset()" を呼び出す。
 \*/
  browser.runtime.onMessage.addListener((message) => {
    if (message.command === "beastify") {
      insertBeast(message.beastURL);
    } else if (message.command === "reset") {
      removeExistingBeasts();
    }
  });
})();
```

このコンテンツスクリプトが最初にすることは、グローバル変数の `window.hasRun` のチェックです: セットされていればスクリプトは早くリターンし、そうでなければ `window.hasRun` をセットして処理し続けます。こうする理由は、ユーザーがポップアップを開くたびに、ポップアップはアクティブなタブのコンテンツスクリプトを実行して、そのために 1 つのタブに複数の実行スクリプトのインスタンスができてしまいます。これが起きると、最初のインスタンスだけが処理するのを確かめる必要があります。

その次に、始まる場所は 40 行で、ここでコンテンツスクリプトはポップアップからのメッセージを待ち受けし、その手段は `browser.runtime.onMessage` API です。上で見たように、ポップアップスクリプトは 2 種類の異なるメッセージを送ります: "beastify" と "reset"

* メッセージが "beastify" なら、動物画像を指す URL が含まれるはずです。以前の "beastify" 呼び出しで追加された動物をすべて削除して、`<img>` 要素を作成、追加して、その `src` 属性に動物の URL をセットします。
* メッセージが "reset" なら、ただ追加された動物をすべて削除します。
### 動物

最後に、動物の画像を用意しておく必要があります。

拡張機能のルートディレクトリー配下に "beasts" という名前のディレクトリーを新しく作成し、その中に 3 つの画像を適切な名前で保存します。画像は GitHub リポジトリ から、またはここからでも取得できます。

![茶色いカエル](/ja/docs/Mozilla/Add-ons/WebExtensions/Your_second_WebExtension/frog.jpg)

![白い縞模様のあるエメラルド色のツリーボア](/ja/docs/Mozilla/Add-ons/WebExtensions/Your_second_WebExtension/snake.jpg)

![アカミミガメ](/ja/docs/Mozilla/Add-ons/WebExtensions/Your_second_WebExtension/turtle.jpg)

動かしてみよう
-------

正しいファイルが正しい場所にあるかどうか、もう一度確認してください。
```

beastify/

    beasts/
        frog.jpg
        snake.jpg
        turtle.jpg

    content_scripts/
        beastify.js

    icons/
        beasts-32.png
        beasts-48.png

    popup/
        choose_beast.css
        choose_beast.html
        choose_beast.js

    manifest.json
```

Firefox で "about:debugging" を開き、"一時的なアドオンを読み込む" をクリックし、自分で作成した manifest.json ファイルを選択してください。拡張機能のアイコンが Firefox のツールバーに表示されているはずです。

![Firefox ツールバーの beastify アイコン](/ja/docs/Mozilla/Add-ons/WebExtensions/Your_second_WebExtension/beastify_icon.png)

適当なウェブページを開き、拡張機能のアイコンをクリックし、動物を選択してください。するとウェブページが動物の画像に置き換わるはずです。

![亀の画像に置き換えたページ](/ja/docs/Mozilla/Add-ons/WebExtensions/Your_second_WebExtension/beastify_page.png)

コマンドラインから開発する
-------------

仮インストールを web-ext ツールを用いて自動化できます。次を試してください。

```bash
cd beastify
web-ext run
```

次は何？
----

これで、より高度な Firefox 用 WebExtension を作成することができました。

* 拡張機能の中身
* 拡張機能の例
* 拡張機能を開発し、テストし、公開するために必要なものを探す
* さらに学びを深める


HTTP リクエストへの介入 - Mozilla

HTTP リクエストへの介入
==============

 HTTP リクエストへ介入するには `webRequest` API を用います。
 この API を利用すると、HTTP リクエストの生成段階における様々なタイミングにリスナー関数を追加できます。
 追加したリスナーの中では、以下の処理を行うことができます。

* リクエストヘッダーや本体とレスポンスヘッダーにアクセス
* リクエストのキャンセルやリダイレクト
* リクエスト・レスポンスヘッダーの改変

この記事では、以下の 3 つの目的それぞれについて、`webRequest` モジュールの使い方を説明します。

* リクエストの生成時にリクエスト URL を記録する
* リクエストをリダイレクトする
* リクエストヘッダーを改変する
リクエスト URL の記録
-------------

 まず "requests" というディレクトリーを新しく作成しましょう。
 このディレクトリー内に "manifest.json" というファイルを作成し、以下を追加してください。

```json
{
  "description": "Demonstrating webRequests",
  "manifest\_version": 2,
  "name": "webRequest-demo",
  "version": "1.0",

  "permissions": ["webRequest", "<all\_urls>"],

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

続いて "background.js" というファイルを作成し、以下のスクリプトを書き込んでください。

```js
function logURL(requestDetails) {
  console.log(`Loading: ${requestDetails.url}`);
}

browser.webRequest.onBeforeRequest.addListener(logURL, {
  urls: ["<all\_urls>"],
});
```

ここでは、リクエストを作成する直前に`onBeforeRequest` (en-US) を利用して、 `logURL()` 関数を呼んでいます。 `logURL()` 関数では、イベントオブジェクトからリクエスト URL を取得し、ブラウザーのコンソールに出力しています。 `{urls: ["<all_urls>"]}` パターン は、すべての URL に対する HTTP リクエストに介入することを表しています。

試してみるには次のようにします。

* 拡張機能をインストールしてください
* ブラウザーコンソールを開いてください（`Ctrl + Shift + J` を使用）。
* メニュー内の *コンテンツメッセージを表示* を有効にしてください。

 ![ブラウザーのコンソールメニュー : コンテンツメッセージの表示](/ja/docs/Mozilla/Add-ons/WebExtensions/Intercept_HTTP_requests/browser_console_show_content_messages.png)
* 適当なウェブページを開いてください。

 ブラウザーコンソールには、ブラウザーがリクエストされたすべてのリソースの URL が表示されるはずです。
 例えば、このスクリーンショットは、 Wikipedia のページを読み込んだときの URL を示しています。

![ブラウザーコンソールのメニュー : 拡張機能から URL を表示](/ja/docs/Mozilla/Add-ons/WebExtensions/Intercept_HTTP_requests/browser_console_url_from_extension.png)

リクエストのリダイレクト
------------

さて、 `webRequest` を使って HTTP リクエストをリダイレクトさせてみましょう。まず最初に manifest.json を以下の内容へ変更します。

```json
{
  "description": "Demonstrating webRequests",
  "manifest\_version": 2,
  "name": "webRequest-demo",
  "version": "1.0",

  "permissions": [
    "webRequest",
    "webRequestBlocking",
    "https://developer.mozilla.org/"
  ],

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

変更点は次の通りです。

* `webRequestBlocking` を `permission` に追加しました。
 この特別な権限は、拡張機能がリクエストを変更したいときにリクエストされます。
* `<all_urls>` 権限を個々のホスト権限に置き換えてください。これはリクエストされた権限数を最小限にするための良い習慣です。

続いて "background.js" を以下のように書き換えます。

```js
let pattern = "https://developer.mozilla.org/\*";
const targetUrl =
  "https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Your\_second\_WebExtension/frog.jpg";

function redirect(requestDetails) {
  console.log(`Redirecting: ${requestDetails.url}`);
  if (requestDetails.url === targetUrl) {
    return;
  }
  return {
    redirectUrl: targetUrl,
  };
}

browser.webRequest.onBeforeRequest.addListener(
  redirect,
  { urls: [pattern], types: ["image"] },
  ["blocking"],
);
```

 ここでも `onBeforeRequest` (en-US) イベントリスナーを使用し、それぞれのリクエストが行われる直前に関数を実行します。
 この関数は `redirectUrl` を関数内で指定された対象の URL に置き換えます。この場合、 2 つめの拡張機能 にあるカエルの画像を指定します。

 今回はすべてのリクエストに介入しないこととします。`{urls:[pattern], types:["image"]}` というオプションにより、(1) "https://developer.mozilla.org/" 配下の URL であり、かつ (2) 画像リソースなリクエストのみに介入します。
 詳細は `webRequest.RequestFilter` (en-US) を参照してください。

 また、`"blocking"` というオプションも渡していることに注意してください。
 このオプションは、リクエストを改変する際に必ず必要となります。これによってネットワークリクエストがリスナー関数にブロックされるため、リスナー関数から処理が戻るまでブラウザーは待機します。
 `"blocking"` に関する詳細は `webRequest.onBeforeRequest` (en-US) のドキュメントを参照してください。

それでは実際に動かしてみましょう。画像が多く載っている MDN のページ（the page listing extension user interface components など）を開き、拡張機能をリロード します。終わったら MDN のページをリロードしてみましょう。

![ページ内の画像をカエルの画像に置き換えた場合](/ja/docs/Mozilla/Add-ons/WebExtensions/Intercept_HTTP_requests/beastify_by_redirect.png)

リクエストヘッダーの改変
------------

最後の例として、`webRequest` を使ってリクエストヘッダーを改変してみましょう。ここでは、ブラウザーが Opera 12.16 と認識されるように "User-Agent" ヘッダーを改変してみます。ただし、"http://useragentstring.com/" の配下へアクセスした際にのみ改変することとします。

"manifest.json" を更新して、以下のように `http://useragentstring.com/` を記載します。

```json
{
  "description": "Demonstrating webRequests",
  "manifest\_version": 2,
  "name": "webRequest-demo",
  "version": "1.0",

  "permissions": [
    "webRequest",
    "webRequestBlocking",
    "http://useragentstring.com/"
  ],

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

"background.js" を以下のように書き換えます。

```js
let targetPage = "http://useragentstring.com/\*";

let ua =
  "Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16";

function rewriteUserAgentHeader(e) {
  e.requestHeaders.forEach((header) => {
    if (header.name.toLowerCase() === "user-agent") {
      header.value = ua;
    }
  });
  return { requestHeaders: e.requestHeaders };
}

browser.webRequest.onBeforeSendHeaders.addListener(
  rewriteUserAgentHeader,
  { urls: [targetPage] },
  ["blocking", "requestHeaders"],
);
```

ここでは、リクエストヘッダーが送信される直前に関数を実行するため `onBeforeSendHeaders` (en-US) イベントリスナーを利用しています。

 このリスナー関数は、 URL が `targetPage` のパターンに一致したリクエストに対してのみ呼び出されます。
 ここでも再び、`"blocking"` がオプションとして与えられていることに注意してください。また、`"requestHeaders"` もオプションで渡すことにより、送信予定のリクエストヘッダーを含んだ配列がリスナー関数に渡されます。
 これらのオプションに関しては `webRequest.onBeforeSendHeaders` (en-US) で詳細を確認してください。

リスナー関数では、リクエストヘッダーの配列から "User-Agent" ヘッダーを探し、ヘッダーの値を変数 `ua` の値で置き換え、改変された配列を最後に返しています。この改変された配列は、やがてサーバーへ送信されることとなります。

それでは実際に動かしてみましょう。 useragentstring.com へアクセスし、自分のブラウザーが Firefox と判定されることを確認します。ここで拡張機能を再読み込みし、 useragentstring.com のページを再読み込みします。すると、先程 Firefox と判定されたのは Opera になっているはずです。

![useragentstring.com が変更されたユーザーエージェント文字列の詳細を示している様子](/ja/docs/Mozilla/Add-ons/WebExtensions/Intercept_HTTP_requests/modified_request_header.png)

詳細情報
----

`webRequest` API でできることを詳しく知りたい場合は、リファレンスドキュメントを参照してください。


manifest.json のブラウザー互換性 - Mozilla

manifest.json のブラウザー互換性
=======================

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

**メモ:** Microsoft Edge の互換性データは Microsoft Corporation により提供され、Creative Commons Attribution 3.0 United States License の下でこのページに含めています。

関連情報
----

* JavaScript API へのブラウザーのサポート


Tabs API を使う - Mozilla

Tabs API を使う
============

タブを使うとユーザーはブラウザーウィンドウに複数のウェブページを開いてウェブページ間をスイッチできるようになります。Tabs API により、タブを操作して、新しい方法でタブを使ったり拡張機能の機能として配布できます。

このハウツー記事では次のようなことを見ていきます:

* Tabs API を使うのに必要な権限
* タブについての詳しい探検と`tabs.query`を使ったプロパティ
* タブの作成、複製、移動、更新、読み込み、と削除
* タブのズームレベル操作
* タブの CSS 操作

その次には、API で提供されるその他のいろいろな機能を見て終わります。

**メモ:** There are some Tab API features covered elsewhere. These are the methods you can use to manipulate tab content with scripts (`tabs.connect` (en-US), `tabs.sendMessage` (en-US), and `tabs.executeScript`). If you want more information on these methods, see the Concepts article Content scripts and the how-to guide Modify a web page.

権限と Tabs API
------------

Tabs API 機能の大半では権限は不要ですが、次の例外はあります:

* Tab オブジェクトの `Tab.url`, `Tab.title`, `Tab.favIconUrl` プロパティにアクセスするには`"tabs`" 権限が必要です。Firefox では、URL の query を実行するのに`"tabs`"が要ります。
* `tabs.executeScript` や `tabs.insertCSS` (en-US) には Host 権限が必要です。

下記は、拡張機能の manifest.json ファイルで `"tabs"` 権限を要求する方法です:

```json
"permissions": [
  "<all\_urls>",
  "tabs"
],
```

この要求により、訪問するすべてのウェブサイトですべての Tabs API 機能を使うことができます。また、host 権限不要の `tabs.executeScript` や `tabs.insertCSS` (en-US) を使うのに、`"activeTab"`の形で権限を要求する別のこの権限は `"tabs"` に `<all_urls>` つきのものと同じですが、次の 2 つの制限があります:

* ユーザーは拡張機能のページアクション、コンテキストメニュー、ショートカットキーいずれかでやりとりせねばならない。
* アクティブなタブ内の権限のみ許可される。

このアプローチの利点は、拡張機能が "Access your data for all websites" という警告をユーザーに表示しないことです。これは `<all_urls>` 権限により、拡張機能がいつでもどんなタブでも実行できるのに対し、`"activeTab"` では、現在のタブでのユーザーが要求したアクションのみ実行するためです。

タブとそのプロパティの探検
-------------

すべてのブラウザーウィンドウのすべてのタブのリストを取得したいときもあるでしょう。ある条件にマッチするタブ、例えば特定のタブから開かれた、あるいはあるドメインのページを表示したもの、のサブセットを発見したいこともあるでしょう。タブのリストを持っていれば、そのプロパティを詳しく知りたくなるでしょう。

これが `tabs.query` の出てくる場所です。単独で使ってすべてのタブを取得したり、`queryInfo` オブジェクト—これでタブがアクティブかとか、現在のウィンドウ内かとかといった、17 の条件のいくつかを指定する条件—をつけて、`tabs.query` はタブの情報を含んだ `tabs.Tab` オブジェクトの配列を返します。

現在のタブだけの情報が欲しいときは、そのタブの `tabs.Tab` オブジェクトを `tabs.getCurrent` (en-US) を使って取得できます。タブの ID がある場合、`tabs.Tab` オブジェクトを `tabs.get` を使って取得できます。

### 方法の実例

`tabs.query` と `tabs.Tab` の使われ方を見るために、tabs-tabs-tabs の例で "switch to tabs" のリストをツールバーボタンのポップアップに追加するのを見てみましょう。

![The tabs tabs tabs toolbar menu showing the switch to tap area](/ja/docs/Mozilla/Add-ons/WebExtensions/Working_with_the_Tabs_API/Switch_to_tab.png)

### manifest.json

これが manifest.json です:

```json
{
  "browser\_action": {
    "browser\_style": true,
    "default\_title": "Tabs, tabs, tabs",
    "default\_popup": "tabs.html"
  },
  "description": "A list of methods you can perform on a tab.",
  "homepage\_url": "https://github.com/mdn/webextensions-examples/tree/master/tabs-tabs-tabs",
  "manifest\_version": 2,
  "name": "Tabs, tabs, tabs",
  "permissions": ["tabs"],
  "version": "1.0"
}
```

**メモ:** 次のことに注意します:

* \*\*tabs.html は `browser_action` の `default_popup` で定義されていること。\*\*これはユーザーが拡張機能のツールバーアイコンをクリックするといつでも表示されます。
* \*\*権限にタブが入っていること。\*\*これはタブリスト機能をサポートするのに必要であり、なぜなら拡張機能がポップアップに表示するためにタブのタイトルを読み取るためです。

### tabs.html

tabs.html では拡張機能のポップアップのコンテンツを定義します。

```html
<!doctype html>

<html>
  <head>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="tabs.css" />
  </head>

  <body>
    <div class="panel">
      <div class="panel-section panel-section-header">
        <div class="text-section-header">Tabs-tabs-tabs</div>
      </div>

      <a href="#" id="tabs-move-beginning"
 >Move active tab to the beginning of the window</a
 ><br />

      … Define the other menu items …

      <div class="switch-tabs">
        <p>Switch to tab</p>

        <div id="tabs-list"></div>
      </div>
    </div>

    <script src="tabs.js"></script>
  </body>
</html>
```

上記のまとめとして:

1. メニュー項目が定義されます。
2. タブのリストを入れるため、ID が `tabs-list` の空の `div` が定義されます。
3. tabs.js が呼ばれます。
### tabs.js

tabs.js では、タブのリストが作られて、ポップアップに追加される方法を見ていきましょう。

#### ポップアップの作成

まず、tabs.html が読み込まれたとき、`listTabs()` を実行するイベントハンドラを追加します。:

```js
document.addEventListener("DOMContentLoaded", listTabs);
```

`listTabs()` は最初に `getCurrentWindowTabs()` を呼び出します。ここで現在のウィンドウ内のタブの `tabs.Tab` オブジェクトを取得するのに`tabs.query` が使われます:

```js
function getCurrentWindowTabs() {
  return browser.tabs.query({ currentWindow: true });
}
```

今度は、ポップアップのコンテンツを作成するために `ListTabs()` が準備できています。

開始するには:

1. `tabs-list` `div` を取得。
2. 文書のフラグメントを作成 (ここにリストが作成される)。
3. カウンターをセット。
4. `tabs-list` `div` のコンテンツをクリアする。

```js
function listTabs() {
 getCurrentWindowTabs().then((tabs) => {
    let tabsList = document.getElementById('tabs-list');
    let currentTabs = document.createDocumentFragment();
    let limit = 5;
    let counter = 0;

    tabsList.textContent = '';
```

次に、それぞれのタブのリンクを作ります:

1. `tabs.Tab` オブジェクトからの最初の 5 項目でループする。
2. 各項目ごとに、文書のフラグメントのハイパーリンクを追加する。
    * リンクのラベル—つまり、そのテキスト—が、タブのタイトル (ない場合はその ID)を使って、セットされる
    * タブの ID を使ってリンクのアドレスがセットされる。

```js
for (let tab of tabs) {
  if (!tab.active && counter <= limit) {
    let tabLink = document.createElement("a");

    tabLink.textContent = tab.title || tab.id;

    tabLink.setAttribute("href", tab.id);
    tabLink.classList.add("switch-tabs");
    currentTabs.appendChild(tabLink);
  }

  counter += 1;
}
```

最後に、`tabs-list` div に文書のフラグメントが書き込まれる:

```js
    tabsList.appendChild(currentTabs);
  });
}
```

#### アクティブタブを操作する

関連するもう 1 つの例として、"Alert active tab" インフォオプションがあり、これはアクティブなタブのすべての `tabs.Tab` オブジェクトのプロパティをアラートにダンプするものです:

```js
 else if (e.target.id === "tabs-alertinfo") {
   callOnActiveTab((tab) => {
     let props = "";
     for (let item in tab) {
       props += `${ item } = ${ tab[item] } \n`;
     }
     alert(props);
   });
 }
```

`callOnActiveTab()` では、アクティブなセットを求めて `tabs.Tab` オブジェクトをループすることで、アクティブなタブを探します:

```js
document.addEventListener("click", function(e) {
 function callOnActiveTab(callback) {
   getCurrentWindowTabs().then((tabs) => {
     for (var tab of tabs) {
       if (tab.active) {
         callback(tab, tabs);
       }
     }
   });
 }
}
```

タブを作成、複製、移動、更新、読み込み、削除する
------------------------

Having gathered information about the tabs you'll most likely want to do something with them—either to offer users features for manipulating and managing tabs or to implement functionality in your extension.

The following functions are available:

* create a new tab (`tabs.create`).
* duplicate a tab (`tabs.duplicate`).
* remove a tab (`tabs.remove`).
* move a tab (`tabs.move` (en-US)).
* update the tab's URL—effectively browse to a new page—(`tabs.update` (en-US)).
* reload the tab's page (`tabs.reload` (en-US)).

**メモ:**

These functions all require the ID (or IDs) of the tab they are manipulating:

* `tabs.duplicate`
* `tabs.remove`
* `tabs.move` (en-US)

Whereas the following functions will act on the active tab (if no tab ID is provided):

* `tabs.update` (en-US)
* `tabs.reload` (en-US)

### 方法の実例

The tabs-tabs-tabs example exercises all of these features except for updating a tab's URL The way in which these APIs are used is similar, so we'll look at one of the more involved implementations, that of the "Move active tab to the beginning of the window list" option.

But first, here is a demonstration of the feature in action:

#### manifest.json

None of the functions require a permission to operate, so there are no features in the manifest.json file that need to be highlighted.

#### tabs.html

tabs.html defines the "menu" displayed in the popup, which includes the "Move active tab to the beginning of the window list" option, with a series of `<a>` tags grouped by a visual separator. Each menu item is given an ID, which is used in tabs.js to determine which menu item is being requested.

```html
<a href="#" id="tabs-move-beginning"
 >Move active tab to the beginning of the window</a
><br />
<a href="#" id="tabs-move-end">Move active tab to the end of the window</a
><br />

<div class="panel-section-separator"></div>

<a href="#" id="tabs-duplicate">Duplicate active tab</a><br />

<a href="#" id="tabs-reload">Reload active tab</a><br />
<a href="#" id="tabs-alertinfo">Alert active tab info</a><br />
```

#### tabs.js

To implement the "menu" defined in tabs.html, tabs.js includes a listener for clicks in tabs.html:

```js
document.addEventListener("click", function(e) {
 function callOnActiveTab(callback) {

   getCurrentWindowTabs().then((tabs) => {
     for (var tab of tabs) {
       if (tab.active) {
         callback(tab, tabs);
       }
     }
   });
}
}
```

A series of `if` statements then look to match the ID of the item clicked.

This code snippet is for the "Move active tab to the beginning of the window list" option:

```js
if (e.target.id === "tabs-move-beginning") {
  callOnActiveTab((tab, tabs) => {
    var index = 0;
    if (!tab.pinned) {
      index = firstUnpinnedTab(tabs);
    }
    console.log(`moving ${tab.id} to ${index}`);
    browser.tabs.move([tab.id], { index });
  });
}
```

It's worth noting the use of `console.log()`. This enables you to output information to the debugger console, which can be useful when resolving issues found during development.

![Example of the console.log output, from the move tabs feature, in the debugging console](/ja/docs/Mozilla/Add-ons/WebExtensions/Working_with_the_Tabs_API/console.png)

The move code first calls `callOnActiveTab()` which in turn calls `getCurrentWindowTabs()` to get a `tabs.Tab` object containing the active window's tabs. It then loops through the object to find and return the active tab object:

```js
function callOnActiveTab(callback) {
  getCurrentWindowTabs().then((tabs) => {
    for (var tab of tabs) {
      if (tab.active) {
        callback(tab, tabs);
      }
    }
  });
}
```

##### ピン止めされたタブ

A feature of tabs is that the user can *pin* tabs in a window. Pinned tabs are placed at the start of the tab list and cannot be moved. This means that the earliest position a tab can move to is the first position after any pinned tabs. So, `firstUnpinnedTab()` is called to find the position of the first unpinned tab by looping through the `tabs` object:

```js
function firstUnpinnedTab(tabs) {
  for (var tab of tabs) {
    if (!tab.pinned) {
      return tab.index;
    }
  }
}
```

We now have everything needed to move the tab: the active tab object from which we can get the tab ID and the position the tab is to be moved to. So, we can implement the move:

```js
browser.tabs.move([tab.id], { index });
```

The remaining functions to duplicate, reload, create, and remove tabs are implemented similarly.

タブのズームレベルを操作する
--------------

The next set of functions enable you to get (`tabs.getZoom` (en-US)) and set (`tabs.setZoom` (en-US)) the zoom level within a tab. You can also retrieve the zoom settings (`tabs.getZoomSettings` (en-US)) but, at the time of writing, the ability to set the settings (`tabs.setZoomSettings` (en-US)) wasn't available in Firefox.

The level of zoom can be between 30% and 300% (represented as decimals `0.3` to `3`).

In Firefox the default zoom settings are:

* **default zoom level:** 100%.
* **zoom mode:** automatic (so the browser manages how zoom levels are set).
* **scope of zoom changes:** `"per-origin"`, meaning that when you visit a site again, it takes the zoom level set in your last visit.
### 方法の実例

The tabs-tabs-tabs example includes three demonstrations of the zoom feature: zoom in, zoom out, and reset zoom. Here is the feature in action:

Let's take a look at how the zoom in is implemented.

#### manifest.json

None of the zoom functions require permissions, so there are no features in the manifest.json file that need to be highlighted.

#### tabs.html

We have already discussed how the tabs.html defines the options for this extension, nothing new or unique is done to provide the zoom options.

#### tabs.js

tabs.js starts by defining several constants used in the zoom code:

```js
const ZOOM\_INCREMENT = 0.2;
const MAX\_ZOOM = 3;
const MIN\_ZOOM = 0.3;
const DEFAULT\_ZOOM = 1;
```

It then uses the same listener we discussed earlier so it can act on clicks in tabs.html.

For the zoom in feature, this runs:

```js
 else if (e.target.id === "tabs-add-zoom") {
   callOnActiveTab((tab) => {
     var gettingZoom = browser.tabs.getZoom(tab.id);
     gettingZoom.then((zoomFactor) => {
       //the maximum zoomFactor is 3, it can't go higher
       if (zoomFactor >= MAX\_ZOOM) {
         alert("Tab zoom factor is already at max!");
       } else {
         var newZoomFactor = zoomFactor + ZOOM\_INCREMENT;
         //if the newZoomFactor is set to higher than the max accepted
         //it won't change, and will never alert that it's at maximum
         newZoomFactor = newZoomFactor > MAX\_ZOOM ? MAX\_ZOOM : newZoomFactor;
         browser.tabs.setZoom(tab.id, newZoomFactor);
       }
     });
   });
 }
```

This code uses `callOnActiveTab()` to get the details of the active tab, then `tabs.getZoom` (en-US) gets the tab's current zoom factor. The current zoom is compared to the defined maximum (`MAX_ZOOM`) and an alert issued if the tab is already at the maximum zoom. Otherwise, the zoom level is incremented but limited to the maximum zoom, then the zoom is set with `tabs.getZoom` (en-US).

タブの CSS を操作する
-------------

Another significant capability offered by the Tabs API is the ability to manipulate the CSS within a tab—add new CSS to a tab (`tabs.insertCSS` (en-US)) or remove CSS from a tab (`tabs.removeCSS` (en-US)).

This can be useful, 例えば、 if you want to highlight certain page elements or change the default layout of the page.

### 方法の実例

The apply-css example uses these features to add a red border to the web page in the active tab. Here is the feature in action:

Let's walk through how it's set up.

#### manifest.json

To use the CSS features you need either:

* `"tabs"` permission and host permission or
* `"activeTab"` permission.

The latter is the most useful, as it allows an extension to use `tabs.insertCSS` (en-US) and `tabs.removeCSS` (en-US) in the active tab when run from the extension's browser or page action, context menu, or a shortcut.

```json
{
  "description": "Adds a page action to toggle applying CSS to pages.",

  "manifest\_version": 2,
  "name": "apply-css",
  "version": "1.0",
  "homepage\_url": "https://github.com/mdn/webextensions-examples/tree/master/apply-css",

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

  "page\_action": {
    "default\_icon": "icons/off.svg",
    "browser\_style": true
  },

  "permissions": ["activeTab", "tabs"]
}
```

You will note that `"tabs"` permission is requested in addition to `"activeTab"`. This additional permission is needed to enable the extension's script to access the tab's URL, the importance of which we'll see in a moment.

The other main features in the manifest.json file are the definition of:

* **a background script**, which starts running as soon as the extension is loaded.
* **a "page action"**, which defines an icon to be added to the browser's address bar.

#### background.js

On startup, background.js sets some constants to define the CSS to be applied, titles for the "page action", and a list of protocols the extension will work in:

```js
const CSS = "body { border: 20px solid red; }";
const TITLE\_APPLY = "Apply CSS";
const TITLE\_REMOVE = "Remove CSS";
const APPLICABLE\_PROTOCOLS = ["http:", "https:"];
```

When first loaded, the extension uses `tabs.query` to get a list of all the tabs in the current browser window. It then loops through the tabs calling `initializePageAction()`.

```js
var gettingAllTabs = browser.tabs.query({});

gettingAllTabs.then((tabs) => {
  for (let tab of tabs) {
    initializePageAction(tab);
  }
});
```

`initializePageAction` uses `protocolIsApplicable()` to determine whether the active tab's URL is one the CSS can be applied to:

```js
function protocolIsApplicable(url) {
  var anchor = document.createElement("a");
  anchor.href = url;
  return APPLICABLE\_PROTOCOLS.includes(anchor.protocol);
}
```

Then, if the example can act on the tab, `initializePageAction()` sets the tab's `pageAction` (navigation bar) icon and title to use the "off" versions before making the `pageAction` visible:

```js
function initializePageAction(tab) {
  if (protocolIsApplicable(tab.url)) {
    browser.pageAction.setIcon({ tabId: tab.id, path: "icons/off.svg" });
    browser.pageAction.setTitle({ tabId: tab.id, title: TITLE\_APPLY });
    browser.pageAction.show(tab.id);
  }
}
```

Next, a listener on `pageAction.onClicked` waits for the `pageAction` icon to be clicked, and calls `toggleCSS` when it is.

```js
browser.pageAction.onClicked.addListener(toggleCSS);
```

`toggleCSS()` gets the title of the `pageAction` and then takes the action described:

* **For "Apply CSS":**
    + toggles the `pageAction` icon and title to the "remove" versions.
    + applies the CSS using `tabs.insertCSS` (en-US).
* **For "Remove CSS":**
    + toggles the `pageAction` icon and title to the "apply" versions.
    + removes the CSS using `tabs.removeCSS` (en-US).

```js
function toggleCSS(tab) {
  function gotTitle(title) {
    if (title === TITLE\_APPLY) {
      browser.pageAction.setIcon({ tabId: tab.id, path: "icons/on.svg" });
      browser.pageAction.setTitle({ tabId: tab.id, title: TITLE\_REMOVE });
      browser.tabs.insertCSS({ code: CSS });
    } else {
      browser.pageAction.setIcon({ tabId: tab.id, path: "icons/off.svg" });
      browser.pageAction.setTitle({ tabId: tab.id, title: TITLE\_APPLY });
      browser.tabs.removeCSS({ code: CSS });
    }
  }

  var gettingTitle = browser.pageAction.getTitle({ tabId: tab.id });

  gettingTitle.then(gotTitle);
}
```

Finally, to ensure that the `pageAction` is valid after each update to the tab, a listener on `tabs.onUpdated` (en-US) calls `initializePageAction()` each time the tab is updated to check that the tab is still using a protocol to which the CSS can be applied.

```js
browser.tabs.onUpdated.addListener((id, changeInfo, tab) => {
  initializePageAction(tab);
});
```

その他の興味深い機能
----------

There are a couple of other Tabs API features that don't fit into one of the earlier sections:

* Capture the visible tab content with `tabs.captureVisibleTab`.
* Detect the primary language of the content in a tab using `tabs.detectLanguage` (en-US). This could be used, 例えば、to match the language in your extension's UI with that of the page it's running in.
関連項目
----

If you want to learn more about the Tabs API, check out:

* Tabs API reference
* Example extensions (many of which use the Tabs API)


Chrome との非互換性 - Mozilla

Chrome との非互換性
=============

Webextension を用いた拡張機能は Chrome や Opera の拡張機能と互換性があるように設計されています。可能な限り、それぞれのブラウザー向けに書かれた拡張機能は最低限の変更で Firefox で実行できるように設計されています。

しかしながら、Firefox は現在、Chrome と Opera でサポートされている機能と API の限られたセットのみをサポートしています。もっと多く機能を追加しようと努めていますが、まだサポートされていない機能もたくさんあり、まったくサポートしないものもいくつかあります。

JavaScript APIs
---------------

### コールバックと chrome.\* ネームスペース

Chrome では、拡張機能は `chrome` ネームスペースを使って特権 JavaScript API にアクセスします:

```js
chrome.browserAction.setIcon({ path: "path/to/icon.png" });
```

WebExtensions は同等の API に `browser` ネームスペースを使ってアクセスします:

```js
browser.browserAction.setIcon({ path: "path/to/icon.png" });
```

多くの API は非同期です。 Chrome では、非同期 API はコールバックを使用して値を返し、`runtime.lastError`がエラーを通知します:

```js
function logCookie(c) {
  if (chrome.extension.lastError) {
    console.error(chrome.extension.lastError);
  } else {
    console.log(c);
  }
}

chrome.cookies.set({ url: "https://developer.mozilla.org/" }, logCookie);
```

同様の WebExtensions API では promises を利用します:

```js
function logCookie(c) {
  console.log(c);
}

function logError(e) {
  console.error(e);
}

var setCookie = browser.cookies.set({ url: "https://developer.mozilla.org/" });
setCookie.then(logCookie, logError);
```

### Firefox は`chrome`と`browser`の両方のネームスペースをサポート

移植の助けとして、WebExtensions の Firefox 実装は、コールバックを使用した`chrome`を、promise を使用した`browser`と同様にサポートします。これは、多くの Chrome 拡張機能が Firefox でそのまま動作することを意味します。ただし、これは WebExtensions 標準の一部ではなく、準拠するすべてのブラウザーでサポートされているとは限りません。

`browser` と promise を使って拡張機能を書いている場合、Chrome でも動かせるようにする polyfill を開発しました: https://github.com/mozilla/webextension-polyfill.

### 部分的にサポートされている API

ブラウザーの JavaScript API サポートのページには、Firefox でサポートされているすべての API の互換性テーブルが含まれています。特定の API アイテムのサポートに関する警告がある場合は、これらのテーブルにアスタリスク "\*" が表示され、API アイテムのリファレンスページにその警告が説明されています。

このテーブルは、GitHub で JSON ファイルとして保存された互換性データから生成されます。

このセクションの残りの部分では、テーブルにまだ取り込まれていない互換性の問題について説明します。

#### notifications

* `notifications.create()`の `"basic"` `type`を指定した場合の、 `iconUrl` は Firefox では任意です。Chrome では必須です。
* 通知はユーザーがクリックするとすぐに消去されます。 これは Chrome では当てはまりません。
* `notifications.create()` を複数回連続して呼び出すと、Firefox が通知を一切表示しなくなることがあります。 `chrome.notifications.create()` のコールバック 関数内に入るまでの時間は、後続のスクリプトが実行されるまで待つほど十分な時間ではありません。

#### proxy

* この API は Chrome API の設計とはまったく異なります。 Chrome の API を使用すると、拡張機能で PAC ファイルを登録できますが、明示的なプロキシルールを定義することもできます。これは拡張 PAC ファイルを使用しても可能であるため、この API は PAC ファイルのアプローチのみをサポートしています。 この API は Chrome の `proxy` API と互換性がないため、この API は `browser` の名前空間でのみ使用できます。

#### tabs

* Firefox では、`tabs.executeScript()` または `tabs.insertCSS()` に渡される相対 URL は、現在のページ URL を基準として解決されます。 Chrome では、これらの URL は拡張機能のベース URL を基準として解決されます。 クロスブラウザーで動作するようにするには、拡張機能のルートから絶対 URL として次のように指定します:

```html
/path/to/script.js
```

* Firefox では、`tabs.query()` を使用して URL でタブにクエリを行うには、`"tabs"`権限が必要です。 Chrome では、`"tabs"`権限がなくても可能ですが、URL がホスト権限と一致するタブに制限されます。
* Firefox では、`beforeunload` イベントの後に `tabs.remove()` の promise が実行されますが、Chrome では コールバック が `beforeunload` を待ちません。

#### webRequest

* Firefox では、元の URL が`http:`または`https:`を使用する場合にのみ、リクエストをリダイレクトできます。
* Firefox では、システム要求（拡張機能のアップグレードや検索バーの提案など）でイベントが発生しません。 Firefox 57 以降では、Firefox はプロキシ認証のために`webRequest.onAuthRequired` (en-US)を遮断する必要がある拡張機能の例外を生成します。`webRequest.onAuthRequired` (en-US)のドキュメントをご覧ください。
* Firefox では、拡張機能が外部 URL (例 HTTPS) から拡張機能ページにリダイレクトする場合、拡張機能の manifest.json ファイルは、その拡張機能ページの URL を一覧にした web\_accessible\_resources キーを含むことが必須です。 その URL にあらゆるウェブサイトからリンクやリダイレクトされる可能性があり、拡張機能は、入力データ(例えば POST データ) を、ただ通常のウェブページがそうするように、アントラステッドなソースからとして取り扱うべきであるのに注意します。

#### windows

* Firefox では、`onFocusChanged` はフォーカス変更に対して複数回トリガされます。
### その他の非互換性

#### CSS 内の URL

Firefox は、挿入されたページからではなく、CSS ファイル自体からの相対パスで、挿入された CSS ファイルの URL を解決します。

#### 付加的な非互換性

Firefox は、バックグラウンドページからの`alert()`, `confirm()` または `prompt()`の使用をサポートしていません。

#### web\_accessible\_resources

chrome では、リソースが web\_accessible\_resources にリストされている場合、`chrome-extension://<your-extension-id>/<path/to/resource>`としてアクセスできます。この場合 extension ID は、指定された拡張機能に対して固定されています。

Firefox ではそうではなく、Firefox のすべてのインスタンスごとに異なるランダムな UUID を使用してアクセスできるように実装されています:`moz-extension://<random-UUID>/<path/to/resource>` このランダム性により、特定の拡張機能の URL を別のドメインの CSP ポリシーに追加するなど、いくつかのことをできなくなります。

#### マニフェストの"key"プロパティ

パッケージされていない拡張機能を使用する場合、Chrome はマニフェストに"key"プロパティを追加して、異なるマシン間で extension ID を固定することができます。 これは主に web\_accessible\_resources で作業する場合に便利です。 Firefox は web\_accessible\_resources にランダムな UUID を使用するため、このプロパティはサポートされていません。

#### コンテンツスクリプトのリクエストは、コンテンツページではなく、拡張機能のコンテキストで発生する

chrome では、コンテンツスクリプトから`/api`のような相対 URL にリクエストが呼び出されたとき（たとえば、`fetch()`を使用して）、`https://example.com/api`に送信されます。Firefox では、絶対 URL を指定する必要があります。

manifest.json のキー
-----------------

メインのmanifest.json ページには、manifest.json のキーのブラウザーサポートを説明する表が含まれています。 特定のキーのサポートに関する警告がある場合、表にアスタリスク "\*"付きで示され、キーのリファレンスページには注意事項が説明されています。

これらの表は、 GitHub で JSON ファイルとして保存された互換性データから生成されます。

ネイティブメッセージング
------------

### コマンドライン引数

Linux と Mac では、Chrome は `chrome-extension://[extensionID]` の形式で、起動した拡張機能のオリジンとなる 1 つの引数をネイティブアプリに渡します。 これにより、アプリは拡張機能を識別できます。

Windows では、Chrome は 2 つの引数を受け取ります。1 つ目は拡張機能のオリジンで、2 つ目はアプリを起動した Chrome ネイティブウィンドウへのハンドルです。

### allowed\_extensions

Chrome では、アプリマニフェストの `allowed_extensions` キーは、代わりに`allowed_origins` と呼ばれています。

### アプリマニフェストの位置

Chrome は、別の場所でアプリのマニフェストを見つけることを期待しています。 Chrome ドキュメントの「ネイティブメッセージングホストの場所」をご覧ください。


クリップボードとのやりとり - Mozilla

クリップボードとのやりとり
=============

Limited availability
--------------------

This feature is not Baseline because it does not work in some of the most widely-used browsers.

* Learn more
* See full compatibility
* Report feedback
拡張機能でクリップボードを扱うのは、Web API の `document.execCommand` メソッド（非推奨）から `navigator.clipboard` メソッドに移行しています。

**メモ:** `navigator.clipboard` API は最近追加された仕様であり、すべてのブラウザーで完全に実装されているとは限りません。この記事ではいくつかの制限事項を説明しますが、API があなたのニーズをサポートしていることを確認するために、使用する前に必ず各メソッドの互換性テーブルを確認するようにしてください。

2 つの API の違いは、`document.execCommand` がキーボードのコピー、カット、ペーストの操作に似ていて、ウェブページとクリップボード間でデータを交換するのに対し、`navigator.clipboard` はクリップボードとの間で任意のデータの書き込みと読み出しを行うことです。

`navigator.clipboard` は、読み書きのためのメソッドを個別に提供しています。

* テキストを扱う場合は `navigator.clipboard.readText()` と `navigator.clipboard.writeText()` が使えます。
* 画像、リッチテキスト、HTML、その他のリッチコンテンツを扱う場合は `navigator.clipboard.read()` と `navigator.clipboard.write()` が使えます。

しかし、`navigator.clipboard.readText()` と `navigator.clipboard.writeText()` はすべてのブラウザーで動作しますが、`navigator.clipboard.read()` と `navigator.clipboard.write()` は動作するとは限りません。例えば、執筆時点の Firefox では、`navigator.clipboard.read()` と `navigator.clipboard.write()` は完全に実装されていないため、次のことを留意する必要があります。

* 画像を扱う場合は、クリップボードに画像を書き込むために `browser.clipboard.setImageData()` を使い、ウェブページに画像を貼り付けるために `document.execCommand("paste")` を使います。
* リッチコンテンツ（HTML、画像を含むリッチテキストなど）をクリップボードに書き込むには、`document.execCommand("copy")` か `document.execCommand("cut")` を使います。その後、`navigator.clipboard.read()` （推奨）または `document.execCommand("paste")` を使ってクリップボードからコンテンツを読み取ります。
クリップボードへの書き込み
-------------

ここでは、クリップボードにデータを書き込むためのオプションについて説明します。

### Clipboard API を使用する

Clipboard API は、拡張機能から任意のデータをクリップボードに書き込むものです。この API を使用するには、`manifest.json` ファイルに `"clipboardRead"` または `"clipboardWrite"` というパーミッションを設定する必要があります。この API は安全なコンテキストでのみ利用可能であるため、`http:` ページで動作するコンテンツスクリプトからは使用できず、`https:` ページでのみ使用できます。

ページスクリプトの場合、Web API の `navigator.permissions` を使用して `"clipboard-write"` パーミッションを要求する必要があります。そのパーミッションは、`navigator.permissions.query()` を使って確認することができます。

```js
navigator.permissions.query({ name: "clipboard-write" }).then((result) => {
  if (result.state === "granted" || result.state === "prompt") {
    /\* write to the clipboard now \*/
  }
});
```

**メモ:** `clipboard-write` のパーミッション名は Firefox ではサポートされておらず、Chromium ブラウザーでのみサポートされています。

この関数は、文字列を受け取り、それをクリップボードに書き込みます。

```js
function updateClipboard(newClip) {
  navigator.clipboard.writeText(newClip).then(
    () => {
      /\* clipboard successfully set \*/
    },
    () => {
      /\* clipboard write failed \*/
    },
  );
}
```

### execCommand() を使用する

`document.execCommand()` メソッドの `"cut"` と `"copy"` コマンドは、クリップボードの内容を選択した素材に置き換えるために使用されます。これらのコマンドは、ユーザーアクションに対する短時間のイベントハンドラー（例えば、クリックハンドラー）で特別な許可なしに使用することができます。

例えば、以下のような HTML を含むポップアップがあったとします。

```html
<input id="input" type="text" /> <button id="copy">Copy</button>
```

`"copy"` ボタンで `<input>` 要素の内容をコピーさせるには、次のようなコードを使用します。

```js
function copy() {
  let copyText = document.querySelector("#input");
  copyText.select();
  document.execCommand("copy");
}

document.querySelector("#copy").addEventListener("click", copy);
```

`execCommand()` の呼び出しはクリックイベントハンドラーの中にあるので、特別な権限は必要ありません。

しかし、その代わりに、アラームからコピーを起動させたとしましょう。

```js
function copy() {
  let copyText = document.querySelector("#input");
  copyText.select();
  document.execCommand("copy");
}

browser.alarms.create({
  delayInMinutes: 0.1,
});

browser.alarms.onAlarm.addListener(copy);
```

ブラウザーによっては、うまく動作しないことがあります。Firefox では動作せず、コンソールに次のようなメッセージが表示されます。

`document.execCommand('cut'/'copy') was denied because it was not called from inside a short running user-generated event handler.`

このユースケースを有効にするには、`"clipboardWrite"` パーミッションを要求する必要があります。つまり `"clipboardWrite"` は、ユーザーアクションのための短時間のイベントハンドラーの外側でクリップボードに書き込むことを可能にします。

**メモ:** `document.execCommand()` は、`type="hidden"` の入力フィールド、HTML5 属性の `"hidden"`、または `"display: none;"` を使った CSS ルールにマッチするものでは機能しません。したがって、`span`, `div`, `p` タグに「クリップボードにコピー」ボタンを追加するには、入力の位置を絶対座標に設定し、ビューポートの外に移動させるなどの回避策をとる必要があります。

### 特定のブラウザーにおける留意事項

クリップボードなどの API は日進月歩で進化しているため、ブラウザーによって動作にばらつきがあります。

Chrome の場合:

* ユーザーが作成したイベントハンドラーの外でクリップボードに書き込む場合でも、`"clipboardWrite"` は必要ありません。

Firefox の場合:

* `navigator.clipboard.write()` はサポートされていません。

詳細はブラウザーの互換性を参照してください。

クリップボードからの読み込み
--------------

ここでは、クリップボードからデータを読み込んだり、貼り付けたりするためのオプションについて説明します。

### Clipboard API を使用する

Clipboard API の `navigator.clipboard.readText()` と `navigator.clipboard.read()` メソッドを使うと、安全なコンテキストでクリップボードから任意のテキストまたはバイナリデータを読み取ることができます。これにより、編集可能な要素に貼り付けることなく、クリップボードのデータにアクセスすることができます。

一度 Permissions API から `"clipboard-read"` パーミッションを取得すると、クリップボードから簡単に読み取ることができるようになります。例えば、このコードのスニペットはクリップボードからテキストを取得し、ID が `"outbox"` の要素の内容をそのテキストで置き換えます。

```js
navigator.clipboard
  .readText()
  .then((clipText) => (document.getElementById("outbox").innerText = clipText));
```

### execCommand() を使用する

`document.execCommand("paste")` を使用するには、拡張機能には `"clipboardRead"` パーミッションが必要です。これは、`click` や `keypress` などのユーザーが生成したイベントハンドラーから `"paste"` コマンドを使用する場合でも同様です。

このような内容を含む HTML を考えてみましょう。

```html
<textarea id="output"></textarea> <button id="paste">Paste</button>
```

ユーザーが `"paste"` の `<button>` をクリックしたときに、クリップボードから ID が `"output"` の `<textarea>` 要素の内容を設定するには、次のようなコードを使用します。

```js
function paste() {
  let pasteText = document.querySelector("#output");
  pasteText.focus();
  document.execCommand("paste");
  console.log(pasteText.textContent);
}

document.querySelector("#paste").addEventListener("click", paste);
```

### 特定のブラウザーにおける留意事項

Firefox はバージョン 54 から `"clipboardRead"` パーミッションをサポートしていますが、コンテンツ編集可能モードの要素への貼り付けのみをサポートしており、コンテンツスクリプトの場合は `<textarea>` でのみ機能します。バックグラウンドスクリプトでは、どの要素もコンテンツ編集可能モードに設定できます。

ブラウザーの互換性
---------

### api.Clipboard

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

### webextensions.api.clipboard

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

関連情報
----

* Clipboard API
* Permissions API
* コンテンツを編集可能にする
* `contenteditable`


ネイティブマニフェスト - Mozilla

ネイティブマニフェスト
===========

ネイティブマニフェストは特別な形式の JSON ファイルで、拡張機能のインストールプロセスの範囲外の方法でユーザーのコンピューターに配布されます。例えば、ネイティブマニフェストは端末の管理者やネイティブアプリケーションのインストーラーによって配布されます。

3 つの異なる種類のネイティブマニフェストがあります:

| Native messaging マニフェスト | native messaging と呼ぶ機能を可能にします、ここでは拡張機能は端末にインストールされたネイティブアプリとやりとりできます。 |
| --- | --- |
| Managed storage マニフェスト | `storage.managed` (en-US) API 使って拡張機能がアクセスする読み込み専用データを定義します。 |
| PKCS #11 マニフェスト | 拡張機能が `pkcs11` API を使って PKCS #11 セキュリティモジュールを列挙して、Firefox にインストールできるようにします。 |

すべてのネイティブマニフェスト用に、ブラウザーがマニフェストを見つけられるように調整する必要があります。 マニフェストの場所 のセクションでこのルールを述べています。

Native messaging マニフェスト
-----------------------

native messaging マニフェストは以下のプロパティを含む単一の JSON オブジェクトです:

| 名前 | 種類 | 説明 |
| --- | --- | --- |
| `name` | String | ネイティブアプリケーションの名前です。この名前は拡張機能の `runtime.connectNative()` (en-US) か `runtime.sendNativeMessage()` (en-US) に渡される名前と一致している必要があります。OS X と Linux では、native messaging マニフェストの（.json 拡張子を除いた）ファイル名とも一致していなければなりません。Windows では、native messaging マニフェストの場所を記した作成済みレジストリキーの名前と一致している必要があります。次の正規表現にマッチする必要があります: "^\w+(\.\w+)\*$" つまり、名前には(大文字か小文字の)英数字とアンダースコア、ドットのみ含めることができます。最初または最後の文字にドットを使用することはできず、ドットを 2 つ以上連続させることもできません。 |
| `description` | String | ネイティブアプリケーションの説明です。 |
| `path` | String | ネイティブアプリケーションのパスです。Windows では、マニフェスト自身からの相対パスを指定することもできます。OS X や Linux では絶対パスでなければなりません。 |
| `type` | String | 拡張機能にアプリケーションが接続するために使用する方法を記述します。現在のところ、"stdio" のみが指定可能です。これはアプリケーションが標準入力 (stdin) を介してメッセージを受信し、標準出力 (stdout) を使用してメッセージを送信することを示します。 |
| `allowed_extensions` | Array of String | Add-on ID の配列です。配列中のそれぞれの値はこのネイティブアプリケーションとの通信が許可されている拡張機能を表します。つまり、作成する拡張機能の manifest.json ファイルに applications キーを含めたくなるものと思われるため、開発中に明示的な ID を設定しておくと良いでしょう。 |

例として、"ping\_pong" ネイティブアプリケーションのマニフェストを以下に示します:

```json
{
  "name": "ping\_pong",
  "description": "Example host for native messaging",
  "path": "/path/to/native-messaging/app/ping\_pong.py",
  "type": "stdio",
  "allowed\_extensions": ["ping\_pong@example.org"]
}
```

この設定では、"ping\_pong@example.org" という ID の拡張機能が "ping\_pong" という名前を適切な `runtime` API 関数に渡すことで接続を許可されます。 アプリケーション自体は "/path/to/native-messaging/app/ping\_pong.py" にあります。

Managed storage マニフェスト
----------------------

managed storage マニフェストには次のプロパティを含む単一の JSON オブジェクトです:

| 名前 | 種類 | 説明 |
| --- | --- | --- |
| `name` | String | ストレージにアクセスできる拡張機能の ID で、拡張機能の applications キーで指定したのと同じものです。 |
| `description` | String | 人間が読める説明で、Firefox には無視されます。 |
| `type` | String | "storage" でなければなりません。 |
| `data` | Object | JSON オブジェクトで、その中にあらゆる有効な JSON 値(文字列、数値、真偽値、配列、オブジェクトを含む)が入ります。これは `browser.storage.managed` ストレージ領域内のデータになります。 |

例えば:

```json
{
  "name": "favourite-colour-examples@mozilla.org",
  "description": "ignored",
  "type": "storage",
  "data": {
    "colour": "management thinks it should be blue!"
  }
}
```

この JSON マニフェストでは、"favourite-colour-examples@mozilla.org" 拡張機能は次のようなコードを使ってデータにアクセスできます:

```js
var storageItem = browser.storage.managed.get("colour");
storageItem.then((res) => {
  console.log(`Managed colour is: ${res.colour}`);
});
```

PKCS #11 マニフェスト
---------------

PKCS #11 マニフェストは以下のプロパティを持った JSON オブジェクトを含むファイルです:

| 名前 | 種類 | 説明 |
| --- | --- | --- |
| `name` | String | PKCS #11 モジュールの名前です。`pkcs11` API で使われている名前と一致している必要があります。OS X と Linux では、マニフェストの (拡張子を除いた) ファイル名とも一致していなければなりません。Windows では、マニフェストの場所を記した作成済みレジストリキーの名前と一致している必要があります。次の正規表現にマッチする必要があります: "^\w+(\.\w+)\*$" つまり、名前には(大文字か小文字の)英数字とアンダースコア、ドットのみ含めることができます。最初または最後の文字にドットを使用することはできず、ドットを 2 つ以上連続させることもできません。 |
| `description` | String | モジュールの説明です。ブラウザー UI (例えば、Firefox の "Security Devices" ダイアログ) で表示される、読みやすい名前を付けるのに使われます。 |
| `path` | String | モジュールのパスです。Windows では、マニフェスト自身からの相対パスを指定することもできます。OS X や Linux では絶対パスでなければなりません。 |
| `type` | String | "pkcs11" でなければなりません。 |
| `allowed_extensions` | Array of String | Add-on ID の配列です。配列中のそれぞれの値はモジュールとの通信が許可されている拡張機能を表します。つまり、作成する拡張機能の manifest.json ファイルに applications キーを含めたくなるものと思われるため、開発中に明示的な ID を設定しておくと良いでしょう。 |

例えば:

```json
{
  "name": "my\_module",
  "description": "My test module",
  "type": "pkcs11",
  "path": "/path/to/libpkcs11testmodule.dylib",
  "allowed\_extensions": ["my-extension@mozilla.org"]
}
```

この JSON マニフェストでは、"my\_module.json" として保存すれば、"my-extension@mozilla.org" 拡張機能はこのようなコードから "/path/to/libpkcs11testmodule.dylib" のセキュリティーモジュールをインストールすることもできます:

```js
browser.pkcs11.installModule("my\_module");
```

マニフェストの場所
---------

Linux と Mac OS X では、マニフェストを特定の場所に保管する必要があります。Windows では、マニフェストの場所を指定するレジストリキーを作成する必要があります。

詳しいルールはどのマニフェストの種類でも同じですが、例外として最後から 2 番目のパスのコンポーネントはマニフェストの種類を特定します。下記の例では、3 種類のそれぞれの形を示しています。すべての例において、*<name>* はマニフェスト内の `name` プロパティの値です。

### Windows

グローバルな設定としては、以下の名前のレジストリキーを作成します:
```

HKEY_LOCAL_MACHINE\SOFTWARE\Mozilla\NativeMessagingHosts\<name>
HKEY_LOCAL_MACHINE\SOFTWARE\Mozilla\ManagedStorage\<name>
HKEY_LOCAL_MACHINE\SOFTWARE\Mozilla\PKCS11Modules\<name>
```

このキーにマニフェストへのパスを示す単一の既定の値を設定します。

Firefox バージョン 64 以降では、32 ビットレジストリ view (Wow6432Node) がこれらのキーの中でまずチェックされ、"native" レジストリ view が続いてチェックされます。いずれの view にしてもアプリケーションにふさわしいレジストリを使用してください。

*Firefox バージョン 63 　以前では、もし 32 ビットのアプリケーションであっても、このキーは Wow6432Node 下に作成しないで下さい。以前のバージョンのブラウザは常に 32-bit エミュレーションではなくレジストリの "native" view 下のキーを探します。確実に "native" view にキーを作成するために、KEY\_WOW64\_64KEY または KEY\_WOW64\_32KEY フラグを RegCreateKeyEx に渡すことができます。Accessing an Alternate Registry View を参照して下さい。*

ユーザごとの設定としては、以下の名前のレジストリキーを作成します:
```

HKEY_CURRENT_USER\SOFTWARE\Mozilla\NativeMessagingHosts\<name>
HKEY_CURRENT_USER\SOFTWARE\Mozilla\ManagedStorage\<name>
HKEY_CURRENT_USER\SOFTWARE\Mozilla\PKCS11Modules\<name>
```

このキーにマニフェストへのパスを示す単一の既定の値を設定します。

### Mac OS X

グローバルな設定としては、マニフェストを以下に配置します:
```

/Library/Application Support/Mozilla/NativeMessagingHosts/<name>.json
/Library/Application Support/Mozilla/ManagedStorage/<name>.json
/Library/Application Support/Mozilla/PKCS11Modules/<name>.json
```

個人ごとの設定としては、マニフェストを以下に配置します:
```

~/Library/Application Support/Mozilla/NativeMessagingHosts/<name>.json
~/Library/Application Support/Mozilla/ManagedStorage/<name>.json
~/Library/Application Support/Mozilla/PKCS11Modules/<name>.json
```

### Linux

グローバルな設定としては、マニフェストを以下のいずれかに配置します:
```

/usr/lib/mozilla/native-messaging-hosts/<name>.json
/usr/lib/mozilla/managed-storage/<name>.json
/usr/lib/mozilla/pkcs11-modules/<name>.json
```

あるいは:
```

/usr/lib64/mozilla/native-messaging-hosts/<name>.json
/usr/lib64/mozilla/managed-storage/<name>.json
/usr/lib64/mozilla/pkcs11-modules/<name>.json
```

個人ごとの設定としては、マニフェストを以下に配置します:
```

~/.mozilla/native-messaging-hosts/<name>.json
~/.mozilla/managed-storage/<name>.json
~/.mozilla/pkcs11-modules/<name>.json
```


拡張機能の中身 - Mozilla

拡張機能の中身
=======

拡張機能は複数のファイルで構成されており、それらのファイルが配布・インストール用にパッケージ化されたものです。この記事では、拡張機能に含まれるファイルについて簡単に説明します。

manifest.json
-------------

どの拡張機能においても不可欠な唯一のファイルです。このファイルには名前やバージョン、必要とするパーミッションなど、拡張機能に関する基本的なメタデータが含まれます。加えて、アドオンに含まれている他のファイルへの参照も含んでいます。

このマニフェストには、下記のファイルに対する参照を含めることができます。

バックグラウンドスクリプト

長時間動作するロジックを実装します。

アイコン

拡張機能とそれが定義するボタンのアイコン。

サイドバー、ポップアップ、オプションページ

色々な UI コンポーネントを提供する HTML 文書です。

コンテンツスクリプト

拡張機能に含まれる JavaScript で、ウェブページに挿入するもの

ウェブでアクセス可能なリソース

まとめられたコンテンツをウェブページやコンテンツスクリプトにアクセス可能とする。

![ウェブ拡張機能の構成要素。manifest.JSON は、すべての拡張機能で存在する必要があります。これは、バックグラウンドページ、コンテンツスクリプト、ブラウザーアクション、ページアクション、オプションページ、およびウェブアクセス可能リソースへのポインターを提供します。バックグラウンドページは HTML と JS で構成されます。コンテンツスクリプトは、 JS と CSS から構成されています。ユーザがアイコンをクリックすることでブラウザアクションとページアクションが発生し、その結果表示されるポップアップが HTML、CSS、JS で構成されています。オプションページは、HTML、CSS、JS で構成されます。](/ja/docs/Mozilla/Add-ons/WebExtensions/Anatomy_of_a_WebExtension/webextension-anatomy.png)

詳細は `manifest.json` のリファレンスを参照してください。

マニフェストに載っているものの他に、拡張機能はサポートするファイルの拡張機能ページを持つことができます。

バックグラウンドスクリプト
-------------

拡張機能には、特定のウェブページやブラウザーウィンドウの寿命とは無関係に、長期的な状態を維持したり、長期的な処理を実行したりする必要があることがよくあります。そのためのものがバックグラウンドスクリプトです。

バックグラウンドスクリプトには、永続的なものと永続的でないものがあります。永続的なバックグラウンドスクリプトは、拡張機能が読み込まれるとすぐに読み込まれ、拡張機能が無効化されるかアンインストールされるまで読み込まれたままになります。このバックグラウンドスクリプトの動作は、Manifest V2 でのみ利用可能です。永続的でないバックグラウンドスクリプトは、イベントに応答するために必要なときに読み込まれ、アイドル状態になるとアンロードされます。このバックグラウンド スクリプト動作は、Manifest V2 ではオプションであり、Manifest V3 で利用可能な唯一のバックグラウンド スクリプト動作です。

スクリプト中で WebExtension API を、あらかじめ要求された必要なパーミッションの限りにおいて使うことができます。

詳しくはバックグラウンドスクリプト (en-US)の記事をご覧ください。

サイドバー、ポップアップ、オプションページ
---------------------

拡張機能には HTML 文書で中身を決めたいろいろなユーザーインターフェイスコンポーネントを入れる事ができます。

サイドバー

ブラウザーウィンドウの左側の、ウェブページの横に表示されるペインです。

ポップアップ

ユーザーがツールバーボタンやアドレスバーボタンをクリックした時に表示されるダイアログです。

オプションページ

ユーザーがブラウザーネイティブのアドオンマネージャー内でアドオン設定にアクセスする時に表示するページです。

各コンポーネント用に、HTML ファイルと、それを指定する manifest.json の特定プロパティを作成します。HTML ファイルには、通常のウェブページと同様に、CSS と JavaScript ファイルを入れる事ができます。

これらのすべては拡張機能ページの一種で、通常のウェブページと異なり、JavaScript はバックグラウンドスクリプトと同じ権限で WebExtension API を使用できます。

拡張機能ページ
-------

拡張機能に事前定義された UI コンポーネントに属さない HTML 文書を入れることもできます。サイドバー、ポップアップ、オプションページに提供されるドキュメントと違って、これは `manifest.json` 内にエントリーがないです。しかし、バックグラウンドスクリプトと同様に権限のある WebExtension API のすべてにアクセスできます。

典型的には `windows.create()` (en-US) か `tabs.create()` を使ってページを読み込みます。

詳しくは拡張機能ページを見てください。

コンテンツスクリプト
----------

ウェブページにアクセスして操作するにはコンテンツスクリプトが用いられます。コンテンツスクリプトは ウェブページに読み込まれて実行されます。

コンテンツスクリプトはアドオンが提供するスクリプトであり、ウェブページのコンテキスト内で動作します。このため、`<script>` 要素で取得されたものなど、そのページ自身が読み込んだスクリプトとは異なります。

ウェブページに読み込まれた通常のスクリプトと同様に、コンテンツスクリプトも ウェブページの DOM へアクセスできます。

通常のスクリプトと異なるのは、コンテンツスクリプトで以下のことが可能な点です。

* クロスドメインの XHR リクエストを作成できる
* WebExtension API の小さなサブセットを利用できる
* バックグラウンドスクリプトとメッセージの交換ができ、この方法ですべての WebExtension API に間接的にアクセスできる

コンテンツスクリプトから通常のスクリプトに直接アクセスすることはできませんが、標準化されている `window.postMessage()` API を用いれば、スクリプト間でメッセージを交換することが可能です。

ここでの議論対象であるコンテンツスクリプトとは基本的に JavaScript のことを指していますが、先ほどと同じ方法で ウェブページに CSS を差し込むこともできます。

詳しくは コンテンツスクリプトの記事を参照してください。

ウェブでアクセス可能なリソース
---------------

ウェブでアクセス可能なリソースとは、拡張機能にインクルードしてコンテンツスクリプトや ウェブページのスクリプトからアクセスできるリソースのことであり、代表的なものは画像や HTML / CSS / JavaScript です。ウェブでアクセス可能なリソースは、特殊な URI スキームを介して ウェブページのスクリプトやコンテンツスクリプトから参照できます。

例えばコンテンツスクリプトから ウェブページ内に画像を挿入したい場合、拡張機能に画像をインクルードしてウェブでアクセス可能とし、画像を `src` 属性で参照する `img` タグをコンテンツスクリプトが作成して ウェブページに追加する、といったシナリオが考えられます。

詳細は、 `manifest.json` のキーである `"web_accessible_resources"` のドキュメントを見てください。


manifest.json - Mozilla

manifest.json
=============

manifest.json ファイルは、WebExtension API を使う拡張機能に必ず含めなければならない唯一のファイルです。

manifest.json を使うことで、拡張機能の名前やバージョンといった基本的なメタデータを指定したり、拡張機能の機能的な側面として、例えばバックグラウンドスクリプトやコンテンツスクリプト、ブラウザーアクションを指定することもできます。

これは JSON形式のファイルですが、１つ例外があります: "`//`"-形式のコメントが許可されています。

manifest.json のキー一覧は次の通り:

* author
* background
* browser\_action
* browser\_specific\_settings
* chrome\_settings\_overrides
* chrome\_url\_overrides
* commands
* content\_scripts
* content\_security\_policy
* default\_locale
* description
* developer
* devtools\_page
* homepage\_url
* incognito
* manifest\_version
* name
* omnibox
* optional\_permissions
* options\_ui
* page\_action
* permissions
* protocol\_handlers
* short\_name
* sidebar\_action
* version
* version\_name
* web\_accessible\_resources
### manifest.json のキーの注意点

* 必須のキーは、`"manifest_version"`, `"version"`, `"name"` だけです。
* `"default_locale"` は `"_locales"` ディレクトリーが存在する場合は必要ですが、そうでない場合は不要です。
* `"applications"` は Google Chrome でサポートされていません。
### manifest.json の情報を実行時に参照する

拡張機能の manifest には、拡張機能の JavaScript から `runtime.getManifest()` (en-US) 関数を使ってアクセスできます:

```js
browser.runtime.getManifest().version;
```

例
-

下記のブロックには一般的な manifest keys の基本文法を示します。

**メモ:** これはコピー・ペーストできるような使われ方のつもりではないのに注意してください: どのキーが必要かは開発している拡張機能に依存します。

完全な例は Example extensions を見てください。

```json
{
  "applications": {
    "gecko": {
      "id": "addon@example.com",
      "strict\_min\_version": "42.0"
    }
  },

  "background": {
    "scripts": ["jquery.js", "my-background.js"],
    "page": "my-background.html"
  },

  "browser\_action": {
    "default\_icon": {
      "19": "button/geo-19.png",
      "38": "button/geo-38.png"
    },
    "default\_title": "Whereami?",
    "default\_popup": "popup/geo.html"
  },

  "commands": {
    "toggle-feature": {
      "suggested\_key": {
        "default": "Ctrl+Shift+Y",
        "linux": "Ctrl+Shift+U"
      },
      "description": "Send a 'toggle-feature' event"
    }
  },

  "content\_security\_policy": "script-src 'self' https://example.com; object-src 'self'",

  "content\_scripts": [
    {
      "exclude\_matches": ["\*://developer.mozilla.org/\*"],
      "matches": ["\*://\*.mozilla.org/\*"],
      "js": ["borderify.js"]
    }
  ],

  "default\_locale": "en",

  "description": "...",

  "icons": {
    "48": "icon.png",
    "96": "icon@2x.png"
  },

  "manifest\_version": 2,

  "name": "...",

  "page\_action": {
    "default\_icon": {
      "19": "button/geo-19.png",
      "38": "button/geo-38.png"
    },
    "default\_title": "Whereami?",
    "default\_popup": "popup/geo.html"
  },

  "permissions": ["webNavigation"],

  "version": "0.1",

  "web\_accessible\_resources": ["images/my-image.png"]
}
```

ブラウザーの互換性
---------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

マニフェストキーとサブキーのすべてを見るには、完全な manifest.json ブラウザー互換テーブルを見てください。


マッチパターン - Mozilla

マッチパターン
=======

マッチパターンは URL のグループを指定する方法です。マッチパターンはいくつかの URL にマッチします。マッチパターンは WebExtensions API を使う拡張機能向けに、いくつかの場所で使用されます。特にコンテンツスクリプトをロードする文書を指定するときや、`webRequest` リスナーを追加する URL を指定する時に使用します。

マッチパターンを使用する API は通常マッチパターンのリストを受け取り、URL がパターンのいずれかにマッチする場合は適切なアクションを実行します。たとえば manifest.json 内の `content_scripts` キーを参照してください。

マッチパターンの構造
----------

**メモ:** ブラウザーによってはサポートしていないスキームがあります。
 詳しくはブラウザー互換性テーブルを見てください。

すべてのマッチパターンは文字列で指定します。特別な値 `<all_urls>` を除き、マッチパターンは３つの部分から成り立っています。 *scheme*, *host*, *path* です。 scheme と host の間は `://` で句切られます。
```

<scheme>://<host><path>
```

### scheme

*scheme* 部は２つの形式のうち、どちらかを指定します。

| 形式 | マッチするもの |
| --- | --- |
| `*` | "http"か"https"のみ、いくつかのブラウザーでは "ws" と "wss" |
| `http`, `https`, `ws`, `wss`, `ftp`, `ftps`, `data`, `file` のうちどれか 1 つ | 指定したスキームのみ |

### host

*host*部は３つ形式のうちどれか 1 つを取ります。

| 形式 | マッチするもの |
| --- | --- |
| `*` | すべてのホスト |
| `*.` に続くホスト名の一部分 | 指定したホストと任意のサブドメイン |
| ワイルドカード無しの完全なホスト名 | 指定したホストのみ |

*host 部*にはポート番号は入りません。

"file"スキームだけは、*host*部はオプションです。

ワイルドカード "\*" は *host* の最初のみに適用できることに注意してください。

### path

パス部は `/` で開始しなければいけません。

その後、ワイルドカード `*` と、URL パスとして許可される文字とを組み合わせたものが続けて入るかもしれません。ホスト部と異なり、パス部はワイルドカード `*` を途中や終わりに含むことができて、さらに 2 つ以上の `*` を含められます。

*path* の値は、URL パスに URL クエリーストリングを加えた文字列と一致します。クエリーストリングがある場合、それらの間に `?` を含んでいます。例えば、`foo.bar` で終わる URL パスのあらゆるドメインに URL マッチしたい場合、マッチパターンは `['*://*/*foo.bar', '*://*/*foo.bar?*']`です。単に `bar*` ではなく `?*` は必要で、最後の `*` は URL クエリーストリングにも、URL パスの部分でないものにも適用するためです。

URL フラグメント識別子や、`#` の後についているものは、*path* とみなされません。

**メモ:** path パターン文字列にポート番号を含めるべきではありません。`http://localhost:1234/*` のようにポート番号を追加するとマッチパターンは無視されます。しかし、`http://localhost:1234` は `http://localhost/*` にマッチします。

### <all\_urls>

特殊な値である `<all_urls>` は、サポートしているすべての scheme の URL（ "http", "https", "file", "ftp", "app" ）にマッチします。

例
-

| パターン | マッチする例 | マッチしない例 |  |
| --- | --- | --- | --- |
| `<all_urls>`すべての URL にマッチ | `http://example.org/` `https://a.org/some/path/` `ws://sockets.somewhere.org/` `wss://ws.example.com/stuff/` `ftp://files.somewhere.org/` `ftps://files.somewhere.org/` | `resource://a/b/c/` (サポートされていないスキーム) |  |
| `*://*/*`すべての HTTP, HTTPS, WebSocket URL にマッチ | `http://example.org/` `https://a.org/some/path/` `ws://sockets.somewhere.org/` `wss://ws.example.com/stuff/` | `ftp://ftp.example.org/` (マッチしないスキーム)`ftps://ftp.example.org/` (マッチしないスキーム)`file:///a/` (マッチしないスキーム) |  |
| `*://*.mozilla.org/*`"mozilla.org" かそのサブドメインでホストされている HTTP, HTTPS, WebSocket の URL にマッチ | `http://mozilla.org/` `https://mozilla.org/` `http://a.mozilla.org/` `http://a.b.mozilla.org/` `https://b.mozilla.org/path/` `ws://ws.mozilla.org/` `wss://secure.mozilla.org/something` | `ftp://mozilla.org/` (マッチしないスキーム)`http://mozilla.com/` (マッチしないホスト)`http://firefox.org/` (マッチしないホスト) |  |
| `*://mozilla.org/`HTTP や HTTPS や WebSocket の"mozilla.org/"のホストのみマッチ | `http://mozilla.org/` `https://mozilla.org/` `ws://mozilla.org/` `wss://mozilla.org/` | `ftp://mozilla.org/` (マッチしないスキーム)`http://a.mozilla.org/` (マッチしないホスト)`http://mozilla.org/a` (マッチしないパス) |  |
| `ftp://mozilla.org/`"ftp://mozilla.org/"のみマッチ | `ftp://mozilla.org` | `http://mozilla.org/` (マッチしないスキーム)`ftp://sub.mozilla.org/` (マッチしないホスト)`ftp://mozilla.org/path` (マッチしないパス) |  |
| `https://*/path`HTTPS URL で path が "path"のホストのみマッチ | `https://mozilla.org/path` `https://a.mozilla.org/path` `https://something.com/path` | `http://mozilla.org/path` (マッチしないスキーム)`https://mozilla.org/path/` (マッチしないパス)`https://mozilla.org/a` (マッチしないパス)`https://mozilla.org/` (マッチしないパス)`https://mozilla.org/path?foo=1` (URL クエリーストリングによりマッチしないパス) |  |
| `https://*/path/`あらゆるホスト上の HTTPS URL で、パスが "path/" で URL にクエリーストリングのないものにマッチ | `https://mozilla.org/path/` `https://a.mozilla.org/path/` `https://something.com/path`/ | `http://mozilla.org/path/` (マッチしないスキーム)`https://mozilla.org/path` (マッチしないパス)`https://mozilla.org/a` (マッチしないパス)`https://mozilla.org/` (マッチしないパス)`https://mozilla.org/path?foo=1` (URL クエリーストリングによりマッチしないパス) |  |
| `https://mozilla.org/*`HTTPS URL のみにマッチで、"mozilla.org"だけ、パスやクエリーストリングは問わない | `https://mozilla.org/` `https://mozilla.org/path` `https://mozilla.org/another` `https://mozilla.org/path/to/doc` `https://mozilla.org/path/to/doc?foo=1` | `http://mozilla.org/path` (マッチしないスキーム)`https://mozilla.com/path` (マッチしないホスト) |  |
| `https://mozilla.org/a/b/c/`この URL 、あるいはフラグメント付きのこの URL にのみマッチ | `https://mozilla.org/a/b/c/` `https://mozilla.org/a/b/c/#section1` | これ以外のすべて |  |
| `https://mozilla.org/*/b/*/`"mozilla.org"でホストされた HTTPS URL で、パスは中間のどこかに "b" を含むもの。クエリーストリングが / で終了していればそれにもマッチ | `https://mozilla.org/a/b/c/` `https://mozilla.org/d/b/f/` `https://mozilla.org/a/b/c/d/` `https://mozilla.org/a/b/c/d/#section1` `https://mozilla.org/a/b/c/d/?foo=/` `https://mozilla.org/a?foo=21314&bar=/b/&extra=c/` | `https://mozilla.org/b/*/` (マッチしないパス)`https://mozilla.org/a/b/` (マッチしないパス)`https://mozilla.org/a/b/c/d/?foo=bar` (URL クエリーストリングによりマッチしないパス) |  |
| `file:///blah/*`FILE URL でパスが "blah" で始まるもの | `file:///blah/` `file://blah/bleh` | `file:///bleh/` (マッチしないパス) |  |

### 無効なマッチパターン

| 無効なパターン | 理由 |  |
| --- | --- | --- |
| `resource://path/` | サポートされていないスキーム。 |  |
| `https://mozilla.org` | パスがない。 |  |
| `https://mozilla.*.org/` | "\*" はホストの先頭に使用する必要があります。 |  |
| `https://*zilla.org/` | ホスト内の "\*" は唯一の文字であるか、"."が続かなければいけません。 |  |
| `http*://mozilla.org/` | スキーム内の "\*" は唯一の文字であるべきです。 |  |
| `*://*` | パスが空: "`*://*/*`"であるべき。 |  |
| `file://*` | パスが空: "`file:///*`"であるべき 。 |  |

ブラウザーの互換性
---------

### scheme

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


ファイルの操作 - Mozilla

ファイルの操作
=======

拡張機能にて完全な機能を提供するのにファイルを操作する必要があるかもしれません。この記事ではファイルを扱うための 5 つのメカニズムを見ていきます:

* ユーザーが選択したファイルをダウンロードフォルダーにダウンロードする
* ウェブページのファイルピッカーを使ってファイルを開く
* ウェブページへのドラッグアンドドロップを使ってファイルを開く
* idb-file-storage ライブラリーを使ってファイルや blob を IndexedDB に保管する
* ユーザーのコンピューター上のネイティブアプリケーションにファイルを渡す

それぞれのメカニズムに対し、各 API ドキュメンテーションやガイド、対応する API の使用法の例のリファレンスと一緒に、使い方を紹介します。

ダウンロード API を使ってファイルをダウンロード
--------------------------

この仕組みでは、あなたのウェブサイト(や URL で決められたあらゆる場所)からユーザーのコンピューターにファイルを入手できます。主要なメソッドは `downloads.download()` で、その簡単なフォームで URL を受け付けて、その URL からユーザーの既定のダウンロードフォルダーにファイルをダウンロードします:

```js
browser.downloads.download({ url: "https://example.org/image.png" });
```

ユーザーが `saveAs` パラメーターで指定した場所にダウンロードさせることもできます。

**メモ:** Using URL.createObjectURL() you can also download files and blobs defined in your JavaScript, which can include local content retrieved from IndexedDB.

ダウンロード API はダウンロードをキャンセル、停止、再開、消去、削除することもできます; ダウンロードマネージャーでダウンロード済みのファイルを探します; コンピューターのファイルマネージャーでダウンロード済みのファイルを表示します; 関連付けられたアプリケーションでそのファイルを開きます。

この API を使うには manifest.json ファイルにて "downloads" API パーミッションが必要です。

 例: Latest download
 API リファレンス: downloads API

ファイルピッカーを使って拡張機能でファイルを開く
------------------------

ユーザーのコンピューターからのファイルを扱いたい場合、1 つの選択はコンピューターのファイルブラウザーを使ってユーザーがファイル選択できるようにすることです。新しいページを作るか既存のページにコードを挿入して、ファイルピッカーをユーザーに提供するため HTML `input` 要素の `file` タイプを使います。ユーザーがファイルを選ぶと、ページに関連付けられたスクリプトにより、DOM File API を使ってウェブアプリケーションと同様な方法でファイルの中身にページからアクセスできます。

 例: Imagify
 ガイド: Using files from web applications
 API リファレンス: HTML input element | DOM File API

**メモ:** 選択したフォルダーの全ファイルにアクセスや処理したい場合、`<input type="file" webkitdirectory="true"/>` を使ってフォルダーを選択して、そこにある全ファイルを返すようにできます。

ドラッグアンドドロップを使って拡張機能でファイルを開く
---------------------------

Web Drag and Drop API では別のファイルピッカーがあります。このメソッドを使うには、UI にフィットする 'drop zone' を設置して、dragenter (en-US), dragover (en-US), drop イベントのリスナーを要素に追加します。ドロップイベントのハンドラーでは、DataTransfer.files を使って dataTransfer プロパティから提供されたオブジェクトからユーザーにドロップされたあらゆるファイルに、コードからアクセスできます。すると DOM File API を使ってファイルにアクセス・操作することができます。

 例: Imagify
 ガイド: Using files from web applications | File drag and drop
 API リファレンス: DOM File API

IndexedDB ファイルストレージライブラリーを使ってローカルにファイル保存する
------------------------------------------

拡張機能がローカルにファイル保存する必要がある場合、idb-file-storage ライブラリーは 完結な Promise ベースのIndexedDB APIラッパーであり、ストレージやファイルや blob の取得を助けます。

Firefox では、このライブラリーは非標準の IDBMutableFile API のラッパーとして Promise ベースの API も提供します (IDBMutableFile API を使うと、拡張機能は IndexedDB データベースファイルオブジェクトの作成と保存ができて、それによりメモリーにファイルを読み込むことなくファイルの中身を読み書きできる API が提供されます)

このライブラリーのキーとなる機能は:

* IDBFileStorage インスタンスを返す getFileStorage。名前付きストレージが存在しない場合には作成します。
* IDBFileStorage。これはファイルを保存したり取得するメソッドを提供します、例えば次のもの:
    + データベース内の(フィルターされた)ファイルの list
    + データベースにファイルや blob を追加する put
    + データベースからファイルや blob を取得する get
    + データベースからファイルや blob を削除する remove

Store Collected Images の例ではこの機能の大半の使い方を示します。(IDBMutableFile は含まれていませんが、idb-file-storage examples にて、ライブラリーの他の多くの例と一緒に見ることができます)。

Store Collected Images の例では、画像コンテキストメニューを使ってユーザーが画像を追加できます。選択された画像はポップアップメニューに集められて、名前付きコレクションに保存されます。ツールバーボタンの(`browserAction`) ナビゲートコレクションのページを開き、そこでユーザーは選択を狭められるフィルターオプションを使って、保存された画像を見たり消去したりできます。この例を見てください。

/utils/ 内の image-store.js を見てライブラリーの動作を理解できます:

### store を作成して画像を保存する

```js
async function saveCollectedBlobs(collectionName, collectedBlobs) {
  const storedImages = await getFileStorage({ name: "stored-images" });

  for (const item of collectedBlobs) {
    await storedImages.put(`${collectionName}/${item.uuid}`, item.blob);
  }
}
```

`saveCollectedBlobs` is called when the user clicks save in the popup and has provided a name for the image collection. First, `getFileStorage` creates, if it does not exist already, or retrieves the IndexedDB database "stored-images" to the object `storedImages`. `storedImages.put` then adds each collected image to the database, under the collection name, using the blob's unique id (the file name). If the image being store has the same name as one already in the database, it is overwritten. If you want to avoid this, query the database first using `imagesStore.list()` with a filter for the file name, and, if the list returns a file, add a suitable suffix to the name of the new image to store a separate item.

### 保管された画像を表示用に取得する

```js
export async function loadStoredImages(filter) {
  const imagesStore = await getFileStorage({ name: "stored-images" });
  let listOptions = filter ? { includes: filter } : undefined;
  const imagesList = await imagesStore.list(listOptions);
  let storedImages = [];
  for (const storedName of imagesList) {
    const blob = await imagesStore.get(storedName);
    storedImages.push({ storedName, blobUrl: URL.createObjectURL(blob) });
  }
  return storedImages;
}
```

`loadStoredImages` is called when the user clicks view or reload in the navigate collection page. `getFileStorage` opens the "stored-images" database, then `imagesStore.list` gets a filtered list of the stored images. This list is then used to retrieve images with `imagesStore.get` and build a list to return to the UI.

Note the use of URL.createObjectURL(blob) to create a URL that references the image blob. This URL is then used in the UI (navigate-collection.jscollection.js) to display the image.

### 集まった画像を削除する

```js
async function removeStoredImages(storedImages) {
  const imagesStore = await getFileStorage({ name: "stored-images" });
  for (const storedImage of storedImages) {
    URL.revokeObjectURL(storedImage.blobUrl);
    await imagesStore.remove(storedImage.storedName);
  }
}
```

`removeStoredImages` is called when the user clicks delete in the navigate collection page. Again, `getFileStorage` opens the "stored-images" database then `imagesStore.remove` removes each image from the filtered list of images.

Note the use of URL.revokeObjectURL() to explicitly revoke the blob URL. This enables the garbage collector to free the memory allocated to the URL. If this is not done, the memory will not get returned until the page on which it was created is closed. If the URL was created in an extension's background page, this is not unloaded until the extension is disabled, uninstalled, or reloaded, so holding this memory unnecessarily could affect browser performance. If the URL is created in an extension's page (new tab, popup, or sidebar) the memory is released when the page is closed, but it is still a good practice to revoke the URL when it is no longer needed.

Once the blob URL has been revoked any attempt to load it will result in an error. 例えば、if the blob url was used as the `SRC` attribute of an `IMG` tag, the image will not load and will not be visible. It is therefore good practice to remove any revoked blob urls from generated HTML elements when the blob URL is revoked.

 Example: Store Collected Images
 API References: idb-file-storage library

**メモ:** You can also use the full Web IndexedDB API to store data from your extension. This can be useful where you need to store data that isn't handled well by the simple key/value pairs offered by the DOM Storage API.

ローカルアプリでファイルを処理する
-----------------

Where you have a native app or want to deliver additional native features for file processing, use native messaging to pass a file to a native app for processing.

You have two options:

* Connection-based messaging. Here you trigger the process with runtime.connectNative(), which returns a runtime.Port (en-US) object. You can then pass a JSON message to the native application using the postMessage() function of Port. Using the onMessage.addListener() function of Port you can listen for messages from the native application. The native application is opened if it is not running when runtime.connectNative() is called and the application remains running until the extension calls Port.disconnect() or the page that connected to it is closed.
* Connectionless messaging. Here you use runtime.sendNativeMessage() to send a JSON message to a new, temporary instance of the native application. The browser closes the native application after receiving any message back from the native application.

To add the file or blob you want the native application to process use JSON.stringify().

To use this method the extension must request the "nativeMessaging" permission in its manifest.json file. Reciprocally, the native application must grant permission for the extension by including its ID in the "allowed\_extensions" field of the app manifest.

 Example: Native Messaging (illustrates simple messaging only)
 Guides: Native messaging
 API references: runtime API