Help us understand the problem. What is going on with this article?

Chrome拡張の開発方法まとめ その1:概念編

More than 3 years have passed since last update.

Chrome拡張の開発に必要な知識とかの覚書です。
この記事では開発の前に知っておくべきChrome拡張の全容について解説していきます。
「実際に開発しながら学ぶ」形式の解説記事は多く見られるのですが、概念についてちゃんとまとめてある記事は見当たらず、そこらへんの理解で結構苦労した覚えがあるので、そういった人達の手助けになればと思います。

対象

  • Chrome拡張が作りたい人
  • Javascriptがまあまあわかる人
  • Chrome拡張の各概念がいまいちわかってない人

概要

まずはChrome拡張の大まかな構造について説明します。
Chrome拡張はHTMLCSSJavascriptで作られています。
必要なhtml,css,jsファイルなどと後述のManifest Fileを一つのフォルダにまとめたものが一つの拡張機能のまとまりとなり、zipで圧縮してアップロードすることでストアで公開することができます。
また、chrome://extensions/のページに上記フォルダをD&Dすることで、自身のChromeに自作の拡張機能をインストールすることができます。

オプションのページやボタンのポップアップなどのGUIの部分はHTMLとCSSで、処理の部分はJavascriptで作ります。
拡張機能内ではchrome.*APIと呼ばれるAPIを利用することができます。
JavaScript APIs - Google Chrome
具体的にAPIを用いてどのような事ができるかは、公式で用意されているサンプルを見ると良いでしょう。ソースコードを見ることができるので、勉強にも役立ちます。
Sample Extensions - Google Chrome

Manifest File

Chrome拡張には必ずManifest Fileが存在します。Manifest Fileとはその拡張機能に関する情報を書くものであり、json形式で書きます。
具体的にはその拡張機能の名前と説明、使用するスクリプトなどのパス、chrome.* APIを用いる為のパーミッションの設定などを書きます。
拡張機能のルートフォルダにmanifest.jsonという名前で配置します。
細かい記法については、公式ドキュメントや、こちらの記事が参考になると思います。
Chrome 拡張機能のマニフェストファイルの書き方 - Qiita

Content Script

Chrome拡張はContent Scriptと呼ばれるスクリプトを任意のページで実行することができます。
例えば、Manifest Fileに以下の記述を付加すると

manifest.json
"content_scripts": [
  {
    "matches": ["http://www.google.com/*"],
    "css": ["mystyles.css"],
    "js": ["jquery.js", "myscript.js"]
  }
]

