SSTabRestoring/SSTabRestoredイベントが、「ウィンドウ全体の復元」の時の物か「個別のタブの復元(または複製)」の時の物かを判別する宣伝。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。2009Firefoxでは、タブがセッション情報も伴って復元された時に、SSTabRestoringとSSTabRestoredという2つのイベントが発行される。イベントのoriginalTargetはいずれも復元されたタブの要素で、SSTabRestoringはセッション復元処理が走ったけれどもタブの読み込みは完了していない段階、SSTabRestoredはタブの読み込みが完了した段階で発行される。これらのイベントが発行される場面は、3つある。ウィンドウが復元された時(起動時のセッション復元、「最近閉じたウィンドウ」からの復元など)タブが個別に復元された時(「閉じたタブを元に戻す」など)タブが複製された時この3つの場合を、特に1とそれ以外とを判別したい、その方法を考えてみたよ、というのがこのエントリの主題です。いう場面で必要か例えばツリー型タブでは、タブの親子関係が変更された時のインデントの変更やツリーの折りたたみ時にアニメーション効果を適用している。Firefoxのセッション復元時に複数のタブのツリー構造を一気に変更する場合には、いちいちアニメーションしてたら重くてしょうがない。この時だけはアニメーション効果を適用しないようにしたいと僕は思った。前述のSSTabRestoringとSSTabRestoredからは、そのイベントが発行されたのが上記の3つの場面のうちいずれなのかが分からない。どの場面でイベントが発行されたのかを判別するには、他の情報も参照する必要がある。実は1の場合については、セッションが復元される時にObserverServiceに登録したオブザーバに対してsessionstore-windows-restoredというメッセージが通知される。これを監視すればいいんじゃないか?と、最初のうちは考えてた。話はそう単純には済まなかった。実際にイベントを監視してみると、セッション復元時には以下のような順番でイベントが発行されていることが分かった。現在のタブのSSTabRestoringイベントが発行される。sessionstore-windows-restoredが通知される。残りのタブのSSTabRestoringイベントが順番に発行される。それぞれのタブのSSTabRestoredイベントが読み込みの終わった物から発行される。3と4はこの通りにならないこともあって、あるタブのSSTabRestoringが発行されてすぐに読み込みが完了したタブについては、次のタブのSSTabRestoringが発行される前にSSTabRestoredが発行される場合もある。致命的なのは、複数タブのセッション復元が始まったことは通知されるのに、全部のタブのセッション復元が終わったことは通知されないという点だ。最初にsessionstore-windows-restoredが通知されたらその後に発行されたSSTabRestoringは全部ウィンドウ単位のセッション復元の一部なんだな、と判断してしまうと、例えば10個のタブを開いたウィンドウのセッションを復元した後、1つタブを開いて、閉じて、開き直した時、そのタブまで「ウィンドウ単位のセッション復元の一部」と誤爆してしまう。「SSTabRestoredが発行されるまでの間にSSTabRestoringが発行されたタブ」がウィンドウ単位のセッション復元なんだな、と判断してしまうと、後続のタブのSSTabRestoringよりも前にSSTabRestoredが発行された時に、セッション復元が終わってないのに終わったと見なされてしまう。いう具合で、「ウィンドウ単位でのセッション復元の終わり」がいつなのかが分からないと、誤爆しまくりで全然役に立たない。重ねて困ったことに、sessionstore-windows-restoredの通知はsubjectがnullなので、どのウィンドウでセッション復元が開始されたのかすらオブザーバ側からは分からない。別のウィンドウでセッション復元がはじまった時に来た通知を見て「これからこのウィンドウで開き直されるタブは全部、ウィンドウ単位のセッション復元の一部なんだな」と判断してしまってはいけない。強引に判別するdump()をコードの中に埋め込んで調べたところ、nsSessionStore.js内の各処理とイベントは、以下のような順番で起こっているらしいということが分かった。最後に復元されたタブのSSTabRestoredが発火したら、すべてのタブの復元が完了このうちnsSessionStore::restoreHistoryPrecursor()やnsSessionStore::restoreHistory()やnsSessionStore::restoreDocument_proxy()は、タブを1つ復元するだけの時にも使われてる。コードをよく読むと、nsSessionStore::restoreHistoryPrecursor()の中で「復元するタブの数だけ新しくタブを開く」「それらのタブのtab.linkedBrowser.parentNode.__SS_data._tabStillLoadingをtrueにセットする」という処理が行われて、その後でsessionstore-windows-restoredがオブザーバに通知されていた。(このtab.linkedBrowser.parentNode.__SS_data._tabStillLoadingは、タブの復元が完了した段階でundefinedになる。)ということは、sessionstore-windows-restoredが通知された時にウィンドウ内の全部のタブを調べて、tab.linkedBrowser.parentNode.__SS_data._tabStillLoadingがtrueであるタブが2つ以上ある(復元待ちのタブが複数ある)なら、そのウィンドウでこれからウィンドウ単位のセッション復元が行われようとしている、と考えてよいわけだ。SSTabRestoredが発行される度に復元待ちのタブの数を確認して、復元待ちのタブの数が0になったら、ウィンドウ単位でのセッション復元が終わったと判断できる。厳密には、「ウィンドウ単位でのセッションの復元中に別途タブを1個だけ復元した」という場合にもそのタブが「ウィンドウ単位のセッション復元の一環で復元された」と見なされてしまうので、この方法にも穴はある。たくさんのタブのツリー構造を一気に変更した時にアニメーションでクソ重くなるのを避けたい、という僕の目的ではこれで必要十分なので、これ以上は追求してない。まとめ以上をコンパクトにまとめると、こんな感じになる。false);セッションストアAPIを使ってタブにいろんな情報を紐付けて、その情報に基づいてあれこれしたい人には、役に立つんじゃないでしょうか。atの末尾に2020年11月30日時点の日本の首相のファミリーネーム(ローマ字で回答)を繋げて下さい。「noda」なら、「2009-10-27_sstabrestoring.trackbacknoda」です。これは機械的なトラックバックスパムを防止するための措置です。