[49] [[Webブラウザー]]は、[[ハイパーリンク]]をたどって次のページに進んだ時、
元のページの [CODE(DOMi)@en[[[Document]]]] [[オブジェクト]]や[[レンダリング]]に必要な情報を[[セッション履歴エントリー]]内に保存しておき、
次の「[[戻る]]」操作でそれを再利用することができます。これによって履歴操作による再[[レンダリング]]がかなり高速化できます。

[50] 履歴操作による再利用を見越したデータの保存を [DFN[[[bfcache]]]] といいます。
[[Webブラウザー]]は [[bfcache]] を実装する義務はありませんが、
多くのブラウザーが実装しています。 [CITE[HTML Standard]] は [[bfcache]]
を適用''できる''条件を規定しています。 (適用義務はなく、
[[メモリー]]利用量等を勘案していつでも[[キャッシュ]]を廃棄できます。)

[51] [[bfcache]] による再利用を考慮したページの表示開始や表示終了で[[発火]]される[[イベント]]として、
[DFN[[CODE[[[pageshow]]]]]] と [DFN[[CODE[[[pagehide]]]]]] があります。

* 仕様書

[REFS[
- [20] [CITE@en-GB-x-hixie[HTML Standard]] ([TIME[2015-04-25 04:40:19 +09:00]] 版) <https://html.spec.whatwg.org/#session-history>
- [41] [CITE@en-GB-x-hixie[HTML Standard]] ([TIME[2014-10-23 20:18:13 +09:00]] 版) <https://html.spec.whatwg.org/#pagetransitionevent>
- [32] [CITE@en-GB-x-hixie[HTML Standard]] ([TIME[2015-04-25 04:40:19 +09:00]] 版) <https://html.spec.whatwg.org/#unloading-documents>
]REFS]

* セッション履歴エントリーにおける文書

[21] [[利用者エージェント]]は、[[現在エントリー]]以外の[[セッション履歴エントリー]]の[[文書]]で[[スクリプト]]から参照されていないものについて、
[[文書を捨てる]]ことにしても構いません。 [SRC[>>20]]

[23] [[利用者エージェント]]がいつ[[キャッシュ]]を保持し続けるべきか、
いつ捨てるべきかは [CITE[HTML Standard]] としては規定していません [SRC[>>20]]。

;; [25] [[利用者エージェント]]は、[[利用者]]体験と利用可能な[[メモリー]]等の[[資源]]の制約のバランスを考慮して保持するかどうかを決められます。

;; [60] [[HTTPキャッシュ]]に対する指示である [CODE(HTTP)@en[Cache-Control:]]
[[ヘッダー]]の指示を [[bfcache]] に適用する義務はありません。

[22] 捨てられた場合、[[利用者]]または[[スクリプト]]により再度そのページに遷移した時は、
新たにそのページを読み込む ([[エントリー更新]]の [[navigate]]
を実行する) ことになります [SRC[>>20]]。

[24] 捨てたかどうかによって[[セッション履歴]]に関する処理に影響があってはなりません。
文書が捨てられていて新たに読み込み直した場合は、同じ[[文書]]を共有していた他の[[セッション履歴エントリー]]の[[文書]]も同じ新しい[[文書]]としなければ[['''なりません''']] [SRC[>>20]]。

[28] しかしながら、捨てて再 [[navigate]] して同じ状態が復元される保証はありません。
(むしろ一般には異なる状態になります。) [[ネットワーク]]が切断されて接続できなくなれば、
以前は通常の[[文書]]だったものが[[ネットワークエラー]]の表示に変わったりします。

[FIG(list)[
- [29] 再 [[navigate]] が[[閲覧文脈]]内の表示ではなく、[[ダウンロード]]等[[閲覧文脈]]外の表示となった場合、
[[セッション履歴エントリー]]の更新は行われず、[[セッション履歴]]の移動 ([[履歴の探索]])
も行われません。
- [30] 再 [[navigate]] が[[閲覧文脈]]の[[活性文書]]の[[素片識別子へのスクロール]]だけに留まった場合、[[セッション履歴エントリー]]の更新は行われず、かわりに[[現在エントリー]]の次に[[素片識別子]]へスクロールした[[セッション履歴エントリー]]が挿入されることになります。
- [31] それ以外なら[[エントリー更新]]が行われますが、新しい[[文書]]の[[スクリプト]]が
[[navigate]] を行った場合など、新[[現在エントリー]]とそれ以後の[[セッション履歴エントリー]]を新しく作成することになる場合もあります。[[文書]]の破棄前の[[セッション履歴]]の状態が復元される保証はありません。
]FIG]

