2012/03/22

EXIF Geotag Checker Chrome機能拡張を作った時の変更点まとめ

画像ファイルにGPSの位置情報が入っていたら気持ち悪いなぁと思って、いくつかChrome機能拡張を探してみました。 ちゃんとExif情報を表示するものはいろいろあったのですが、その中からプライバシー情報を見つけるのは面倒で、汎用的なViewerはあったのですが、適当なものがみつかりませんでした。

Exif Geotag Checker Window

EXIF情報が常に悪いかというと、写真なんかを扱う人であれば、管理上は間違いなく重要な情報で、画像ビューアーがいつの間には画像を上書き保存していてEXIF情報が失われたと知れば怒る人もいるでしょう。

iPhoneであれば画像をメーラーで添付する時にEXIF情報は削除してくれますが、iPhoto経由なんかで普段とちょっと違う方法で画像をアップロードしたらGPS情報がばっちり残っている可能性もあります。

今回は思い切ってEXIFの数あるエントリの中から、GPSだけ、それも時刻、緯度、経度情報の3点だけを表示するツールを作成してみました。 EXIFの時刻情報のDateTimeOriginalもみるべきなのかもしれませんが、それはツールが勝手に改竄したとかいう主張も通りそうに思えるので今回は省きました。

興味のある方は、Gitoriousでコードをforkして機能を追加してみてください。

今回作成した機能拡張の機能そのものもはライブラリに頼っていますが、それ以外の基本的なところでいろいろ壁にぶつかったのでまとめておくことにしました。

作成したChrome機能拡張について

DateTimeOriginalについてのところで書きましたが、今回はGPS情報の3点だけを表示するようにしました。 EXIFのコメント欄にメールアドレスなんかが入っている可能性もあります。
とはいえ、全部をチェックするのは時間がかかる上に、現実的には問題が発生する可能性は低そうです。

それが必要であればEXIFのViewerを使ってください。

制限事項

今回作成したアプリケーションは、Ajaxなどでブラウザに動的に表示している画像には対応できません。

その他にもいくつか制限はあるものの、本格的なExifViewerはむしろ余計に混乱するので、いくつかの技術的なポイントの解決策の模索と合わせて、ニッチな需要を探してみる事にしました。

技術的なポイント

機能的の中心になるEXIF情報の取得には、既に公開されている外部ライブラリを使っています。 README.mdにも含めていますが、中心となる機能部分にはJacob Seidelinさんのexif.js(+binaryajax.js)を使用しました。

この他にもContent Scripts側での非同期処理を扱うために、jsDeferredライブラリ(jsdeferred.nodoc.js)も使用しています。

いろいろライブラリは使用しましたが、最終的にはexif.jsとbinaryajax.jsには、次のような変更を加えています。

  • IEに特化したコードをロードする個所のコメントアウト
  • 起動時の自動ロード(loadAllImages)の停止
  • exif属性のあるimgタグだけを対象にしていた処理のスキップ
  • 画像データを入手するためにメッセージボディ全体を入手するコードの追加

作業の中心はEXIFについてではなく、できるだけドキュメントのDOMの影響を受けないようにしたり、非同期呼び出しの内部構造作成に時間を取るようにしました。

取り組んだいくつかの課題

開発の前の調査段階と開発初期の試行錯誤の時期では次のような、課題を感じました。

Chrome機能拡張に固有な課題

画面に表示されているデータはDOMとして入手が可能ですが、Chromeで直接にDOMを操作するためにはContent Scriptsと呼ばれるコードインジェクションの手段を使う必要があります。 BackgroundタイプやBrowser Actionタイプからは非同期(コールバック)通信の仕組みを利用して、その結果だけを受け取る事になります。

またContent Scriptsは、画面に表示されているドキュメントの構造(DOM)の影響を受けます。 例えば、Content Scriptsを使って画面上にjQuery UIのポップアップウィンドウを表示させようとすると、コンテンツが既にロードしていたCSSの影響などを受ける可能性があります。

他のExifViewerを使って感じた課題

EXIF Viewerと呼ばれるようなアプリケーションをいくつか触ってみた時に、画像を解析するために外部のCGIを呼んでいるものがありました。

Browser Actionであるポップアップウィンドウから画像データにアクセスするのは面倒なのでリーズナブルな方法ではありますが、データを送信したCGIから画像データにアクセスするため、外部に公開されているサイトでないと正常に動かない課題があります。

このため、NATの中にいる自宅やイントラネットなどからはうまく動かない事になります。 また場合によっては画像が外部サイト上にキャッシュされる事を問題視する事もあるでしょう。

解決するべき課題

ざっと感じた点をまとめて、以下のようなポイントに注意して作業を進めました。

  • 外部サイトのCGIなどに頼らず、Chromeブラウザで完結した処理を実現する
  • アドレスバーの横にアイコンを出し、ポップアップした画面に結果を表示する
  • 問題があるか、ないか、ひと目で判断できるようにする

