Firefox 4 での History API の変更

補足: この記事では HTML5 の History API の仕様に問題があり、Firefox 4 では改善された API を提案・実装した事が書かれています。これだけ読むと互換性が心配になるかと思いますが、この提案はすぐに他のブラウザベンダーからも賛同が得られ、仕様 にも他のブラウザの実装にも反映されています。より詳しくは WHATWG ML での議論などをご覧ください。

原文: History API changes in Firefox 4 (公開: 2011-03-02)

この記事は Gecko 開発者 Jonas Sicking によるゲスト投稿です。

もうご存知でしょうが、私たちは Firefox 4 のリリース直前という段階に来ています。Firefox 4 では HTML5History API (pushState(), replaceState() などを定義) を実装しています。この API は Safari, Chrome でも実装されていますが、Firefox 4 の実装はそれらと異なっています。これが重要なことなので、このポストで説明します。

少し前に、pushState API に重大な欠陥があることが分かりました。欠陥とは、 pushState() や replaceState() に state 引数を渡したページで、ユーザーがその状態にあるままページをリロードした際に、load イベントが発火するまでその状態にアクセスできないというものです。これはその状態にアクセスする手段である popstate イベントが load イベントが発火するまで発火されないことにあります。

つまり、state 引数を利用するページは、与えられた状態について知らないままページを描画しなければならず、さらに適切な状態がユーザーに表示されるのはページが完全にロードされた時になってしまうのです。

ここでいう「状態」とは、pushState()/replaceState() に渡された state 引数になります。URL (pushState()/replaceState() にもっと使い勝手の良い引数) は document.locationwindow.location など普通の API からアクセスできます。

この問題を解決するため、私たちは現在の HTML5 の定義と異なる実装を Firefox に施しています。変更点は次の 2 点です。

  • 現在の statewindow.history.state から常に参照できるようにした。これによって popstate の発火を待たずともページの状態にすぐアクセスできます。
  • popstate イベントを load イベントの直後に常には発火しないようになった。代わりに、実際にセッションヒストリーが遷移したとき (ユーザーが戻るボタンや進むボタンをクリックした、history.back()/forward()/go() が呼び出された、など) にのみ発火するようにしています。popstate イベントの目的はページの状態を得るために存在していましたが、window.history.state の導入でその必要がなくなりました。また、ページはこのイベントを予期せぬものとして扱い、さらにバグの温床であることもわかりました。

最初の変更はただ機能を追加しただけですから、後方互換であると言えるでしょう。このプロパティを利用していませんから、既存のコードに影響することはありません。

しかし、2 番目の変更は懸念する必要があるでしょう。あなたのコードが popstate イベントが常に発火されることを想定している場合問題が起こる可能性があります。しかし、この変更がリスクを和らげる側面もあります。Safari 5 はどうやらこの機能について誤った実装を行っているらしく、state が pushState()/replaceState() に指定されたときのみ popstate が発火するようなのです。つまり、state 引数を与えない限り、Firefox 4 からは Safari 5 と同じような挙動となります。

そしてもうひとつ加えようとしている変更があります。

  • popstate をページのロード中に発火することを許可する。

HTML5 は現時点でページの load イベントの後にしか popstate イベントを発火させないという、よく分からない制限がかけられています。これがどう悪いのかというと、たとえば pushState で動くリンクを持つページがあります。重い画像があるなどロード時間が長く、その最中にユーザーがリンクをクリックし、その後戻るボタンを押しても、popstate イベントが発火しないのです。私たちはこの制限を取り除き、戻る/進むボタンが押された際や history.back()/forward()/go() が呼び出された際には常に popstate イベントが発火されるようにしました。

私はいくつかのテストをしましたが、これらの変更で問題を見つけていません。この問題が見つかるのが遅すぎたため、Firefox 4 RC までこの変更は反映されません。テストビルドがある ので、それで試すことはできます。