URLが"http://www.google.com/*"にマッチするページで、指定したCSSとJSが読み込まれます。
Content Scriptは特殊な性質を持っており、

  • chrome.*APIが一部のものしか用いることが出来ない(公式ドキュメントを参照
  • ページ内で定義されている変数や関数にアクセスができない(DOMにはアクセスできる

という制約を持っています。
特に「ページ内の変数や関数にアクセスができない」というのがなかなか厄介で、要するにContent Scriptはページ上のスクリプトから隔離されたスコープで実行されているということです。
この特殊な実行環境は公式ドキュメントによるとisolated worldと呼ばれています。
但し、DOMの取得・操作は出来るため、例えばページ内で定義されている関数を実行したい場合など、Webページのスコープ内でスクリプトを実行したい場合は、以下のようにscript要素を挿入してやることで実現できます。

content_script.js
var script = document.createElement("script");
script.textContent = "hoge();";
document.body.appendChild(script);

Background PageEvent Page

Content Scriptでは一部のAPIしか使えない制約がありました。そこで登場するのがBackground Pageです。
Chrome拡張はBackground Pageと呼ばれるバックグラウンドで動くページを持つことができ、そこで様々な処理を行うことができます。
Manifest Fileに以下のように記述すると、指定したスクリプトがBackground Pageで動作します。

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

「ページ」と言っても目に見えないのでイメージし辛いかもしれないですが、Chromeの内部にWebページのような物が存在していて、そこでJavascriptが動いているという感じです。
ちなみにchrome://extensions/からデベロッパーモードをオンにした状態で、「ビューを検証」からBackground PageのDeveloper Toolsを開く事ができます。
image

但し、Background Pageは常にバックグラウンドで動き続けるため、メモリを常に占有してしまうという欠点があります。
そこで用意されたのがEvent Pageで、Background Pageと同じようにバックグラウンドで動作するものの、必要な時だけ立ち上がって、動作が完了すると閉じるという性質を持っています。
Event Pageの動作状況はchrome://extensions/で確認することができます。
image

現在ではEvent Pageの利用が推奨されています。

Browser ActionPage Action

Chrome拡張はBrowser ActionまたはPage Actionと呼ばれるボタンをChromeのツールバーに追加することができます。
どちらを選んでもできる事は変わらないのですが、Page Actionは一時的にしかボタンが表示されず、Browser Actionは常にツールバー上にボタンが表示されるという性質があります。

image

これ、Chrome48以前ではPageActionはURLバーの中に表示される仕様だったのですが、48からは拡張機能のボタンの仕様が変わって、Page Actionも常にツールバー上に表示されるようになりました…
image
利便性の為にわざわざこうして分けられていたのに、何故か一本化され、さらにActionの存在しない拡張機能までボタンが表示されるようになったなど、UIとしても全くダメだし、本当に何の意図の仕様変更なのか全くもって理解できず憤慨しているのですが…それはまあ、とりあえず置いておきましょう。

Browser Action

Browser ActionはURLバーの右にボタンが表示されます。
常にボタンが表示されているため、特定のWebページを対象としないような拡張機能はこちらが良いでしょう。

Page Action

Page ActionはURLバー内の右端にボタンが表示されます。
Browser Actionと違い、常にボタンが表示されている訳ではなく、拡張機能側から任意のタイミングで表示させることができるため、特定のWebページを対象とするような拡張機能に向いています。
例えば、特定のURLパターンのページでのみ表示する、Webページ内に特定の要素が含まれている時にのみ表示するといったような事が可能です。

前述の意味不明な仕様変更により、何故かボタンが常に表示されるようになりました。
その代わり、Page Actionが有効になった時はボタンに青いバッジが付きます。
image←こんな感じ

Actionの機能

Browser ActionまたはPage Actionにはポップアップを設定でき、ボタンがクリックされた時に用意したhtmlファイルをポップアップで表示させることができます。
image

Manifest Fileの記述は以下のようになります。ここで設定したhtmlファイル(popup.html)がポップアップで表示されます。(Page Actionの場合はbrowser_actionがpage_actionに変わります)

manifest.json
"browser_action": {
    "default_icon": {
        "19": "icon19.png"
    },
    "default_popup": "popup.html"
}

または、Content ScriptやBackground Page側からbrowserAction.setPopupでポップアップを設定することも出来ます。(Page Actionも同様)
Page Actionの表示・非表示(Chrome48~はアクティブ・非アクティブ)はpageAction.show,hideでコントロール出来ます。

ポップアップも一つのページとしてできていて、アイコンを右クリックし、「ポップアップを検証」から、ポップアップページのDeveloper Toolsを開く事ができます。
image

また、ポップアップを表示する以外にも、chrome.browserAction.onClicked.addListenerを用いて、クリックしたタイミングで任意のスクリプトを実行するといったことも出来ます。

番外編:Override Pages

上記の他にOverride Pagesという物があり、例えばchrome://bookmarksなどを拡張機能で置き換える事ができます。
使う機会は限られていると思うのでここでは解説しませんが、そういうものがあるという事は知っておいて損は無いでしょう。

Message Passing

ここまで、Webページで動くContent Scripts、Browser(Page) Actionのポップアップ、Background(Event) Pageと、3つのJavascriptの実行コンテキストが出てきました。これらはそれぞれ独立して動いており、異なるコンテキストを跨いで互いに変数や関数を参照することは出来ません
そこで、それぞれの環境から互いにメッセージを送り、スクリプト間の連携を実現するMessage Passingという概念が登場します。
Message Passingはchrome.*APIによって実現します。これは実際に例を見てみたほうが理解できると思うので、まずは例を見てみましょう。

例えば、以下のようなContent ScirptとBackground Pageのスクリプトがあったとしましょう。

content_script.js
chrome.runtime.sendMessage({greeting: "hello"}, function(response) {
  console.log(response.farewell);
});
background.js
chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
    if (request.greeting == "hello")
      sendResponse({farewell: "goodbye"});
  });