とはいえ画像データを2重にダウンロードする課題はあります。そのためJPEG画像のみをロードするようにPNGやGIFは無視する仕組みを入れています。そのためにexif.jsにもともとあった、読み込む対象をexif属性で指定する処理と、全ての画像データにアクセスする処理は動かないようにしています。

その他にも、ポップアップ画面で画像を表示させる時にもBasic認証が必要なページや相対URLから絶対URLへの変換といった面倒な処理への対応が必要になります。

こんな感じで、解決するべき課題はありました。 しかしなんとか、荒削りですが、一通り動かすことができるものを作る事ができました。

作成したコードはGitoriousから入手する事が可能です。

また機能拡張自体はChrome Webストアから入手する事ができます。 [ EXIFジオタグチェッカー ]

Browser Actionとしてポップアップ画面からコンテンツの画像を表示する方法

Chrome機能拡張についていえば、アクセスしているタブページとBrowser Actionでアドレスバー横のボタンを押して表示されるポップアップ画面とは完全に独立しています。

この環境でポップアップ画面に画像データを表示するためには、imgタグのsrc=...に次の2つの方法で画像ファイルを指定する必要があります。

  • httpから始まるURLで画像ファイルの場所を指定する
  • Base64でエンコードされたデータそのものを指定する

データそのものを指定する方法は、情報量が増えるので普通は使いません。

どちらでも良さそうだと思ったのですが、認証が必要なページの画像をURL指定でポップアップ画面からアクセスした時には認証ダイアログは出ずにアクセスが拒絶されました。 そんな分けでBase64を使って、タブページから画像データの情報そのものを入手できるようにContent Scriptsを作成しました。

扱うデータの量が増えたので、少し負荷テストらしき事をしてみました。 この段階ではSpeed Tracerのために開発版のChromeを使う必要があって準備していないので、細かい時間の測定は後回しにして、印象だけで書いています。

負荷テスト1

バックグラウンドでexif情報をチェックするスクリプトを動かして画面のレンダリングに異常がないか、確認しました。

テストのためにWebサイトの画像作成用に持っているイメージ素材集のJPEGイメージをいくつか使い、一つのindex.htmlの中にimgタグを2600以上配置させる負荷テストを行ないました。

この中にgeotag情報を持ったJPEG画像を配置しましましたが、時間がかかったものの、ローカルのWebサーバを経由させた事を考えても、思ったよりも早いスピードで処理を行なう事ができました。

負荷テスト2

次にGPS情報を持ったJPEGファイルのコピーを100件作成し、index.htmlに100件のimgタグを書き込んで、このファイルをロードするテストを行ないました。

この記事を書きながらテストしている今は、結果を表示するまでに1、2秒程度のタイムラグがあります。 デフォルトのページの内容が更新されないと、読み込み中なのかどうか、そのステータスを表示することができていません。

機能拡張を入れることでJPEGデータのロードは2倍発生するので、その分の処理時間は単純に倍かかります。 その予測とほぼ同じ変化がある事はわかりました。

ブラウザが表示した画像データのキャッシュにアクセスできれば良いのですが、そうするとCaptchaや乱数表のようなアクセスする度に変化する事でセキュリティを保っているようなアプリケーションの脅威になってしまうので、永遠にそんな事はできないでしょう。

そんな分けで、JPEGファイルについて再読み込みを許可、あるいは予測していない場合には、JPEGファイルに2回アクセスする事で問題が発生する可能性はあります。

今後Chrome機能拡張を作成する時に気をつけること

今後も気が向けば機能拡張を作ったり、現状のアプリケーションに変更を加える事もあるでしょう。

タブに開いているコンテンツ(のDOM)にアクセスするタイプのアプリケーションは、その解析にContent Scriptsを使い、そのデータの取得に非同期(コールバック)通信の仕組みを使う事になります。

そういった全体の構造を考えて、どういうコードをどこで使うか、そういう判断というか、全体の構成と楽をするための手段について検討することが必要でしょう。

JavaScriptを使う時にはライブラリを使わなくとも似たような処理はいろいろな方法で可能になります。 しかしコードが長くなって読み難くなってしまうところが難点だと思っています。

苦労すれば最終的に同じ(ような)機能の実装はできるけれど、それを高いメンテナンス性を保って作る事は、相当な知識と面倒な作業が発生するでしょう。JavaScriptの手軽さと難しさはそういうところにあると思います。

余談ですが、JavaScriptを学校で教えるべき言語に上げる人は多いそうですが、かなり特定の問題領域(ドメイン)に特化した知識+経験になりそうな点が心配です。

情報系の大学であれば、同じものを別の角度でみるためにも、JavaとScalaを合わせて教えて、同じ機能を別々の方法で実装させたりするのが、時間はかかっても最終的には良いJavaScriptプログラマを生み出すような気がします。

創造性っていうのは応用力の積み重ねでしかないので、思ったものが作れれば何でもいいんですけどね。

JavaScriptの需要が多いのは確かですが、最初からJavaScriptだと、苦労をしてもJavaScriptしか使えない人間になりそうに思えるのが怖いところです。

0 件のコメント: