#コーディング
ホントに初めてのGoogle Chrome拡張機能開発

個人的に欲しいChrome拡張機能があり、作ってみたら思いの外簡単だったので忘備録として記す。
こういったものは0から1への壁が高いので、超最低限の構成でひとまず「なんだ、できるじゃん」という感覚を掴むことが大事だと思う。そこで本当に必要最低限のことしか記さない。hello worldレベルである。これで「お、できた」と思えたなら詳しく調べて色々やってみれば良い。そのきっかけとなれば幸いである。

そもそもChrome拡張って何で動いてるの?

考えてみれば当たり前ではあるが、フロント(ブラウザ上)で動くプログラムであるのだからJavaScriptに決まってる。乱暴に言ってしまえばDeveloper ToolsのコンソールでJavaScript打つのをバックグラウンドで実行させてる感じ、だと思う。
拡張機能のアイコンをクリックした時に出てくるポップアップはhtmlで書かれているし、要するにhtml, css, JavaScriptを用いたコーディングができる程度の能力がある方であれば問題なく開発が可能である。
ポップアップ作ってそこでなんらかのアクションを実行とかっていうのは最低限の範囲を超えるので今回はやらない。

Chrome拡張の最低構成要素とは?

たったの2ファイルである。

  1. 実行したいJavaScriptを記述したファイル
  2. manifest.json

さえあればとりあえず動く。動くが、あまりに物寂しいのでツールバーに表示させるアイコン画像くらいはオマケで設定してみよう。

manifest.jsonとは

名前のまんま、作成した拡張機能を定義するjsonである。ここで拡張機能の名前とか詳細とか作者とか実行するファイルとかを指定するわけ。

何を作るか?

けものフレンズというアニメをご存知だろうか。筆者は連休中ヒマすぎてこれを一気見したりなどしてやりすごしていた。
このアニメは「IQを溶かすアニメ」などと揶揄されるように基本的に「わーい!」とか「たーのしー!」とか「すっごーい!」などの幼稚園児レベルの語彙で成り立っている。アホかと思いつつ、どんな些細なことでも褒め合う姿勢に筆者は感銘を受けた。
我々エンジニアにとって「ググる」というのは呼吸をするに等しいほど日常的な行為であるが、これすらも「よく調べられたね、凄いね」と褒め合う世界になれば世界平和に一歩近づくのでは?などと考えた。そこで、ググる度に褒めてもらえる拡張機能を作ってみる。

manifest.jsonを作成する

まずは適当な作業用ディレクトリを用意しよう。そのディレクトリ内にmanifest.json, app.js, icon.pngを作成する。manifest.json内には

  • manifest_version

manifest.jsonファイル自体のバージョンを示すもの。これは決め打ちで2にしてあげる必要がある。Chromeの仕様が変更になった際はここを変えてあげる必要が出てくるはずだが、現状ではとにかく2としておくと覚えておけば良い。

  • name

そのまま、拡張機能の名前。

  • version

そのまま、拡張機能のバージョン。
上記は必須で記述する。しかしこれだけでは何を実行させるか指定できていないので、どのファイルを実行させるかを指定する。今回は特定ページ下でスクリプトを実行させたいだけなので”content_scripts”として実行させるファイルを指定する。
今回は特に必要ないが、DOMの操作などをしたい場合jQueryを使えた方が楽な場合がありそうなので同一ディレクトリ内にjQueryを突っ込んでおこう。

content_scriptsには”matches”に実行させたいページのURLを、”js”に実行させたいjsを指定する。ここは通例通りjQueryを先に読み込んでおく。
ディレクトリの中身は下記画像のようになるはずで、これが必要最低限のファイル群となる。

実行させたいJavaScriptを書く

IQが溶けた結果と言わざるをえない程度のコードを書く。

これで完成。本当にただのhello worldになった。

Chrome拡張機能を読み込む

chrome://extensions/にアクセスし、上記ファイル群が存在するディレクトリをそのままドラッグアンドドロップするだけで拡張機能が読み込まれる。

この拡張機能を有効化した状態でGoogle検索を行うと

わーい!うれしー!!
これで任意のスクリプトを拡張機能から実行することができるということが分かった。あとはJavaScriptを書ける人であれば、自分好みの拡張機能をいくらでも思いつき作成することが可能なはずだ。私はSNSの「いいね!」機能が非常に嫌いなので「どうでもいいね!」に書き換える拡張でも作って世界に喧嘩を売ろうかと思う。

Chrome拡張が実行されるタイミング

私が本当にやりたかったことはJavaScriptで動的に生成されたDOMに対する操作だったのだが、リロードを繰り返してみると半々くらいの確率で動作したりしなかったりした。
https://developer.chrome.com/extensions/content_scripts#registration
こちらのドキュメントにある通りContent Scriptsが実行されるタイミングはmanifest.json内のrun_atプロパティで指定することができる。
問題はデフォルト値であるdocument_idleなのだが、実行タイミングが

In the case of “document_idle”, the browser chooses a time to inject scripts between “document_end” and immediately after the window.onload event fires. The exact moment of injection depends on how complex the document is and how long it is taking to load, and is optimized for page load speed.

とされている。DOMの構築完了からonloadイベント発火直後までの間の任意のタイミングで発火されるわけだから、onloadイベントにて動的にDOMを構築する作りでありそれに時間がかかってしまった場合、最終的なDOMの構築が完了する前に発火してしまうことがある。厳密にonloadイベント終了後という指定はできないのだ。このせいで、なぜかうまくいったりいかなかったりしたのだろう。
注意として

Note: With “document_idle”, content scripts may not necessarily receive the window.onload event, because they may run after it has already fired. In most cases, listening for the onload event is unnecessary for content scripts running at “document_idle” because they are guaranteed to run after the DOM is complete. If your script definitely needs to run after window.onload, you can check if onload has already fired by using the document.readyState property.

と書かれている。なるほど、ならばreadyStateがcompleteになった段階で実行すれば動的なDOM生成完了後を保証できるのかと思いきや、そうでもなかった。あくまで全てのリソースを読み込んだ後であることを保証しているに過ぎず、jsによる処理が完了した段階を保証できるわけではなさそう、多分。
え、じゃあどうすりゃいいの…ってわけで仕方なくsetTimeoutで適当にリソース読み込み完了後から数秒後とかって指定をすることでひとまず動作を保証することができたが、タイムラグが生まれるので完璧からは程遠い。
本来であればDOMを生成する関数のコールバックとして実行させれば良さげだが、そこをいじるのは難しそう。なんか美しい方法はないのだろうか、誰か教えてください。

まとめ

普段からお世話になっている拡張機能達のことをめっちゃ便利!これ作った人すげぇ!なんて思っていたがいざ作ってみると意外と簡単。せっかくエンジニアをやっているのだから、享受する側ではなく提供する側でありたいものである。
ちなみに万が一本当に「サーバルちゃんに褒めてもらおう!!」拡張を作ってしまった方がいたら今すぐ削除した方が良い。マジ邪魔、セルリアンかよ。

CONTACT

この度は当社へご興味お持ちいただき
ありがとうございます。
Webに関するお悩みございましたら、
是非一度お気軽にご相談ください。
平日10:00~19:00