[40] [[安全メソッド]]では無いなら、自動的に再 [[fetch]] するのは好ましくありませんから、
エラー文書がかわりに表示されるべきと思われます。[[利用者]]が明示的に[[再読込]]したら、
再度 [[fetch]] するべきです。

;; [39] [[セッション履歴エントリー]]も参照。

@@
[61] 
再 [[navigate]] における[[キャッシュ指令]]の挙動

* イベント

[FIG(short list)[
- [CODE(DOMe)@en[[[pageshow]]]]
- [CODE(DOMe)@en[[[pagehide]]]]
]FIG]

[35] [CODE(DOMe)@en[[[pagehide]]]] は、[[文書のunload]]時に[[発火]]されることがあります。

[33] [[文書]]の [DFN[[[page showing]]]] フラグは、 [CODE(DOMe)@en[[[pageshow]]]]
と [CODE(DOMe)@en[[[pagehide]]]] の整合性を保つためのフラグです [SRC[>>32]]。 
[[文書]]作成時の初期値は[[偽]]です [SRC[>>32]]。両イベントの[[発火]]により値が変化します
([[履歴の探索]]、[[文書のunload]]、[[stops parsing]] 参照)。

* [CODE(DOMi)@en[PageTransitionEvent]] インターフェイス

[42] [DFN[[CODE(DOMi)@en[[[PageTransitionEvent]]]]]] [[インターフェイス]] [SRC[>>41]] は、
[CODE(DOMe)@en[[[pageshow]]]]/[CODE(DOMe)@en[[[pagehide]]]] [[イベント]]と共に用いられます。
[[文書環境]][DEL[と[[ワーカー環境]]]]に[[晒され]]ています [SRC[>>41]]。

[44] [CODE(DOMi)@en[[[PageTransitionEvent]]]] [[インターフェイス]]は、
他の [CODE(DOMi)@en[[[Event]]]] [[インターフェイス]]同様の[[コンストラクター]]を持ちます
[SRC[>>41]]。
初期化のための辞書は [DFN[[CODE(DOMi)@en[[[PageTransitionEventInit]]]]]] です [SRC[>>41]]。

[43] [CODE(DOMi)@en[[[PageTransitionEvent]]]] [[インターフェイス]]は、
[CODE(DOMi)@en[[[Event]]]] [[インターフェイス]]を[[継承]]しています [SRC[>>41]]。
[CODE(DOMi)@en[[[PageTransitionEventInit]]]] [[辞書]]は、
[CODE(DOMi)@en[[[EventInit]]]] [[辞書]]を[[継承]]しています [SRC[>>41]]。

* [CODE(DOMi)@en[PageTransitionEvent]] インターフェイス [CODE(DOMa)@en[persisted]] 属性

[45] [CODE(DOMi)@en[[[PageTransitionEvent]]]] [[インターフェイス]]および
[CODE(DOMi)@en[[[PageTransitionEventInit]]]] [[辞書]]は、[CODE(IDL)@en[[[boolean]]]]
型の読み取り専用[[IDL属性]] [DFN[[CODE(DOMa)@en[[[persisted]]]]]]を持ちます [SRC[>>41]]。
初期値は[[偽]]です [SRC[>>41]]。

[34] [[文書]]は、 [DFN[[[''salvageable'']]]] かどうかの状態を持ちます [SRC[>>32]]。
これが [CODE(DOMa)@en[[[persisted]]]] [[属性]]の値として使われます。

[37] [[''salvageable'']] の初期値は[[真]]です [SRC[>>32]]。 

