LDRizeをFirefox13で使えるようにする 解説編

調べたこと、あったことの時系列に合わせて、大雑把に解説。

死亡

過去の問題ほどややこしくなさそうだと目星がついたので、のんびり調べているうちにこんな記事を見つける。

※2012/06/07 追記

下記手順は Firefox12&Greasemonkey(0.9.20)まで有効だったが、Firefox13 ではさらに「ldrize.user.js」を修正する必要があることが判明。

詳細(というか対処療法)は別エントリに掲載予定。

Fascinating: Firefox10 で LDRize を使うには?

ldrize.user.jsの修正だけでよいのなら、すぐに記事もアップされるだろう……と待ってみても、一向に更新されないので自分でどうにかすることを決意する。2〜3日の遅れとあなどるなかれ、拙速が尊ばれる部類の記事もあるのだ。特にこれはそう。そうに違いない。
(ところでblogspotにトラックバックってできないのね。Googleのサービスは時々こういうところで片手落ちなのが解せない)

調査

週末になってちょっと時間ができたのでLDRizeをいじる。安直にconsole.log使ってプリントデバッグ。今から考えるとエラーを吐かない種類の問題点だったので、こうするしかない。

途中までは順調だったが、 LDRize Class の中の initSiteinfo から先にたどれなくなる。

  initSiteinfo: function(){
	  var filter2 = function(arr, fn){
		  var res=[], tmp;
		  arr.forEach(function(arg){if(tmp=fn(arg)) res.push(tmp)});
		  return res;
	  }
	  try{
		  this.siteinfo_available = filter2(this.siteinfo_all, function(arg){
			  var s=new Siteinfo(arg);
			  return s.isAvailable() && s;
		  });
		  return this.siteinfo_current = this.siteinfo_available[0];
	  }catch(e){
		  this.disable = true;
		  return false;
	  }
  },

filter2 に渡される無名関数の中で、 var s=new Siteinfo(arg) が複数回実行されるところまでは確認できるのに、その続きで死んでいる。
arr.forEach だといつ死んでいるのか明確でないので、同じ動作をするfor文に書き換えて( for(var i = 0; i < arr.length; ++i) )、ループの何回目で死んでいるのか確認。 arr.length も出力してみて、明らかにループ途中で止まっているのを確認。具体的には590ちょっとのSITEINFOがあるところ、174回目( i = 173 )でエラーも吐かずに落ちている。

174回目にあたるSITEINFOを確認すると、

アイテム詳細: google sites


name google sites
domain //b[@class="powered-by"]/a[contains(@href, "sites.google.com")]
disable
paragraph id("sites-canvas")/descendant::*[self::h1 or self::h2 or self::h3 or self::h4 or self::h5 or self::h6]
link
view
stripe
height
exampleUrl http://wiki.slash-reader.com/

アイテム: google sites - データベース: LDRize - wedata

明らかにdomainがあやしい。

犯人

順当に今度は isAvailable の中身の確認に入る。場所としては Siteinfo Class 内の isAvailable 。

  isAvailable: function(){
	  try{
		  if((this.domain == true || this.domain == 'microformats') &&
			 $X(this.paragraph).length){
			  return true;
		  }
		  if( this.domain.length && location.href.match(this.domain) && (this.disable || $X(this.paragraph).length)){
			  if(this.disable) throw 0;
			  return true;
		  }
		  if($X(this.domain).length && (this.disable || $X(this.paragraph).length)){
			  if(this.disable) throw 0;
			  return true;
		  }
	  }catch(e){
//		  log(['errer', info]);
		  if(e==0) throw 0;
	  }
	  return false;
  }

ふたつ目のif文が機能していないことが分かる。やっぱりdomainがあやしい。
&& で区切られている真偽値の判定を、ひとつひとつがif文になるよう入れ子状に書き換えて、console.logを挟む。
ということで、実用編で述べている問題の箇所をあぶり出せた。

こういうことだ。

まとめ

javascriptの動作として考えると、LDRizeが動かなくなった要因は、

ということなので、短くまとめると、

  • Firefoxの仕様が少し変わった
  • 特に正規表現RegExp)まわりで変わった
  • matchに文字列渡すと、内部では正規表現オブジェクトへと暗黙の変換をしているはず
  • 暗黙の変換がエラーを吐かない
  • つまり不正な文字列を渡すとエラーを吐かずに死ぬのでデバッグが超大変
  • 明示的に変換して渡してやるといい
  • 明示的な変換の段階でエラーが出る

