Subscribed unsubscribe Subscribe Subscribe

枕を欹てて聴く

香炉峰の雪は簾を撥げて看る

Chrome Dirty Hacks 01: fc2 image for LDR

続ける予定は未定ですが01と銘打ちました, ChromeのDirtyなHackをお届けするChrome Dirty Hacks.

今回のレシピは, LDR上でfc2の画像を表示する です.

注意

Chrome Dirty Hacksはたまーに見つかるあんなのやこんなので, Chromeで実用性皆無のdirtyなhackを! というもので, 実用性本位ではありません. 最近ちょっと忙しいのですが, せっかくろくでもないことを思いついたので, 試しておかなくてはと...

結果

あら不思議!
f:id:Constellation:20100119214607p:image
fc2のblogで著名なネタ帳さんのページを例としてあげさせていただきました.
LDRの記事未読件数とかが破産寸前になってて恥ずかしいので隠しました.

成果物のChrome拡張はここにおいておきます. ちなみにsupportは予定していません...
fc24ldr.crx

内部的概要

fc2の画像はfc2以外から見られるのを防ぐために, referrerをcheckしています. もしreferrerにfc2以外のものが入っていたら画像表示が防がれてしまうわけです. これは普段は特に気になりませんが, 例えばLDRなどでRSSを表示するとこの問題が非常に困るわけです. ちなみにこのcheckによるとfc2以外のdomainが入っていた場合に防がれてしまうため, referrerにfc2のdomainが入っているか, もしくはそもそもreferrerが入っていない場合には画像を表示することが出来ます.

そしてここで本題. Chrome拡張機能の開いているbackgroundのページはhttpから始まるURLではありません. こんな変なURLからreferrerを送られてきたって送られる側もいい迷惑ですし, 拡張のIDが入っているURLを送るのはあまりsecurity上よろしくありませんね. それで, 実はChromeのbackgroundページやpopupなどchrome urlのページからはreferrerが送られていないんです. 考えてみれば当たり前ともいえます.
しかしこれを利用してbackgroundでfc2の画像をloadすれば, load出来てしまいます. おいしいです.

技術概要の流れを以下に示します.

  1. backgroundで通信を待ち受ける
  2. LDR でimgがDOMNodeInsertedされるのにhookしてimg収集, 画像のsrc urlがfc2 or amebaならbackgroundにurlを送りつける
  3. backgroundでimgをXHRでloadする. このとき, Referrerが入っていないのでloadできる.
  4. binaryを取得してbase64変換してLDR側にdataURLを返却する
  5. LDRのNodeのsrcにdataURLをいれる

binaryを扱おう!

ImageにむかってXHRを出すときに, charset=x-user-definedを指定しています.

// req は new XMLHttpRequest();

//XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com]
req.overrideMimeType('text/plain; charset=x-user-defined');

JavaScriptの文字列はUTF-16であり, XHRは自動的にページのEncoding情報にあわせて外部データをencodeしようと試みます. このときoverrideMimeTypeを指定しておくとこの情報に合わせてencodeを行ってくれるわけです. しかし, 今回ほしいのはascii互換のUTF-8でもなく, binaryです. つまりうっかり解釈できるからといってそのうちの2byteをとって一文字に変換でもされてしまうと, 長さが狂ってしまうわけです. この指定を行うとencodeされない状態でresponseが返ってきます.

そしてもうひとつがこれです.

btoa(res.responseText.replace(/[\u0100-\uffff]/g, function(c){
  return String.fromCharCode(c.charCodeAt(0) & 0xff);
}))

上でparseされないまま文字列長に入れられた1byteずつのdata. 1byteずつのためUTF-16の表記の最小単位である2bytesずつの文字の中に入っているわけですが, 上1byteは明らかに勝手に入ってしまったごみです. そこでc.charCodeAt(0) & 0xffで上のbyteを吹き飛ばします.
それをbtoa関数でBase64化してしまえばdataURLを手に入れることが出来ます.

ここらへんのことは Using XMLHttpRequest - Document Object Model (DOM) | MDN に詳しく解説されています. また, id:youpy さんの書かれた, GlitchMonkey も非常に参考になりました.

補足

リダイレクトされている画像が表示できない疑惑が出ていますが, ネタなのでここまでで.
実用にもならないネタプログラミングは超面白いので, みんなやるといいよ!