Google Contacts/Calendar API for JavaScript をブックマークレットから呼び出す

Google GData API には PHP, Python, Java などといったサーバサイド言語の他に JavaScript の実装がある。これを使うと、Webブラウザから直接Googleのサーバに保管されたデータにアクセスできる。

しかも単なるJSONPでのフィード配信(パブリックに公開されたデータのみ)などというわけではなく、Google Account Authと連動して、Googleデータストレージに対して読み/書きができるようになっている。サーバ上にプロキシプログラムは必要ない。セッショントークンのやり取りまで含め、すべてClient JavaScriptのみで完結する。

さて、せっかくそこまでできているなら、どうせならブックマークレットから呼べないかと思った。そして調べて実装してみた。

結論から言うと、可能。

実装してみたブックマークレットの例:

呼び出し用ブックマークレット

ソースコード
http://stomita.sakura.ne.jp/lab/gdata_bookmarklets/gdata-bookmarklet.js

カスタムで作成するには、このスクリプトで定義されている waitGdataReady メソッドの第一引数にスコープURL*1を指定して、コールバック関数に実際の処理を渡してやればよい。コールバックされてくるときには、すでに同スコープへのアクセス許可が与えられた状態になっている。

解説

ブックマークレットで呼び出すにあたって、いくつか注意した点があった。

まず、Developer Guideにある通常のスクリプト読み込みの方法は、ドキュメントロード完了前にスクリプトがロードされることを前提にしているためそのままでは使えない。
ブックマークレットからgoogleスクリプトをロードするためには、コールバック関数をスクリプトに指定するという方法で行う。この方法は公式ドキュメントには見当たらず、おそらくどこかのメーリングリストなどにでていたのかもしれないが、自分は以下の記事からその方法を知った。

Big Sky :: Google Visualizationを動的にロードしてGreasemonkey等で使用されているSITEINFOをGoogle SpreadSheetから取得する

function jsapi_loaded() {
    google.load("visualization", "1", {"callback" : querySITEINFO});
}

function loadExampleSITEINFO() {
    var s = document.createElement('script');
    s.charset = 'utf-8';
    s.src = 'http://www.google.com/jsapi?callback=jsapi_loaded';
    document.body.appendChild(s);
}

また、Google Account Auth が内部的に絡んでくるので、トークンのやり取りのためにリダイレクトが発生してしまう。そのため、コンテキストが失われないように、別ウィンドウにする必要があった。

これはIFRAMEでやってもできたかもしれないが、フィッシングの懸念からアドレスがわかるようにすべきと考え、ポップアップウィンドウにした。そのためブックマークレットを呼び出すサイトにおいてポップアップブロックは無効にしておく必要がある。

また、Google からのトークンはもともとのページのURLにリダイレクトされて帰ってくるようになっており、そしてそのページにはもちろんgoogleスクリプトは入っていない(=ログイン処理が続けられない)。そこで、親ウィンドウ側のスクリプトでポップアップウィンドウを監視し、トークンが戻ってきたときには動的にgoogleスクリプトをロードするように補助している。

その他、ブラウザ毎に挙動に違いがあったため、場当たり的な修正を入れた。すべての場合に対応できたか自信が無いが、手元の環境ではなんとか動いている(Firefox3, IE7, Safari3)。

応用例

Contacts APIでは 自分のGmailのコンタクトリストに対してクエリを投げることができるので、任意のテキストボックスに対してアドレス補完機能を勝手に追加、みたいなことができそう。

注意事項

ページのエンコーディングによって文字化けるor取得に失敗する。おそらくUTF-8なページなら大丈夫だけど、ブックマークレットとして呼び出す以上できるだけすべてのページで使いたいのだが、今の時点ではむずかしいな。

あと、GoogleJavaScript AuthSubは、次回以降にトークンのやり取りをしなくてもよいように、呼び出したページ内で有効なcookieの中にセッショントークンを保存する。これは通常の場合は構わないかもしれない(もともとそのサービスの提供者であるため)が、ブックマークレットとして呼び出した場合は、セッショントークンの保持者は実質的にはユーザ自身であるはずなので、なるべくサーバには伝えてほしくないはずだ。そのため、処理が終わったらログアウトメソッドを呼び出して無効にしてやるのがおそらく望ましい。

他のサービスは?

Google GData API同様に、Webブラウザから直接読み書きできるサービスとしては、AOL WebAIMやFacebookJavaScript APIがある。見たところ、AOL WebAIMはリダイレクト元を登録制にしているのでおそらく使えない。Facebook の方も DeveloperキーをサイトURLにひもづけているならばだめだし、そもそもWebサービスコンシューマ側の要件として、クロスドメイン通信を達成するためのIFRAMEプロキシHTMLを同一ドメインのサーバに置くように促しているため、ユーザから勝手に使うということはできそうにない。

もちろんブラウザからはみ出してグリモンやらGearsやらAIRやらやればできるだろうけど、そこにいっちゃったらおもしろくないじゃん、という主旨で書いているのはご理解いただけているかと思います。

感想

自分のデータの使い方は、データを預かっているサービスプロバイダでもなく、そのサービスプロバイダと連携しているサイトの開発者でもなく、自分が決める。これは一種の User Centric Web Service, あるいは Data Portability の姿なんだとおもうんですが、どうでしょうか。