Chrome Extensionを作ってみよう
この章では、どのようにChrome Extensionの開発を進めていくか、実際に開発したExtensionを例に、ハンズオン形式で具体的に解説していきます。 作りたいものは既に無限に存在していると仮定しますが、一応見つけるヒントも書いておきます。
作りたいものの探し方
Chrome Extensionは、ブラウザの様々な動作に影響を及ぼすことができます。 普段ブラウザを使っている時に、何かがめんどくさいとか、何かを自動化したいとか、特定のWebサイトが見づらいとか、ショートカットキーが欲しいとか、ブラウザの機能が足りないとか、痒いところに微妙に手が届かないとか、いろいろ不満を抱えているのではないでしょうか? これらの不満の多くはChrome Extensionを作ることにより解決できるものがほとんどだと思われます。
おそらくみなさん起きている時間の90%くらいをChromeの上で過ごしているはずですが、 そのような人生の非常に大きなウェイトを占めるChromeの不満を、そのままにしておいては絶対にいけません。 確実に人間の精神を蝕んでいきます。 ブラウザを使っていて、少しでも「めんどくさい」とか「使いにくい」と感じ、 それが2回目もあるようならば、Chrome Extensionを作る価値があります。 自分専用でもいいのです。
作るもの
例として、ダウンロードしたファイルの名前を変えるExtensionを作ります。 iwara.tvというMMD専用の動画サービスでは、動画ファイルのダウンロードが可能なのですが、ユーザがアップロードしたファイル名のままダウンロードされてしまいます。
これでは見たい動画を探すのに不便なのは明らかで、以前は手作業で iwara/ユーザ名/動画タイトル.mp4
というファイル名にリネームして保存していました。 一度にたくさんのタブを開き同時ダウンロードするので、ダウンロードした動画をひとつひとつ再生し、サイト上でも再生し、一致するタブを探しリネームするというかなり手間のかかる作業です。 この作業のめんどくささのため、動画を使うまでのハードルが非常に高くて困っていました。 あるときいい加減めんどくさい!!と思い、Extension化を決断しました。
実現可能性を探る
まず重要なことに、サイトのDOMからユーザ名 や 動画タイトルを拾わなければならないので、Content Scriptは必須だというのが判明しました。 動画をダウンロードさせる方法は、この段階ではまだ決めていませんが、候補はいくつかに絞られます。
サイトのダウンロードボタンを再利用する
Page Actionのボタンを使う
サイトに新たにダウンロードボタンを追加することもできますが、 それよりもPage Actionを使ったほうがスジが良さそうです。 サイトのダウンロードボタンを再利用できるのが最良です。
ダウンロード手段も、
ブラウザのダウンロードを使い
始まる前にリネームする
終わった後にリネームする
それ以外のなにかしらの方法でダウンロードする
くらいしかパターンはないでしょう。
次は、API一覧を見て、使えそうなAPIがないか調べてみます。 chrome.downloadsというAPIが見つかったので、もしかしたらこれが使えるのではないかと期待します。 APIを見てみると download
というメソッドと、 `onCreated
や onDeterminingFilename
などのイベントがあることがわかります。 なんとかなりそうな気がしたので、このAPIを試してみることにします。
iwara-downloader
のディレクトリを作り、yo
で雛形を生成します。
このイベントはなんじゃ?
とりあえず、Chrome Extensionではイベントリスナを登録する手法のほうが自然なので、まずイベントの方に注目します。 onCreated
より onDeterminingFilename
、 直訳すると「名前を決定するとき」というイベントが気になります。
ドキュメントを読むと
ファイル名を決定するとき、Extensionは
DownloadItem.filename
を上書きする機会が与えられます
まさにドンピシャの機能です。 これを試してみましょう。 background.js
にこんなコードを書いてみます。
このExtensionを登録し、適当なサイトでファイルをダウンロードしてみると、~/Downloads/hello/world.mp4
というファイル名で保存されます。 大成功ですね。 勝ったな! ガハハ!!という気分ですが、間違いなくこれは敗北フラグです。
さてこれを
iwara.tvで
HTMLを見て適切な名前を付ける
を満たそうとするととたんに難しくなります。
まずBackground Pageはブラウザ全体に影響するので、iwara.tvだけに限定するならダウンロードURLなどで判断するしかありません。 そして何より問題なのは、どのタブでダウンロードされたかがわからないので、Content Scriptに問い合わせることが難しいのです。
ひとつだけ手はあるかもしれません。 chrome.tabs APIの onActivated
を使い、常にアクティブなタブを監視します。 今見ているタブがiwara.tvの動画ページかどうか、そうであればユーザ名/動画タイトルは何か、という情報を取得しておけば、ほぼ上手くいきます。 ほぼというのは、ダウンロードボタンを押し、onDeterminingFilename
が呼ばれる前にタブを移動してしまった場合、上手く行かない可能性があるということで、これは実際の作業ではおそらく頻繁に起こるでしょう。 人間の操作スピードと気の短さは、ダウンロードの準備ができるすなわちWebページがレスポンスを返す時間よりも圧倒的に短いからです。
ここまで試してみた結果、イベントを使うのは難しそうだと考え方針を転換しました。
download メソッドを使ってみる
chrome.downloads
APIには、もうひとつ download
メソッドという気になるものがあります。 こちらも試してみましょう。 ダウンロードするという機能を考えると、Content Scriptでやるのが一番相応しそうに見えますが、やはりBackground Pageでしか動きません。
Background Pageにコードを書いて試す場合、何かしらのイベントの中でやらないと検証しにくいということが多々あります。 そのため、いったんアイコンを押すとダウンロードが発動するようなコードを書いてみます。
Extensionのアイコンをクリックすると、https://google.com/
のHTMLが ~/Downloads/hello/world.txt
に保存されるでしょう。 これで任意のURLを任意のファイル名でダウンロードできることが判明しました。 download
メソッドを使うという方針が確定します。
本物のイベントを探る
仮に置いた chrome.browserAction.onClicked
のことはいったん忘れて、次はContent Script側を試します。 というのも、Content Scriptの中で上手くMessageを送るタイミングを見つけられたのなら、おそらくこのコードは chrome.runtime.onMessage
に置き換えられるからです。
まず、Content Scriptは対象URLを限定できるので、 http://*.iwara.tv/videos/*
にマッチするURLだけで読み込まれるようにします。
Content Scriptは普通にサイトにJavaScriptを追加するように書けるので、自分がiwara.tvの管理者になった気分でコードを書いていきます。 インスペクタでHTMLを見ながら、本物のダウンロードボタンの仕様を確認しましょう。
となっています。 単純な <a>
タグであることがわかります。 Content Scriptでクリックイベントを追加してみましょう。
この状態のExtensionを読み込み、おもむろにダウンロードボタンを押してみます。 何も起こりません。 なんとデフォルトのイベント伝搬はExtensionからでも止められることが判明しました。
この段階でやっと、今度こそ本気で勝ったなガハハ!!の確信が持てました。 chrome.runtime.sendMessage
でBackground Pageにメッセージを送りましょう。 メッセージの中身は、href
に指定されたファイルのURLと、DOMからタイトルとユーザ名を探し、保存したいファイル名を指定します。
もちろん、Background Page側も chrome.runtime.onMessage
に差し替えです。
こうしてできあがったのが iwara-downloaderです。
ユニットテストをしよう
開発するからにはテストが書きたくてウズウズしちゃうんじゃないでしょうか? でも残念、テストはほとんどできません。 諦めましょう。
なぜならChrome JavaScript APIの動作が予測不可能だからです。 例えば、 chrome.downloads.download
が本当にファイルをダウンロードしてくれるのか分かりません。 download
メソッドに、想定する通りの引数が渡るかどうかSpyを設定することはできるでしょう。 しかし、Content Scriptからメッセージを飛ばすところを再現するのは非常に困難です。 そしておそらく、Chrome以外で実際にAPIを叩ける環境は無いでしょう。
結局、テストする方法は今のところなさそうなので、あっさりと諦めたほうが心は安泰です。
ふりかえる
まず最初に、どんなAPIを使うか考えます。 ブラウザを構成する要素にはほとんど対応するAPIが用意されています。 作りたいものを手動でやった場合に使う要素を考えれば、おそらく必要なAPIが見つかるでしょう。 今回はダウンロードしたファイルをどうにかする話なので、download とか、もしかしたら file とかそんな名前かもと当たりをつけて探します。 偶然 chrome.downloads
が見つかったのでそれを試してみました。
おそらくそのAPIを使える場所は、Background Pageになるはずです。 次は、Background PageでそのAPIが想定通りに使えるかどうかを試します。 試すときはBrowser Actionの onClicked
を使うと楽でしょう。 今回は、最終的に chrome.downloads.download
メソッドを使えば上手く行くことが判明しました。
次に、実際にどんなイベントでその機能が発動するか考えます。 Webページ内から操作したければContent Scriptとメッセージをやり取りするし、 例えばタブが切り替わった時に発動したいのなら、同じようにAPI一覧からtabっぽいAPIを探し、イベントを選びます。 すぐに chrome.tabs.onActivated
が見つかるでしょう。
この段階で、おそらく欲しい機能を満たすことはできているでしょう。 いくつかの機能が必要なら、同じことを繰り返すだけです。 あとは使いやすさを向上させるために、Content Script側をがんばれば、いいものができあがるのではないでしょうか
Last updated