[36] 次の操作を行うと、 [[''salvageable'']] は[[偽]]となります。
[FIG(list)[
- [DEL[[CODE(JS)@en[[[document.open]]]] の実行]]
- [CODE(DOMe)@en[[[beforeunload]]]] の[[イベントリスナー]]の呼び出し ([[prompt to unload a document]])
- [CODE(DOMe)@en[[[unload]]]] の[[イベントリスナー]]の呼び出し ([[文書のunload]])
- [CODE(DOMi)@en[[[WebSocket]]]] [[オブジェクト]]が[[文書を捨てる]]時点で未破棄
- [[文書のabort]]による [[abort]] の発生
- [[子孫閲覧文脈]]が [[''salvageable'']] ([[文書のunload]])
]FIG]

[46] [CODE(JS)@en[[[document.open]]]] では、 [[''salvageable'']] が[[偽]]になったあと、
再び[[真]]となることがあります。

[38] [[利用者エージェント]]は適宜未参照の[[文書]]を捨てられますから (>>21)、
[CODE(DOMa)@en[[[persisted]]]] が[[真]]であっても、後から [[bfcache]]
で再表示される保証はありません。

* 関連

[52] [CODE(DOM)@en[[[pageshow]]]]/[CODE[[[pagehide]]]] と前後して、
[[Page Visibility]] も変化します。

* 歴史

** bfcache (Firefox)

[1] [CITE@en-us[Using Firefox 1.5 caching - MDC]] ([TIME[2009-01-24 00:43:53 +09:00]] 版) <https://developer.mozilla.org/ja/Using_Firefox_1.5_caching>

- [11] [[Firebug]] が動作していると機能しない? [TIME[2011-02-08T06:29:29.700Z]]
- [12] [[XHR]] が接続中だと機能しない? [TIME[2011-02-08T06:29:40.500Z]]

** Fast history navigation (Opera)

[2] [CITE@en[JavaScript and History Navigation - Opera Knowledge Base]] ([TIME[2009-02-08 17:32:47 +09:00]] 版) <http://www.opera.com/support/kb/view/827/>

>Starting in Opera 9.0 Technical Preview 2, Opera attempts to detect pages that rely on this behavior, and will not use fast history navigation on those pages. This means that the page, and any scripts it contains, will be loaded from cache, and processed again when the page is loaded using the back or forward navigation. This is known as "compatible history navigation". 

[5] いつ互換モードになるのかの条件は >>2 に明記されていませんが、
[CODE(JS)@en[[[onload]]]] と [CODE(JS)@en[[[onunload]]]] が関わっているようです。

[3] [[利用者JavaScript]] で[[履歴ナビゲーション・モード]]を指定するには、
[CODE(JS)@en[[[opera.setOverrideHistoryNavigationMode]]]] [[特性]]を使います。
値は [CODE(JS)@en[[[automatic]]]]、[CODE(JS)@en[[[fast]]]]、
[CODE(JS)@en[[[compatible]]]] です。 [SRC[>>2]]

[4] [[著者]]が[[スクリプト]]で[[履歴ナビゲーション・モード]]を明示的に指定するには、
[CODE(JS)@en[[[history.navigationMode]]]] [[特性]]を使います。
値は [CODE(JS)@en[[[automatic]]]]、[CODE(JS)@en[[[fast]]]]、
[CODE(JS)@en[[[compatible]]]] です。 [SRC[>>2]]

[6] なんか、 [CODE(DOMa)@en[[[onunload]]]] を設定しても [CODE(DOMa)@en[[[onload]]]]
が呼ばれないという情報がありました。どういうときに自動判定で互換モードになるのでしょうかね?

** Page cache (WebKit)

[REFS[
- [17] [CITE[Surfin' Safari - Blog Archive » WebKit Page Cache I – The Basics]] ([TIME[2014-10-27 02:01:28 +09:00]] 版) <https://www.webkit.org/blog/427/webkit-page-cache-i-the-basics/>
- [18] [CITE[Surfin' Safari - Blog Archive » WebKit Page Cache II – The unload Event]] ([TIME[2014-10-27 02:01:43 +09:00]] 版) <https://www.webkit.org/blog/516/webkit-page-cache-ii-the-unload-event/>
]REFS]

** 標準化

[7] [CITE@en[(X)HTML5 Tracking]]
([TIME[2009-10-21 23:23:27 +09:00]] 版)
<http://html5.org/tools/web-apps-tracker?from=4230&to=4231>

[8] [CITE[Bug 7896 – Specify pageshow and pagehide events]]
([TIME[2009-10-25 15:53:56 +09:00]] 版)
<http://www.w3.org/Bugs/Public/show_bug.cgi?id=7896>

[9] [CITE[Changeset 47824 – WebKit]]
([TIME[2009-10-25 15:58:08 +09:00]] 版)
<http://trac.webkit.org/changeset/47824>

[10] [CITE[IRC logs: freenode / #whatwg / 20100113]]
([TIME[2010-01-15 23:57:05 +09:00]] 版)
<http://krijnhoetmer.nl/irc-logs/whatwg/20100113#l-474>

[55] [CITE@en['''['''giow''']''' (2) Make pageshow/pagehide use PageTransitionEvent, to match r…]] ([[Hixie]]著, [TIME[2010-02-23 20:29:14 +09:00]]) <https://github.com/whatwg/html/commit/9d17f6130b0dbcd46be35d952604ae7267b653e6>

[13] [CITE[''''''[''''''whatwg'''''']'''''' pagehide vs pagevis]]
( ([TIME[2013-08-30 00:53:42 +09:00]] 版))
<http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2013-August/040657.html>

[14] [CITE@en[Web Applications 1.0 r8160     Provide hook for pagevis. Correctly return false for event.persisted in pagehide. Define one of the variables in an algorithm.]]
( ([TIME[2013-09-01 06:43:00 +09:00]] 版))
<http://html5.org/tools/web-apps-tracker?from=8159&to=8160>

[15] [CITE@en[274784 – (blazinglyfastback) Make back and forward blazingly fast and side-effect free]]
( ([TIME[2013-12-16 09:05:37 +09:00]] 版))
<https://bugzilla.mozilla.org/show_bug.cgi?id=blazinglyfastback>

[16] [CITE[IRC logs: freenode / #whatwg / 20131214]]
( ([TIME[2013-12-16 09:01:34 +09:00]] 版))
<http://krijnhoetmer.nl/irc-logs/whatwg/20131214>

[FIG(quote)[
[FIGCAPTION[
[19] [CITE@ja[Chrome で高速にページを読み込む新しい技術のご紹介 - Google Developer Japan Blog]]
([TIME[2015-04-10 17:01:36 +09:00]] 版)
<http://googledevjp.blogspot.jp/2015/04/chrome.html>
]FIGCAPTION]

> Chrome 42 では、このコンパイル済みコードのローカルコピーを保持する拡張技術を採用することで、ユーザーがそのページに戻ってきたときにパースをダウンロードしてコンパイルする手順をすべて省略できるようになります。これによってすべてのページでコンパイル時間の 40% を削減し、モバイル端末において貴重なバッテリーの消費量を減らすことができます。 

]FIG]

[26] [CITE[Surfin’ Safari - Blog Archive » WebKit Page Cache II – The unload Event]]
([TIME[2009-10-25 15:54:55 +09:00]] 版)
<http://webkit.org/blog/516/webkit-page-cache-ii-the-unload-event/>

[27] [CITE@en[Web Applications 1.0 r8160     Provide hook for pagevis. Correctly return false for event.persisted in pagehide. Define one of the variables in an algorithm.]]
( ([TIME[2013-09-01 06:43:00 +09:00]] 版))
<http://html5.org/tools/web-apps-tracker?from=8159&to=8160>

[FIG(quote)[
[FIGCAPTION[
[47] [CITE[JavaScript - ブラウザバック時にリロードさせる - Qiita]]
([TIME[2015-07-17 12:48:40 +09:00]] 版)
<http://qiita.com/nabettu/items/48f066155f49629be83e>
]FIGCAPTION]

> window.onpageshow = function(evt) {
>     if (evt.persisted) {
>       location.reload();

]FIG]


[FIG(quote)[
[FIGCAPTION[
[48] [CITE@ja[戻るナビゲーションのキャッシュ (Windows)]]
([TIME[2015-09-29 15:28:32 +09:00]] 版)
<https://msdn.microsoft.com/library/dn265017(v=vs.85).aspx>
]FIGCAPTION]

> キャッシュされるためには、Web ページが次の条件を満たしていることが必要です。
-HTTP: プロトコルを使って表示される (HTTPS ページは、セキュリティ上の理由からキャッシュされません)。
-ページで beforeunload イベント ハンドラーが定義されていない。
-load イベントと pageshow イベントがすべて完了している。
-ページに次のいずれも含まれない。
--indexedDB トランザクションが保留されている。
--Web ソケット接続が開かれているか、アクティブである。
--Web ワーカーが動作中である。
--Microsoft ActiveX コントロール。
-F12 開発者ツールのウィンドウが開いていない。

]FIG]


[53] [CITE[Issue 511340 - chromium - Investigate faster back/forward page navigation - An open-source project to help move the web forward. - Google Project Hosting]]
([TIME[2015-12-19 00:34:02 +09:00]] 版)
<https://code.google.com/p/chromium/issues/detail?id=511340>

[54] [CITE@en[Editorial: define Event attribute defaults through IDL · whatwg/html@0be0229]]
([TIME[2016-03-23 21:15:47 +09:00]] 版)
<https://github.com/whatwg/html/commit/0be02299f128cbb7c65a7ce259fdd838fd44a4b5>

[56] [CITE@en[PopStateEvent/HashChangeEvent/PageTransitionEvent are Window-only]]
([[zcorpan]]著, [TIME[2016-09-13 21:25:40 +09:00]])
<https://github.com/whatwg/html/commit/dec1a5809e15d29592a66147ce2bed5018121b5f>

[57] [CITE@en[Wait for pageShow event on forward/back commands]]
([[AutomatedTester]]著, [TIME[2017-02-03 08:55:51 +09:00]])
<https://github.com/w3c/webdriver/commit/7c56dfc5f2fefd2af437bfd61e98f9837165704a>

[58] [CITE@en[567365 - Cache-Control no-cache on https page disables history]]
([TIME[2017-05-11 16:18:10 +09:00]])
<https://bugzilla.mozilla.org/show_bug.cgi?id=567365>

[59] [CITE[Microsoft Edgeでブラウザバックをした時ラジオボタンのチェックが外れる問題 - Qiita]]
( ([TIME[2017-05-24 17:03:18 +09:00]]))
<http://qiita.com/activefactor/items/990d5e02f2f69b90b172>

[62] [CITE@en[112564 - Cache-Control: no-cache should not affect back/forward buttons]]
([TIME[2017-06-27 13:22:47 +09:00]])
<https://bugzilla.mozilla.org/show_bug.cgi?id=112564>

[63] [CITE@en[Remove more types from document.createEvent()]]
([[ayg]]著, [TIME[2017-08-06 21:41:25 +09:00]])
<https://github.com/whatwg/dom/commit/5a532da58455e55b1e611161b72224d083fee7d4>

[64] [CITE@en[22291 - Investigate benefits of a bfcache - chromium - Monorail]]
([TIME[2018-04-03 23:36:56 +09:00]])
<https://bugs.chromium.org/p/chromium/issues/detail?id=22291>

[65] [CITE@en[2879 - Implement fast back - chromium - Monorail]]
([TIME[2018-04-03 23:37:42 +09:00]])
<https://bugs.chromium.org/p/chromium/issues/detail?id=2879>

[66] [CITE@en[511340 - Investigate faster back/forward page navigation - chromium - Monorail]]
([TIME[2018-04-03 23:38:11 +09:00]])
<https://bugs.chromium.org/p/chromium/issues/detail?id=511340>

[67] 
[[Google AdSense]] を埋め込むときに使われる
[[GPT]] は
<https://securepubads.g.doubleclick.net/gpt/pubads_impl_191.js> や
<https://securepubads.g.doubleclick.net/gpt/pubads_impl_rendering_191.js>
を読み込むのですが、
このスクリプト内で [CODE[unload]] や [CODE[beforeunload]]
の[[イベントリスナー]]が登録されています。 (パフォーマンス計測用?)
そのため [[bfcache]] が無効化されてしまうようです。
[TIME[2018-04-03T15:23:00.00Z]]

[68] [CITE@ja[Back-forward cache: web-exposed behaviour - Google ドキュメント]], [TIME[2020-07-22 12:22:55 +09:00]] <https://docs.google.com/document/d/1JtDCN9A_1UBlDuwkjn1HWxdhQ1H2un9K4kyPLgBqJUc/edit#heading=h.58d6ijfz2say>

[70] [CITE@en[GitHub - rakina/bfcache-explainer]], [TIME[2023-05-17T02:04:59.000Z]] <https://github.com/rakina/bfcache-explainer>


[69] [CITE@en[Back-forward cache on Android - Chrome Platform Status]], [TIME[2023-05-17T02:03:52.000Z]] <https://chromestatus.com/feature/5815270035685376>


[71] [CITE@ja[バック/フォワードキャッシュ]], [TIME[2023-05-15T23:44:55.000Z]], [TIME[2023-05-17T02:15:04.093Z]] <https://web.dev/bfcache/>


[72] [CITE@ja[BFCache Chrome devtools]]
([TIME[2023-05-17T02:30:44.000Z]])
<https://groups.google.com/a/chromium.org/g/bfcache-dev/c/HkgtcRIdjso?pli=1>

[73] >>72 [CODE[BrowsingInstanceNotSwapped]]

[74] [CITE@en[HTTPS pages with Cache-control: no-store and Back/Forward Cache - Chrome Platform Status]], [TIME[2023-05-17T03:13:10.000Z]] <https://chromestatus.com/feature/6705326844805120>




[75] 
[[Android]] の [[Chrome]] で謎に [CODE[BrowsingInstanceNotSwapped]] のために
[[bfcache]] が効かなかったのですが、
<chrome://flags/#back-forward-cache>
を有効にするとこのエラーは出なくなって普通に [[bfcache]]
が使われるようになりました。

令和3年の Chrome 96 から段階的にデフォルト有効化されてるとのことですが、
未だに完全には有効化されていないのか、
端末によっては設定の更新が行き渡っていないのか事情はよくわかりませんが、
まだ無効のままの端末もあるということみたいです。


そして [[Chrome]] の DevTools もこのフラグが無効なときはその旨を表示してくれるのですが、
挙動が謎で、 [[navigation]] の種類?によってその旨のエラーを出したり、かわりに
[CODE[BrowsingInstanceNotSwapped]] を出したりするみたいです。
[TIME[2023-05-17T03:26:45.900Z]]



[76] 
まったく同じ端末で同じページを表示して、
[[iOS]] の [[Safari]] では [[bfcache]] が効かないのに [[Chrome]] では効くことがあります。
どちらも同じ [[WebKit]] のはずなのに、このあたりは[[ブラウザー]]独自の部分が影響しているみたいです。



[77] 
[[Chrome]] のドキュメントのどれかに、 [[Android]] の [[WebView]] は複雑になるから
[[bfcache]] に対応する予定はないと書いてありました。
[[埋め込みブラウザー]]ではなく [[Chrome]] を使わないと[[利用者]]の体験が悪くなるケースがあるということ。



[78] 
[[Facebook]] の [CODE[iframe]] が [CODE[unload]] 使ってるせいで [[bfcache]] が効かないんだが、、、糞が


[79] 
[[iPhone]] の [[Safari]] の [[bfcache]] ([[Page Cache]]) は適用条件が厳しい(?)らしく、
[[Firefox]] や [[Chrome]] なら効くときでも [[Safari]] だと効かないことがかなりあるみたいです。
メモリー利用量なのか何なのかいまいちその条件はよくわからないのですが、
特定サイトのほとんど同じ構成のページでも情報量が多い所では効かなくて少ない所では効いたり、かと思ったら履歴移動中にさっきまで効いてたページが効かなくなってかわりにずっと効いていなかったページが効くようになったり、
とページ自体の構造と関係ない動的な何かで判断が変わってくるっぽくて。
[TIME[2023-05-18T06:45:36.700Z]]

[80] 
よく作り込まれてるであろう [[Google検索]]なんかでも効かないんすよね。







