User Scriptのnicovideo Thumbinfo popupを不完全ながらGoogle Chromeで動作させる事に成功した

自分がGoogle Chromeを最初のリリースから使い始めて約2年半、Mozilla Firefoxから完全に移行してもうすぐ1年になる。Firefoxの利用時は拡張によって多くの機能が利用できていたので、Chromeにはなかなか移行する事ができなかったが、Firefoxのあまりの初回起動速度の遅さに耐えかねてChromeに移行することを決意し、今に至っている(拡張や履歴、ブックマークの管理と設定を改善し、SSDやRAM Diskが利用できる性能の高いPCを使用するならば問題ないのかもしれないが…)。その際に諦めなければならなかった拡張機能は多数あったが、その中でも特に困ったのが、Firefoxの拡張であるGreasemonkeyによって動作するUser Scriptのnicovideo Thumbinfo popupがChromeでは動作しない事だった。

image

nicovideo Thumbinfo popupはニコニコ動画のリンクをマウスオーバーすると、画像で示されているように様々な情報を表示してくれる。ChromeはUser Scriptをインストールすると、拡張としてある程度は動作してくれるのだが、nicovideo Thumbinfo popupはGreasemonkeyのAPIであるGM関数(GM_*)や、Firefoxが独自に実装している機能(分割代入、Object.watchなど)を処理に組み込んでいた為、動作しなかった。移行時は仕方なく諦めたのだが、その後、NinjaKitという、User ScriptがいくつかのGM関数を含んでいてもChrome上で動作させる事ができるGoogle Chrome版Greasemonkeyといえる拡張が公開され、これによってどうにかならないだろうかと試してみたのだが残念ながら動作しなかった。

それから半年ほど経った頃から、Tumblrのテーマのカスタマイズに興味を持った事をきっかけにJavaScriptについて勉強し始め、最近になってGitHubで他の人が公開したUser Scriptを自分なりに修正して動作させられるようになってきた。これによって、nicovideo Thumbinfo popupを自分で修正して、何とかChrome上で動作させるようにできないかと思い、1週間程前から試行錯誤を重ねていた。

まず、nicovideo Thumbinfo popupは2000行以上、50KB以上という巨大さなので、自分の力ではまだその全容を把握する事が難しく、どこを修正すれば良いのか最初はわからなかった。その為、コードの最初の方から地道に修正しつつ、コードを参考に1から自分でChrome拡張として作れないかと無謀な事をしてみたのだが、やはりまだ自分ではそれも難しく、作業は遅々として進まなかった(ようやくprototype拡張の箇所(400行ぐらい)を「目が通せた」ところで、ほとんど理解できてない)。Chrome拡張でCross-Origin XMLHttpRequestをする為に最低限必要な処理を知る事ができたのは良い成果だったが。

しかし、コードを読んで、その断片がChromeのデベロッパーツールやFirefoxの拡張であるFirebugのConsoleで動作するかどうかを試していく内に、何が原因で動作しないのかが僅かにわかってきた。それは関数内で「Array.shift(arguments)」などとしている処理もChromeでは動作しないという事だった。これは「[].shift.call(arguments)」などと置換する事でChromeでもFirefoxでも動作させられるようになった。このようなケースが他に無いか探してみたところ、他に「Array.slice」、「Array.unshift」、「Array.forEach」、「Array.map」が該当する事がわかった。

さらに、分割代入もChromeでは動作しないようだった。分割代入による変数宣言を行うと、変数にオブジェクト同士で対応するプロパティ名の値が代入される(参考)。

var array = [1, 2],
    object = {c: 3, d: 4},
    [a, b] = array,
    {c: e, d: f} = object;
console.log(a, b, e, f); // a -> 1, b -> 2, e -> 3, f -> 4(Firefox)

分割代入の存在を知ったのはNinjaKitが公開された時に付属のJSLintによって調べていた時だったが、改めて調べてみると以下のようにすれば上記と同じ結果が得られる事がわかった。

var array = [1, 2],
    object = {c: 3, d: 4},
    a = array[0], b = array[1],
    e = object.c, f = object.d;
console.log(a, b, e, f); // a -> 1, b -> 2, e -> 3, f -> 4(Chrome, Firefox)

nicovideo Thumbinfo popupでは配列同士の分割代入のみ行っていたようで、該当する12箇所をこれに従って書き換えた。関数や、文字列や配列のメソッドによって新たに生成した配列を代入している場合は、新たに一時変数を作成して配列自体をキャッシュし、それを使用して各変数に代入するようにした。

そして、GM_getResourceText。このGM関数はUser Scriptの所定のところに「@resource 名前 ファイルを指す任意のURL」と記述しておくと、User Scriptのインストール時にURLからJavaScriptやCSSファイルなどをダウンロードして、そのファイルをこの関数によって読み込み、User Script内の処理で扱えるようにできるらしい(参考)。nicovideo Thumbinfo popupには他にもGM関数が含まれているのだが、GM_getResourceTextは先程のNinjaKitでも動作できるようにはなっていない。だが、これは予めファイルの中身(nicovideo Thumbinfo popupの場合はCSS)をUser Script内のGM_getResourceTextと置き換えて、CSSに存在する改行や「-moz-」のようなベンダープレフィックスを削除したりコメントアウトを入れて, 全体をシングルクオーテーションで囲ってしまえば何とか動作するようだった。

ここまで修正したところで、NinjaKitならば動作させられるのではないかと思い、NinjaKitに修正したnicovideo Thumbinfo popupをインストールして、NinjaKitのエディターから「DEBUG」変数を「true」と編集してログを出力するようにして動作を確認してみた。すると「TypeError: Object #<Object> has no method ‘watch'」というエラーが出力された。そこでまた調べてみるとObject.watchもまたChromeでは動作しないという事がわかった。Object.watchが使用されている処理は1つだけだったので、その箇所を削除して再度試したところ以下のように表示された。

image

こ、これは…!! まさかここまでFirefoxと同じように表示されるとは思っていなかったので大変驚いた。タグの右横にニコニコ大百科のアイコンが無いのは、Object.watchが使用されている処理を削除した為のようだった。そこでオブジェクトのprototype拡張によりChromeでもFirefoxのObject.watchに近い挙動をさせる事ができないかと調べてみたところ、参考となるスニペットを公開しているエントリーが見つかった。これを、修正したnicovideo Thumbinfo popupでも扱えるように、Object.watchに関するMDCのサンプルも参考にしつつ自分で修正してみたところ、以下のようになった。

Object.prototype.watch = function(prop, handler) {
    var oldval = this[prop],
        that = this;
    Object.prototype.i = {};
    this.i[prop] = setInterval(function () {
        var newval = that[prop];
        if (oldval !== newval) {
            handler(prop, oldval, newval);
            oldval = newval;
        }
    }, 1);
};

これをprototype拡張が集中している箇所に追加して動作を確認したところ、以下のようになった。

image

おお…遂に…!! まだ様々な状況で検証してみる必要があり、エラーが出るケースがあるようで、完全とは言いがたいが、とりあえず何とかなったらしい。これから常用していけば問題点も自然とわかるだろう。その問題を修正できるかどうかはわからないが…。

確認した動作環境はWindows XP Professional SP3、Google Chrome 10.0.648.151 stable、NinjaKit 0.8。また、修正したnicovideo Thumbinfo popupは2010/10/30版。

ソースコードと修正の差分は以下で公開した。

https://gist.github.com/879869

とりあえず動作した段階なのでどのような問題が生じるかはわからないが、それでも使用してみたいという方は以下の注意点に留意してインストールしてみよう。動作にはNinjaKitが必須となる。User Scriptの使用は自己責任が大前提であるという事を強調しておく。

使用する際の注意点を以下に纏めておく。

  • このUser Scriptはほぼ全てのページで読み込まれる為、ブラウザのパフォーマンスが(もしかするとかなり)低下する可能性がある。
  • 基本的に「リンクにマウスオーバーしてポップアップが表示される」という挙動しか確認していないので、今のところキーボードショートカットなどの機能が動作しないのは不具合ではなく仕様である。
  • 修正を行った私、syoichiは現在存在している不具合や、今後のニコニコ動画、Google Chrome、NinjaKitなどのプラットフォームの仕様変更やnicovideo Thumbinfo popupの機能追加に対応できるだけの能力は全く無い。よって、万が一私のところにこの修正したUser Scriptに関して、何らかの要望や不具合報告が寄せられても全く応える事はできない。ただし、このページのコメント欄に不具合報告を残していく事は全く構わない。
  • ありえないとは思うが、NinjaKitやnicovideo Thumbinfo popupの開発者の方々に、この修正したUser Scriptに関して問い合せる事は、その方の迷惑になってしまうと思われるのでやってはいけない。

インストールは以下より。

https://gist.github.com/raw/879869/cd15c390790218be901a2cd18eea0a1c9acf6c95/nicovideo%20Thumbinfo%20popup.user.js

テスト用としてニコニコ動画のsm9のリンクを以下に用意した。NinjaKitにインストールしてこのページを再度読み込んだ後に、以下のリンクをマウスオーバーしてみよう。

テスト用リンク