DATE : 2021/05/07 (Fri)
はじめに
5/4に、ブラウザのタブを前や次の空白タブに移動するFirefox拡張「Go to empty tab」を公開しました。ソースコード一式は、GitHubに公開しています。
本記事では、開発の振り返りとして、公開までの経緯を書いていきます。具体的には、以下のことについて記載します。
- 開発の動機
- 開発期間
- これまでの経験
- 採用した言語、ツール、フレームワーク
- 開発の記録
開発の動機
私はFirefoxを主に使用しています。
Firefoxはタブを大量に開いても軽いので、タブをつい開きすぎてしまいます。新しいWebページを開くときもタブを閉じるのが面倒なので、とりあえず空白タブを開いて、タブ間に区切りを入れて新しいタブを開いています。
タブ間の区切りとして空白タブを入れている関係上、閲覧のためにも、タブの整理のためにも、いま開いているタブから空白タブに瞬時に切り替えることができれば便利だと思いました。
そのような拡張機能がないか探してみましたが、検索には引っかかりませんでした。そこで、ないなら作るの精神のもと、拡張機能の開発を思い立ちました。
開発期間
開発を思い立ったのは、2021/2/28のことでした。
しかし、私生活が忙しかったり、他のことを優先していた結果、プロジェクトのディレクトリを作ったのは4/4になりました。
ですので、実質1ヶ月程度で開発を行ったことになります。もっとも、コードの大部分は、最後の数日の間に仕上げました。
これまでの経験
拡張機能の開発にはWeb技術、つまりHTML、CSS、JavaScriptを用いることはすでに知っていました。
しかし、それらをある程度書いたのは2015年ごろが最後で、ここ最近はJavaやC++、Pythonばかり書いていました。つまり、Web技術からはすっかり離れていて、浦島太郎状態だったのです。当時は、ECMAScript 2015で導入された機能や構文を使っておらず、「JavaScript: The Good Parts ―『良いパーツ』によるベストプラクティス」などの本を読んでコードを書いていました。
ですので、まずはECMAScript 2015以降のJavaScriptについて調査するところから始めました。
ECMAScript 2015では、糖衣構文ではあるものの、クラスが定義できるようになっています。また、モジュールシステムも整備されています。ただ、モジュールシステムについては、CommonJSなどを使用した、モジュールシステムが言語機能に導入される前の仕組みを解説した情報もあり、少々混乱しました。
また、ECMAScript 2015ではPromiseが、ECMAScript 2017ではasync/await構文が導入され、非同期処理が簡潔に書けるようになっています。以前に書いたコードでは、コールバックからさらにコールバックを呼ぶことでコードの可読性が低下する、コールバック地獄になっていたことを思い出しました。以前、JavaでRxJavaを用いたリアクティブプログラミングを試したこともあったため、RxJSの採用も考えましたが、今回は採用を見送りました。そこまで高度なイベント処理は必要なく、async/await構文でじゅうぶん簡潔に記述できると予想できたためでした。
ビルドツールも充実しています。当時の私は、JavaScriptはブラウザ上でコンパイルせずに動作することもあり、JavaScriptファイルに対してビルドツールを使うという発想はありませんでした。昨今のビルドツールでは、JavaScriptファイルをひとつに結合したり、minifyなどの最適化を施すことができたりなど、大幅に進化しています。ここまで来ると、コンパイラ言語と変わらない部分もあるように思いました。
採用した言語、ツール、フレームワーク
よく分からない分野については、まずは長い物には巻かれよ、ということで、「State of JavaScript 2020」の利用度を基本に、あとは好みで決めました。
Node.js/npm
Node.jsのLTS版である14系をインストールしました。何はともあれ、Web技術を用いた開発は、最近はこれがないと始まらない印象を受けました。
web-ext
拡張機能の開発を行うには、web-extが必要です。このツールで、拡張機能の実行やビルドを実行します。
webpack
開発中のタスクを自動化する観点から、ビルドツールを探したところ、webpack(https://webpack.js.org/)を採用しました。複数のファイルをひとつに結合したり、minifyなどの最適化を行うのであれば、個々の作業を行うコマンドを手打ちで実行するよりも、ビルドツールのコマンドひとつで実行できたほうが便利だからです。
また、web-extのプラグインもインストールしました。
TypeScript
小規模な拡張機能なので、JavaScriptを用いて実装しても良いと初めは考えていました。しかし、どうせビルドをかけるなら、より堅牢な代替言語が使用できないか、検討を始めました。問題は後で見付かれば見付かるほど修正に時間がかかるため、ビルドの時点やコードを書いている最中に問題を検出できるなら、それに越したことはありません。
そのため、TypeScriptを採用しました。
JEST
書き捨てのスクリプトやごく小規模なツールを除いて、可能な場合はテスト駆動開発を行ってきました。最低限の品質を確保できるという利点もありますが、動作するコードが少しずつできあがっていくので、開発のモチベーションを保ちやすい点が自分にとっては大きいと思います。
そのため、ユニットテストフレームワークとしてJESTを採用しました。
ESLint
静的コード解析とスタイルチェックを同時にできるのでESLintを採用しました。
規模が小さい拡張機能ですが、実行前にバグを検出できるということ、またJavaScriptらしいコードを書きやすくなるため、採用しました。
スタイルには、「Airbnb JavaScript Style Guide」を採用しました。採用についてこだわりは特になく、ESLintの初期化時に、使用するスタイルガイドとして初めに選択されていたためです。
開発の記録
拡張機能の開発は、次のように進めていきました。
- ドキュメントを読む
- ソフトウェアライセンスを選択
- タブの情報を出力する拡張機能を作成
- テストコードを書く
- 拡張機能のコードを書く
- アイコンを作成
- AMOで公開
ドキュメントを読む
拡張機能の作り方や、タブ関係のAPIについて公式のドキュメントを調査しました。例えば、次のページが役に立ちました。
拡張機能の入門には「Your first extension」を参照しました。実際にサンプルの拡張機能を作りながら、学ぶことができました。
タブ関係のAPIについては「Working with the Tabs API」を参照しました。
TypeScriptやJEST、webpackを使用した拡張機能の例として、「Rikaichamp!」を参考にしました。
また、各種ツールのドキュメントも参照しました。
TypeScriptの学習には、「プログラミングTypeScript――スケールするJavaScriptアプリケーション開発」を読みました。JavaScriptの知識を前提として、TypeScriptに特化した記述が多めになっており、参考になりました。
ソフトウェアライセンスを選択
開発した拡張機能はOSSとして公開するつもりでしたので、今回はMITライセンスを選択しました。
これまではソースファイルのヘッダにライセンスヘッダを入れていましたが、今回はSPDXを使用しました。これで、ファイルを開いた直後の画面がライセンスヘッダに占領されることがなくなり、見通しが良くなったように思います。ただ、SPDX側としては、ライセンスヘッダがライセンスに定義されている場合は、それも入れることを推奨しています。MITライセンスでは、ライセンスヘッダは定義されていないので、SPDX short-form identifiersのみ記載しています。
タブの情報を出力する拡張機能を作成
こうして拡張機能を開発する準備は整いましたが、疑問がひとつありました。
そもそも、空白タブはどのように見付ければ良いのでしょうか。タブに紐付くURLを見れば良さそうですが、Firefox上で新規タブを開いても、アドレスバーには何も表示されません。
そこで拡張機能の開発の練習がてら、タブの情報をブラウザーコンソールに出力する拡張機能を開発しました。その結果、タブに紐付くURLが以下のいずれかの場合に、空白タブだとわかりました。
- about:blank
- about:newtab
これで、拡張機能を開発する準備が整いました。
テストコードを書く
テスト駆動開発を行うため、まずは簡単なテストコードを書きました。エディタの補完機能を利かせるために、メインコード側は中身が空の関数や型を定義し、テストコードを書きながらそのインタフェースを調整していきました。
ここで問題になるのは、Firefoxが提供するAPIをテストコード上で扱う方法です。例えば、タブ関連のAPIや、DOMがそれに当たります。
今回は、その部分はテストコードの対象外としました。もちろん、モックオブジェクトなどの方法でそれらのコードをシミュレートすることはできます。しかし、テストコードを書くほど時間的コストをかける必要はないと判断しました。その部分と、切り替え先の空白タブを見付ける処理は明確に分離することができ、またFirefoxが提供するAPIとの接合部分はごくシンプルなものだったためです。
結果、切り替え先の空白タブを見付ける処理についてのみテストコードを書くことにしました。今後、Firefoxが提供するAPIとの接合部分が複雑になれば、今回は対象外としたその部分についてもテストコードを書くかもしれません。
拡張機能のコードを書く
テストコードを書き足しながら、拡張機能のメインコードも書き足していきました。
JavaScriptは久しぶりで、TypeScriptは初めてだったため、簡単なロジックミスをテストコードが検出することも何度かありました。小さい拡張機能ながら、これだけでも十分にテストコードの効果があったと思います。デバッグも、修正ができたか確認するのも、テストコードを実行するコマンド一発でできました。これでテストコードがなければ、Firefox上で毎回拡張機能を操作しながら、デバッグを繰り返すことになっていたでしょう。テストコードのおかげで、時間が節約できたと思います。
拡張機能の開発者は私一人ですが、次にいつこの拡張機能を編集するかわかりません。将来の自分のためにも、モジュールからexportされる型や関数には、ドキュメントコメントを記載しました。形式は、TSDocに従いました。
ところで、拡張機能の中では、enumが適していそうな部分があります。しかし、TypeScriptのenumには問題が指摘されているため、Union Typesを用いて定義しました。
アイコンを作成
これで拡張機能は一通りできあがりました。公開するうえで、デフォルトアイコンのままでは他の拡張機能と区別が付けづらくなるため、アイコンを作成することにしました。
アイコンの形式には、SVGを選びました。複雑なアイコンを作るつもりはなかったことと、解像度に合わせてアイコンの画像ファイルを用意するのが面倒に思ったためです。
アイコンは、Google Fontsで提供されているタブのアイコンを加工して作成しました。
アイコンのライセンス確認すると、 Apache License Version 2.0で、帰属の表示は必須ではないとのことでした。
初めはDraw.ioを使用してアイコンを加工しようと思いました。しかし、画像サイズをピクセル単位で指定できませんでした。そのため、Inkscapeを使用しました。
AMOで公開
これで一通り拡張機能の公開の準備が整ったので、AMOへ登録を行いました。なお、AMOとは、アドオンが配布される公式サイトaddons.mozilla.orgのことです。
AMOでは、以下の2種類から配布方法を選択できます。
- 自主配布
- AMO上では、拡張機能を登録した自分自身のみインストールできます。
- AMO上で公開
- アドオンマネージャーから閲覧できるようになり、誰でも拡張機能をインストールできます。ただし、審査に合格する必要があります。
なお、審査のために、ソースコード一式のアップロードが必要な場合があります。minifyなどの最適化や、webpackなどを使用してファイルを結合した場合は、必要となります。また、アップロードする場合、READMEファイルか、別途表示されるページに、拡張機能をビルドする方法やテストする方法を記載する必要があります。自主配布では不要かと思いましたが、実際には登録を求める内容が表示されました。
今回はwebpackを使用してファイルの結合とminifyを行っているため、ソースコード一式のアップロードを行いました。
まずは、自主配布の方法を試しました。バージョン0.0.1は、このために登録しました。これで実際に、AMO上からアドオンをインストールして正常に動作するか確認できました。
5/4の17時頃に、公開のためにバージョン0.0.2をアップロードしました。審査待ちの状態となり、57個中57個と、拡張機能の審査を待機する列の長さが表示されました。
そして、21時頃に審査に合格し、拡張機能が公開されました。
DATE : 2013/04/20 (Sat)
これまで、tcpdumpなどのパケットキャプチャツールの存在は知っていましたが、それをどのように使うのか、いまひとつイメージが沸きませんでした。そこで、パケットキャプチャについての知識を仕入れようと思い、「実践 パケット解析 第2版」を読みました。代表的なパケットキャプチャツールであるWiresharkの使い方が中心ですが、どこにパケットキャプチャツールを仕掛けるべきか、プロトコルヘッダからパケット解析を行う際のコツ、使用事例まで書かれており、パケット解析を行うノウハウは十分に得られる内容となっていました。本記事では、「実践パケット解析 第2版」を読んで感じたことや興味深かったことを記します。
もともと、パケットキャプチャツールに対してはどこか近寄りがたいイメージがありました。ネットワークに流れる他人のパケットをキャプチャして解析というのは、いかにもクラッカーがすること、のようなイメージを抱いていたのです。
しかし最近、身の回りでネットワークのトラブルを見るにつけ、パケット解析の必要性を感じるようになりました。特定のホストだけインターネットに繋がらないといった、事象の切り分けが容易な場合は、ホストからpingを打ってみる、ホストの設定を見直すなど、ホストの操作だけで事足ります。しかし、全ホストが繋がらない、ネットワークが異様に遅いなど、事象の切り分けが難しい場合は、まず何が原因なのか、調査を行う必要があります。しかし、あちこちのマシンやルータの設定を見直すのでは、時間がかかります。ここでもし、ネットワーク上に流れているパケットの様子を見ることができれば、原因の絞り込みが容易となり、何の設定を見直せば良いのか、見当がつくようになります。
そこで、パケットキャプチャツールを用いたパケット解析の知識を仕入れようと、「実践パケット解析 第2版」を読みました。パケット解析とは何か、どのように行うのかといった問いに答えてくれそうでしたので、この本を選びました。
パケット解析のことをよく知らない自分にとっては、パケットキャプチャツールをネットワークに仕掛ける方法が印象的でした。仕掛ける、といっても様々な方法があり、状況に応じて異なります。基本的には、ネットワーク機器に直接触れることができるか、設定を変更することができるかというところが重要なポイントとなります。もし可能であれば、ポートミラーリングやネットワークタップ、リピーターハブを利用して、パケットキャプチャツールを仕掛けることができます。不可能であれば、ARPキャッシュポイゾニングを用いて、ホストと、スイッチングハブやルータとの間に割り込んでパケットキャプチャツールを仕掛けることになります。
本書の中心はWiresharkの使い方ですが、スクリーンショット付きで説明されており、わかりやすく書かれています。Wiresharkはオープンソース、マルチプラットフォームのパケットキャプチャツールで、パケットのプロトコルを解析し、ヘッダを分かりやすく表示するだけでなく、フィルタリング機能や統計表示機能など、パケットを解析する支援機能も充実しています。特にフィルタリング機能では、独自の構文を用いて検索条件を作成することができ、ヘッダの各要素レベルで条件を作成可能です。ネットワーク上では大量のパケットが流れるため、検索条件の善し悪しが解析作業の効率を左右すると感じました。
使用事例も記載されているため、初めてパケット解析を行う際には心強い味方となりそうです。ネットワークに繋がらないといったネットワークトラブルから、不正な侵入を受けた場合の解析など多岐にわたった事例が紹介されています(ただし、不正な侵入を受けた場合の解析は、それだけで奥深いテーマのため、本書では触り程度に触れられています)。ネットワークトラブルの場合は、まず異常な状態と正常な状態を区別するため、ベースラインとなる状態を定めておくべきである、という主張が繰り返しなされていたのが印象的でした。確かに、トラブルの有無にかかわらず、ネットワーク上にはパケットが流れ続けているわけですから、ベースラインとなる状態を定めておかないと、何と比較して異常な状態と判断するのか、基準がなくなってしまいます。
このように、基本から丁寧に説明されているため、パケット解析のノウハウが一通り得られたように思えました。実際にパケット解析を行う場面となった際には、本書を片手に頑張ってみようと思います。もっとも、企業でパケットキャプチャツールを使用する場合は、企業のセキュリティポリシー上、インストールや使用が禁止されている場合があるため、関連部署との調整が必要不可欠ではありますが……。
DATE : 2013/03/14 (Thu)
Javaソースコードのカバレッジ計測ツールのEMMAには、HTMLレポートの生成時にJavaソースコードの文字エンコーディングを指定する機能がありません。そこで、HTMLレポート生成時に、ソースコードの文字エンコーディングを指定できるようにしたパッチを作成しました。パッチは本家に投稿済みであり、Android SDK版についてはGitHubにもアップロードしています。
現在最新バージョンのEMMA(2.0.5312)では、HTMLレポートを生成する際に、ソースコードの文字エンコーディングを指定することができません。プラットフォームのデフォルト文字エンコーディングがソースコードの文字エンコーディングとして使用されるため、両者が一致していない場合はHTMLレポートが文字化けしてしまいます。例えば日本語版Windowsのデフォルト文字エンコーディングはShift JISのため、それ以外の文字エンコーディングで書かれたソースコードからHTMLレポートを生成すると、文字化けしてしまいます。
そこで、ソースコードの文字エンコーディングを指定できるようにしたパッチを作りました。本パッチを適用したEMMAでは、以下のいずれかの方法でソースコードの文字エンコーディングを指定することができます。
- 「EMMA report generation property」としてreport.source.encodingキーを追加したため、その値として文字エンコーディングを指定する。
- Ant向けに、html要素にsourceencoding属性を追加したため、その値として文字エンコーディングを指定する。
本パッチは、本家に投稿済みです。また、Android SDKに含まれているEMMAについても本パッチを適用し、結果をGitHubにアップロードしました。EMMAプロジェクトは現在休止中であるため、マージが待てない方は本パッチをご自由に(ただし自己責任で)お使いください。
DATE : 2013/01/26 (Sat)
ビルド時にJDK 1.4が必要なライブラリをビルドしようと64ビット版XubuntuにJDK 1.4をインストールしようとしたのですが、「./install.sfx.8648: not found」というエラーメッセージとともにインストーラが停止してしまいました。x64用Linux版JDK 1.4はOracleのサイトのアーカイブにはなく、x32用しかありません。そのためx32用JDKをダウンロードしてインストーラを実行したのですが、「./install.sfx.8648: not found」というエラーメッセージが出力され、インストールできなかったのです。
そこで調べてみると、g++-multilibパッケージをインストールすると32ビット用JDKを64ビット版Linuxにインストールできることがわかりました。g++-mutilibとは、g++に標準で備わっているアーキテクチャのサポートをさらに追加するためのパッケージで、このパッケージをインストールすることで64ビット版Linuxでも32ビット向けプログラムをビルドできるようになります。
実際にg++-multilibをインストールし、再びJDK 1.4のインストーラを起動したところ、無事にJDK 1.4をインストールできました。前述のライブラリのビルドにも成功しました。
参考文献
DATE : 2012/10/10 (Wed)
Androidアプリの開発でテスト用のスクリプトを書いていると、特定のログが出力されるまで待機したい、ということがあります。しかし、Android SDK Revision 20の時点では、adbにはそのような機能はありませんし、monkeyrunnerにもそのようなAPIはありません。
そこで、指定したログがlogcatから出力されるまでスクリプトを待機させるPythonモジュールを作りました。以下のように使うと、Activityが起動するまで待機できます。
import logmatcher logmatcher.start() # ... (Activityの起動) logmatcher.wait( 'START {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER]')
以下の環境でスクリプトは動作します。
- Python 2.7
- monkeyrunner (Android SDK Revision 20以上)
- Jython 2.5
GitHubでスクリプトを公開しています。「Downloads」からアーカイブをダウンロードしてください。ライセンスはApache License, Version 2.0です。
特定の文字列がログに現れるのを待機するほかに、特定の正規表現パターンにマッチする文字列がログに現れるのを待機することもできます。詳しくは、READMEをご覧ください。