content_script.jsのchrome.runtime.sendMessageが実行されると、Background Page側でchrome.runtime.onMessageイベントが発火します。
chrome.runtime.sendMessageの第一引数の{greeting: "hello"}が、chrome.runtime.onMessage.addListenerのコールバックの第一引数requestに入ります。
コールバックの第二引数のsenderには、メッセージを送ってきたページの情報が入ったMessageSenderオブジェクトが入ります。これとchrome.tabsAPIを用いて色々できるのですが、それについては次回詳しく説明する予定です。
第三引数sendResponseはレスポンスを返す為の関数で、これに引数を渡して実行することでsendMessageのコールバックが実行され、コールバックの引数responseに、sendResponseで渡した引数(ここでは{farewell: "goodbye"})が入っています。
console.logresponse.farewellを参照し、コンソールに「goodbye」が表示されます。

このようにして、sendMessageonMessageの組み合わせでオブジェクトをやり取りすることにより、異なるコンテキスト同士での連携が実現できます。

chrome.storageAPIとOption Page

拡張機能内で特定の値を保存しておいて、次回起動時にも使いたいという場合があると思います。例えば、拡張機能のユーザー設定項目などです。
また、ユーザー設定項目を入力するための設定画面も必要になります。
これらを実現するのがchrome.storageAPIとOption Pageです。

chrome.storageAPI

chrome.storageAPIは、拡張機能内で特定の値の保存と読み取りを実現するAPIです。
chrome.storageAPIを利用するには、Manifest Fileにpermissionの設定を記述する必要があります。

manifest.json
"permissions": [
  "storage"
]

chrome.storageの保存領域にはsynclocalの2種類があります。
syncを利用すると、ユーザーが同期を有効にしている場合、Googleアカウントを用いてデータが同期され、同じアカウントでログインしている異なるPCのChrome上で同じデータを共有することができます。
localはコンピューター上にデータが記録されます。
syncはlocalに比べて記録できる量や書き込みの頻度などが制限されています。(公式ドキュメント参照
また、managedという読み取り専用の領域もあるようです。これの使い方は知らないです…。教えて偉い人!

storageAPIの使い方は簡単で、保存するときはset、読み取る時はgetを用います。

set.js
chrome.storage.sync.set(
  {
    "value1": "string1",
    "value2": "string2"
  }
);
get.js
chrome.storage.sync.get(["value1", "value2"], function(items) {
    console.log(items.value1); // -> "string1"
    console.log(items.value2); // -> "string2"
});

getでは第一引数に読み取りたい値のstringの配列を渡すと、コールバックの引数itemsに読み取った値が入ります。
localを利用する場合は、それぞれchrome.storage.local.set、chrome.storage.local.getになります。

ちなみに、一部の記事では値の保存にlocalStorageを用いると説明しているものもあります。
確かにlocalStorageでも同じような事は可能なのですが、Content Scripts内でのlocalStorageはContent Scriptsが実行されたサイトのlocalStorageとなってしまうので、Content Scriptsから拡張機能のlocalStorageを読み取ることが出来ないという問題があります。

Option Page

Option Pageは拡張機能に関する設定変更を行うページです。
image

htmlファイルを用意し、Manifest Fileに以下の記述をすることで、拡張機能にオプションページを実装することができます。

manifest.json
"options_page": "options.html"

options.html内で必要なのは

  • 設定項目を入力するフォーム
  • chrome.storageからgetしてフォームにセットする処理
  • 保存ボタンを押した時にフォームの値をchrome.storageにsetする処理

です。
フォーム処理はJSerとしては慣れ親しんでいると思われるので、ここでは実例は省いてしまいます。

また、Chrome40から新しいタイプのオプションが搭載されたようです。これについてはまた調べてみて記事にしたいと思います。

まとめ

  • Content Scriptsで任意のページでスクリプトを実行できる
  • Browser(Page) Actionでツールバー上にボタンを追加できる
  • Background(Event) Pageでバックグラウンドで処理を行う
  • Chrome拡張上にはContent Scripts、Actionのポップアップページ、Background(Event) Pageなど複数のJS実行コンテキストが存在する
  • Message Passingで複数コンテキスト間の連携を実現する
  • chrome.storageAPIとOption Pageでユーザー設定項目を実現する

この辺りを理解していれば、Chrome拡張は作れるようになるでしょう。
この記事では概念の説明を中心としたので、実際に作る時は他の記事や公式ドキュメントを見ると良いと思います。

次回はMessage Passingについてもう少し詳しい解説をしたいと思っています。

k7a
Game & Web application Engineer
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした