Googleのトップページを開くと毎秒100回実行されるスクリプトを止めるユーザースクリプト

Googleのトップページを開くと毎秒100回JavaScriptが実行されるよ - 射撃しつつ前転をみて、ちょっとソース読んでみたらホントにそんな実装になっていた。んじゃ、止めちゃいますかってことで、サジェスト自体はそのまま機能させつつ、IMEでの確定前の入力には反応しないという実装に無理やり変えるスクリプトを書きました。一応動作を確認したのはFirefox3.5(Greasemokey 0.8)、Opera10、Google Chrome Dev(4.0.213.1)です。GreaseKitは実行タイミングの問題で怪しいかも。

404 Not Found – Userscripts.org

ちなみに、なんでGoogleはポーリングをしているかっていうと、IMEが確定する前の入力途中のキーワードを捕まえるためのようです。そこまでする必要ないよって人も多そうですよね。そういう人向けのスクリプトです。ただし、Googleのちょっとした実装の変更で動作しなくなりますし、そういった場合でも極力誤動作をし難いように書いてありますが、バグる可能性はあります。そのあたりはご容赦を。あと、毎秒100回っていうと凄そうだけど、大抵のPCではこれぐらいの処理でそこまで顕著に重くなるようなことはありません。このスクリプトを導入しても、多くの場合は気持ち軽くなったかもというプラシーボ効果しか得られない可能性は高いです(毎秒100回JavaScriptが実行ってのがどの程度なのか - 0xFF)。ちなみにサジェストを完全に止めるのはGoogleの表示設定で可能です。


以下は、Googleの難読化されたソースをどう読んだかっていうメモ書きです。

まず、HTMLソースを表示、script部分をざっと眺める。下のほうに、

google.ac.i(document.f,document.f.q,'','')}

という部分を見つけたので(document.f.qは検索ワードのinput要素)、ここの定義を探す。そのすぐ上でscriptを動的に読み込んでいるっぽいのでその中(extern_js/なんとか)でgoogle.ac.iを捜す。

window.google.ac={i:ga,h:qa,u:X}

という部分があり、ここの前後が無名関数で囲まれているので、この無名関数をFirebugのコンソールパッドにコピペ。
関数をtoStringするといい感じに整形してくれるので、Firebugのcopyコマンドと組み合わせて難読化されたコードを読みやすくする。
ちなみにどんな感じに整形されるかというと、

copy((function(){1;2;3;var x=0;alert(x);}).toString())

で、

function () {
    var x = 0;
    alert(x);
}

こんな感じになる。コード的に意味のない部分まで削ってくれるのが素晴らしい。

で、コードを読んでみると、function gaがサジェストの初期化、function caが無効化とわかった。
特に、function gaの

ba = window.setInterval(function () {var k = t.value;k != h && V(0);h = k;}, 10);

この部分でbaにInterval IDが入っているので、これを取得できればclearできるのだけど、baは無名関数内のプライベート変数なのでどうにもできない。
仕方ないので、function caで一時的に無効化して、window.setIntervalを上書きしてダミーのなにもしない関数に置き換え、function gaで有効化、その後window.setIntervalを元に戻すという処理でwindow.setInterval(function , 10);だけを無効化することに成功。
結果としてこんなコードに。(time !== 10 は関係のないsetIntervalを無効にしないための保険)

if (/^function ca/.test(ca)) {
	ca();
	var _setInterval = window.setInterval;
	window.setInterval = function(func,time){
		if (time !== 10) return _setInterval.apply(this,arguments);
	};
	var f = document.getElementsByName('f')[0];
	google.ac.i(f, f.q, '', '');
	window.setInterval = _setInterval;
}