以上。

感想

正規表現オブジェクトの動作は、これがバグじゃなかったら何なんだという感じなので、おそらくFirefox13.01あたりになったら直っているんじゃないだろうかと思う。もちろんそれまでLDRizeなしで過ごす気はないのでさっさと対策を施してしまうほうがずっといい。

LDRizeをFirefox13で使えるようにする 実用編

Firefox13へのアップデートが提供されて早や5日、世間的にはスクロールがぬるぬるするのが気持ち悪いという声がほとんどなのではないかというタイミング。メモリがどうのアドオン・スクリプトの対応がどうのという声は、いつぞやのアップデートに比べるとさほど大きくないように感じられる。

LDRizeは死活問題

しかし、LDRizeは例によって動かなくなってしまい、数々の対応策――古いブラウザを使う、あるいはgreasemonkeyにパッチを当てる、あるいはスクリプト自体に手を加える、あるいはそれら全て――を駆使して使い続けてきた人が、今回のアップデートに被災し、あちこちでまた悲嘆の声をあげている。
twitterのリアルタイム検索なんかを覗くと、阿鼻叫喚を垣間見れる。

LDRizeがないとtumblrで快適なReblog環境が構築できない! ということで困っている人もたくさんいることだろう。

手っ取り早く動かす方法

長々と記事を読む気がない人も多かろうと思うので、結論だけ先に書く。

前提条件

Greasemonkey0.9.12以降でMinibufferとLDRizeが動かない理由とか - tyoro.exeで示された対策を処方済みであること。
これまでのLDRize死亡→復活の経緯もよくまとまっているので、情報を参照しやすくて実にありがたい。

具体的にやること

素のLDRize(ただしsharedObjectは使えるようにしてあるもの)なら992行目(SITEINFOを書き足している人はその限りではない)にある【location.href.match(this.domain)】を、【location.href.match(new RegExp(this.domain))】に書き換えるだけ。

差分は以下。

diff --git a/LDRize.user.js b/LDRize_mod.user.js
index 128fa25..ae7cabd 100644
--- a/LDRize.user.js
+++ b/LDRize_mod.user.js
@@ -7,7 +7,7 @@
 // @include        file:///*
 // ==/UserScript==
 
-const SCRIPT_VERSION = "2011.11.11"	//sharedObject
+const SCRIPT_VERSION = "2012.06.10"	//sharedObject and RegExp
 const SCRIPT_URL     = "http://userscripts.org/scripts/show/11562"
 
 // ------------------------------------------------------------------
@@ -989,7 +989,7 @@ Siteinfo.prototype = {
 			 $X(this.paragraph).length){
 			  return true;
 		  }
-		  if( this.domain.length && location.href.match(this.domain) && (this.disable || $X(this.paragraph).length)){
+		  if( this.domain.length && location.href.match(new RegExp(this.domain)) && (this.disable || $X(this.paragraph).length)){
 			  if(this.disable) throw 0;
 			  return true;
 		  }

gistはコチラ LDRizeをFirefox13で動かす · GitHub

脆弱性とか

だいぶ前、それこそ年が変わる前に見たものと、年明けすぐの記事とを見て、何か書いておかなくちゃと思ってずっと放置していたのだけれど、一応忘れないようにメモ。

去年のがこれ。
id:teramako:20111121:p1
Minibuffer Exploit - hogehoge @teramako

年が明けてからのがこれ。
g:moz-addon:id:teramako:20120106:1325859336
Components.stack と Greasemonkey の GM_apiLeakCheck - hogezilla - Mozilla 拡張機能勉強会

ともに id:teramako さんの記事。
前者の原因が後者にあるのではないかと思っているのだが、今さらになって記事にしているくらいなので、ソースを読んだりデバッグや検証をしようという気力は今んところない。他力本願極まれり。

書いてるうちに思いついたこと(暫定解決策)

pageElementがそもそも追加ノード、追加予定ノードの情報を全部持ってるんだから、こいつを上手くバラしてやればいいんだ、ということで、多分ページ側でAutoPagerizeの持つ全イベントのエミュレートは可能、だと思う。

おそらく、

    pageElement:    '//hoge/fuga|//hage/piyo',

に対して、

    pageElementN:    '(//hoge/fuga|//hage/piyo)[N]',

という感じでN番目のノードを指定してやれば、うまいこといくはず。

ところでウチのページに引用した箇所の見え方がひどい。CSSいじくったほうがいいのかね……。