* 仕様書

[REFS[
- [15] [CITE@en-GB-x-hixie[HTML Standard]] ([TIME[2015-04-25 04:40:19 +09:00]] 版) <https://html.spec.whatwg.org/#state-object>
- [21] [CITE@en-GB-x-hixie[HTML Standard]] ([TIME[2015-04-25 04:40:19 +09:00]] 版) <https://html.spec.whatwg.org/#dom-history-state>
- [28] [CITE@en-GB-x-hixie[HTML Standard]] ([TIME[2015-04-25 04:40:19 +09:00]] 版) <https://html.spec.whatwg.org/#dom-history-pushstate>
- [34] [CITE@en-GB-x-hixie[HTML Standard]] ([TIME[2015-04-25 04:40:19 +09:00]] 版) <https://html.spec.whatwg.org/#history-notes>
- [41] [CITE@en-GB-x-hixie[HTML Standard]] ([TIME[2015-04-25 04:40:19 +09:00]] 版) <https://html.spec.whatwg.org/#popstateevent>
]REFS]

* 直列化済み状態

[16] [DFN[[RUBYB[直列化済み状態]@en[serialized state]]]]
(旧[DFN[[RUBYB[状態オブジェクト]@en[state object]]]])
は、[[利用者インターフェイス]]の状態を表す[[オブジェクト]]です [SRC[>>15]]。

[17] [[状態オブジェクト]]は、[[セッション履歴エントリー]]中に設定されることがあります。

[18] [[著者]]は [CODE(DOMm)@en[[[pushState]]]] や [CODE(DOMm)@en[[[replaceState]]]]
により[[状態オブジェクト]]を指定できます。

[19] [[著者]]は [CODE(DOMe)@en[[[popstate]]]] [[イベント]]により[[状態オブジェクト]]を受け取ることができます。

* [CODE(JS)@en[history.state]] 属性 (DOM)

[22] [CODE(DOMi)@en[[[History]]]] [[インターフェイス]]の
[DFN[[CODE(DOMa)@en[[[state]]]]]] [[IDL属性]]の[[取得器]]は、
次のようにしなければ[MUST[なりません]] [SRC[>>21]]。

[FIG(steps)[
= [25] [[文脈オブジェクト]]の[F[文書]]の[F[完全に活性]]が[[偽]]なら、
== [23] [CODE(DOMe)@en[[[SecurityError]]]] [[例外]]を[[投げ]]、ここで停止します。
= [26] [[文脈オブジェクト]]の [F[[CODE(DOMa)@en[state][history.state]]]]
を返します。
]FIG]

[24] [CODE(DOMi)@en[History]] [[オブジェクト]]は、
[DFN[[F[[CODE(DOMa)@en[state][history.state]]]]]] を持ちます。
初期値は [CODE[null]] です [SRC[>>21]]。

;; [27] [[履歴の探索]]と [CODE(DOMm)@en[pushState]]/[CODE(DOMm)@en[replaceState]]
で値が設定されます。

* [CODE(DOMm)@en[pushState]] メソッド、[CODE(DOMm)@en[replaceState]] メソッド

[29] [CODE(DOMi)@en[[[History]]]] [[インターフェイス]]の
[DFN[[CODE(DOMm)@en[[[pushState]]]]]] [[メソッド]]と
[DFN[[CODE(DOMm)@en[[[replaceState]]]]]] [[メソッド]]は、
次のようにしなければ[MUST[なりません]] [SRC[>>28]]。

[FIG(steps)[
= [57] [VAR[データ]]を、第1引数を必須の [CODE(IDL)@en[any]] として解釈した結果に設定します。
= [58] [VAR[題名]]を、第2引数を必須の [CODE(IDL)@en[USVString]] として解釈した結果に設定します。
= [59] [VAR[URL]] を、第3引数を省略可能な [CODE(IDL)@en[[[DOMString]][[?]]]]
として解釈した結果に設定します。省略時の[[既定値]]は、 [[null]] とします。
= [60] [VAR[文書]]を、[[文脈オブジェクト]]の[F[文書]]に設定します。
= [83] [VAR[文書]]が[F[完全に活性]]でなければ、
== [61] [CODE(JS)@en[[[SecurityError]]]] [[例外]]を投げ、ここで停止します。
= [62] [[利用者エージェント]]の判断で、ここで停止して構いません。
[EG[
[63] [[利用者動作]]によって起動された場合以外の時や、
短時間で連続して実行された時、
[VAR[データ]]が巨大すぎる場合などに、
本[[メソッド]]呼び出しを無視できます。
]EG]
= [64] [VAR[直列化済みデータ]]を、
[CODE[[[StructuredSerializeForStorage]] ([VAR[データ]], [[文脈オブジェクト]]の[F[Realm]])]]
の結果に設定します。[[例外]]が投げられたら、それを[[再度投げ]]、ここで停止します。
= [70] [VAR[現在のURL]]を、[[文脈オブジェクト]]の[F[文書]]の[F[URL][文書の番地]]に設定します。
= [65] [VAR[URL]] が [[null]] 以外なら、
== [67] [VAR[新URL]] を、[VAR[URL]]を[VAR[文書]]に対して[[構文解析][URLの構文解析]]した結果に設定します。
== [66] 
[FIG(list)[
- [68] [VAR[新URL]]が[[失敗]]
- [50] [VAR[新URL]]の[[URL記録]]と[VAR[現在のURL]]を比較した時、
[F[path]]、[F[query]]、[F[素片]]を除く部品が異なる
- [69] [VAR[新URL]]の[F[起源][URLの起源]]と[VAR[文書]]の[F[起源][文書の起源]]が[[同じ起源]]では''ない''場合で、
[VAR[新URL]]の[[URL記録]]と[VAR[現在のURL]]を比較した時、
[F[path]]と[F[query]]の一方または両方が異なる
]FIG]
... のいずれかの場合、
=== [71] [CODE(DOMe)@en[SecurityError]] [[例外]]を[[投げ]]、ここで停止します。
= [51] それ以外の場合、
== [52] [VAR[新URL]]を、[VAR[文書]]の[F[閲覧文脈]]の[F[セッション履歴]]の[F[現在エントリー]]の [F[URL]] に設定します。
= [37] 
[[URLおよび履歴更新手順群]]を実行します。
[FIG(list members)[

: [VAR[文書]] : [VAR[文書]]
: [VAR[新URL]] : [VAR[新URL]]
: [VAR[直列化済みデータ]] :  [VAR[直列化済みデータ]]
: [VAR[題名]] : [VAR[題名]]
: [VAR[状態pushフラグ]] : 本メソッドは [CODE[pushState]] であるか否か

]FIG]
= [78] 
[[文脈オブジェクト]]の [F[[CODE(DOMa)@en[state][history.state]]]] を、
[CODE[[[StructuredDeserialize]] ([VAR[直列化済みデータ]], [[文脈オブジェクト]]の[F[Realm]])]]
の結果に設定します。
[[例外]]が[[投げ]]られたら、
[[文脈オブジェクト]]の [F[[CODE(DOMa)@en[state][history.state]]]] を、
[CODE[null]] に設定します。
= [79] [[現在エントリー]]の[[文書]]の[[最新エントリー]]は、[[現在エントリー]]とします。
= [80] [VAR[現在のURL]]と[VAR[新しいURL]]が異なるなら、
== [81] [CODE[webNavigation]] API の [CODE[onHistoryStateUpdated]] を呼び出します。
[VAR[[[遷移型]]]]と[VAR[[[遷移修飾子]]群]]は適宜設定します。現在時刻も引き渡します。
[SRC[仕様書なし]]

]FIG]


;; [53] >>50 が[[同じ起源]]かどうかの判断ではないので、
[[userinfo]] の有無や値も (あれば) 判定基準となります。

[30] 両[[メソッド]]は[[文書の番地]]を変更することができるものですが、
[[文書の起源]]や[[実効スクリプト起源]]や[[文書のドメイン]]を変更することはできません。
この意味で[[同一起源ポリシー]]を緩和するものではありません。更に、
[CODE(JS)@en[[[document.domain]]]] を使っても変更可能な範囲を拡大することはできません。

;; [31] [[文書の起源]]ではなく[[文書の番地]]の[[起源]]が問題となりますから、
[[文書の起源]]と[[同じ起源]]であっても [CODE(URI)@en[[[about:blank]]]] や [CODE(URI)@en[[[data:]]]]
[[URL]] が[[文書の番地]]となっている時、 [CODE(URI)@en[[[http:]]]] や
[CODE(URI)@en[[[https:]]]] の [[URL]] に変更することはできません。
この意味で[[同一起源ポリシー]]より制限が厳しいと言うこともできますが、
両メソッドの趣旨が[[文書の番地]]を変更するものですから、当然の制約でもあります。

[32] [[砂箱]]によって異なる[[起源]]で動作する[[スクリプト]]は[[素片識別子]]以外を変更することもできません。

;; [33] >>31 と >>32 は、[[文書の起源]]と[[文書の番地]]の[[起源]]の両方と[[同じ起源]]の
[[URL]] には変更できる、と言い換えられます。

[35] [[利用者エージェント]]は、 [CODE(DOMm)@en[[[pushState]]]] の濫用を防ぐため、
[[タイマー]]からの呼び出しや[[利用者]]の明確な操作によらない[[イベントリスナー]]からの呼び出し、短時間での連続呼び出しを無視したほうが良いかもしれません
[SRC[>>34]]。



* [CODE(DOMe)@en[popstate]] イベント

[40] [DFN[[CODE(DOMe)@en[[[popstate]]]]]] [[イベント]]は、[[履歴の探索]]により
[CODE(JS)@en[[[history.state]]]] の値が変更された時に[[発火]]されます。

[42] [DFN[[CODE(DOMi)@en[[[PopStateEvent]]]]]] [[インターフェイス]] [SRC[>>41]] は、
[CODE(DOMe)@en[[[popstate]]]] [[イベント]]と共に用いられます。
[[文書環境]][DEL[と[[ワーカー環境]]]]に[[晒され]]ています [SRC[>>41]]。

[44] [CODE(DOMi)@en[[[PopStateEvent]]]] [[インターフェイス]]は、
他の [CODE(DOMi)@en[[[Event]]]] [[インターフェイス]]同様の[[コンストラクター]]を持ちます
[SRC[>>41]]。
初期化のための辞書は [DFN[[CODE(DOMi)@en[[[PopStateEventInit]]]]]] です [SRC[>>41]]。

[43] [CODE(DOMi)@en[[[PopStateEvent]]]] [[インターフェイス]]は、
[CODE(DOMi)@en[[[Event]]]] [[インターフェイス]]を[[継承]]しています [SRC[>>41]]。
[CODE(DOMi)@en[[[PopStateEventInit]]]] [[辞書]]は、
[CODE(DOMi)@en[[[EventInit]]]] [[辞書]]を[[継承]]しています [SRC[>>41]]。

[45] [CODE(DOMi)@en[[[PopStateEvent]]]] [[インターフェイス]]および
[CODE(DOMi)@en[[[PopStateEventInit]]]] [[辞書]]は、[CODE(IDL)@en[[[any]]]]
型の読み取り専用[[IDL属性]] [DFN[[CODE(DOMa)@en[[[state]]]]]]を持ちます [SRC[>>41]]。
初期値は [[null]] です [SRC[>>41]]。[[履歴の探索]]で [CODE(DOMe)@en[[[popstate]]]]
[[イベント]]が[[発火]]される時は、[[状態オブジェクト]] (なければ [[null]])
が設定されます。

* 利用者インターフェイス

[36] [[履歴]]を参照。


* 乱用


[48] 悪意のある[[著者]]が、
[[利用者]]の正常な[[履歴]]操作を妨害するために
[CODE(DOMm)@en[[[pushState]]]]/[CODE(DOMe)@en[[[popstate]]]]
を使うことがあります [SRC[>>47]]。
例えば[[戻る]]ボタンを無効化するための手段として、
あたかも正当な技法のように紹介する悪い人がいます。

[96] 
[[Webブラウザー]]はそうした悪用を防ぐための対策を講じることができ (>>35)、
[[利用者]]を保護することが期待されています。
最近の
[[Webブラウザー]]の[[履歴]]の[[インターフェイス]]は、
複数まとめて戻るような機能が提供されていたりもします。
[SEE[ [[履歴]] ]]


[REFS[
- [47] [CITE[HTML5 history apiで戻るボタン無効化 - Qiita]] ([TIME[2016-01-07 11:46:33 +09:00]] 版) <http://qiita.com/maruo327/items/a0502c8b508fed4ec169>
]REFS]

[97] 
[CITE@ja[ブラウザの履歴を操作して「戻る」ボタンで広告を出すやつについて – コーヒーサーバは香炉である]], [TIME[2024-08-15T01:24:57.000Z]] <https://blog.maripo.org/2024/08/history-api-abuse/>

[98] 
[CITE@ja[XユーザーのP-Chanさん: 「ブラウザバック広告実装したことあるけど、技術的に可能なので、断るに断れなかった。 「Evil ですよ!? Evil ですよ!?」と言いながら実装した。 結果、めっちゃ効果あって、みんなやる理由が理解できた。 ブラウザ側で完全に技術的に不可能にして欲しい。」 / X]], [TIME[午後8:06 · 2024年8月15日][2024-08-15T11:06:17.000Z]], [TIME[2024-08-16T01:41:30.000Z]] <https://x.com/p1ch_jp/status/1824039906957737987>

[99] [[技術者倫理]]! っていいたいところだけど会社員や下請事業者としての立場もあるし難しいのう...
「みんなやってる」と断りにくさが急激に上がるよねえ。最初にやった奴は犯罪レベルだろ。

[100] なぜこういうときに[[神奈川県警]]は仕事しないのか。

-*-*-

[86] 
ページを[[スクロール]]していくだけで、
勝手にその位置を表す [[URL]] (例えば記事に投稿されたコメントの [[URL]]
や、章節を表す [[URL]]、全体の [[URL]] と[[パス]]や[[素片識別子]]が違うもの)
に [CODE[replaceState]] で置き換えていくサイトがあります。

この機能は一見便利そうですが、
[[URL]]
を[[共有]]しようとする読者が思っていもいない [[URL]]
を渡してしまうので、
不便です。やめてほしいです。




* 歴史

[1]
[CITE[History management for "Ajax" applications - Anne’s Weblog]] ([CODE[2007-08-10 19:42:23 +09:00]] 版) <http://annevankesteren.nl/2007/08/ajax-history>
([[名無しさん]] [WEAK[2007-08-10 16:32:50 +00:00]])


[2]
[CITE[Re: several messages about pushState() and related ideas]] ([[Ian Hickson <ian@...>]] 著, [TIME[2007-08-10 00:00:31 +09:00]] 版) <http://permalink.gmane.org/gmane.org.w3c.whatwg.discuss/11643>

[3] [CITE@en[[[Web Applications 1.0]] r5964 12277]]
( ([TIME[2011-03-25 09:55:00 +09:00]] 版))
<http://html5.org/tools/web-apps-tracker?from=5963&to=5964>


[4] [CITE@en-us[Manipulating the browser history - [[MDC]]]]
([TIME[2010-06-01 08:09:18 +09:00]] 版)
<https://developer.mozilla.org/en/DOM/Manipulating_the_browser_history>


[5] [CITE[''''''[''''''whatwg'''''']'''''' Asynchronous history navigation and some inconsistencies]]
( ([TIME[2012-06-29 17:49:57 +09:00]] 版))
<http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2012-June/036505.html>

[6] [CITE[IRC logs: freenode / #whatwg / 20120629]]
( ([TIME[2012-07-15 16:06:48 +09:00]] 版))
<http://krijnhoetmer.nl/irc-logs/whatwg/20120629>

[7] [CITE@en[Interview with Ian Hickson, HTML editor | HTML5 Doctor]]
( ([TIME[2013-01-13 18:27:28 +09:00]] 版))
<http://html5doctor.com/interview-with-ian-hickson-html-editor/>

[8] [CITE[Issue 45361 - chromium - history.pushState does not update http referrer - An open-source project to help move the web forward. - Google Project Hosting]]
( ([TIME[2013-06-06 03:12:16 +09:00]] 版))
<https://code.google.com/p/chromium/issues/detail?id=45361>

[9] [CITE[Bug 40027 – location.href and outgoing referrer not updated properly by pushState/replaceState]]
( ([TIME[2013-06-06 03:13:42 +09:00]] 版))
<https://bugs.webkit.org/show_bug.cgi?id=40027>

[10] [CITE@en[622088 – XHR referer header is not modified with history.pushState]]
( ([TIME[2013-06-06 03:14:48 +09:00]] 版))
<https://bugzilla.mozilla.org/show_bug.cgi?id=622088>

[11] [CITE@en[History (Windows)]]
( ([TIME[2013-11-13 13:52:56 +09:00]] 版))
<http://msdn.microsoft.com/en-us/library/ie/hh920758(v=vs.85).aspx>

[12] [CITE@en[''''''[''''''whatwg'''''']'''''' Proposal: Allow disabling of default scroll restoration 	behavior]]
([[Majid Valipour]] 著, [TIME[2015-03-20 02:31:31 +09:00]] 版)
<https://lists.w3.org/Archives/Public/public-whatwg-archive/2015Mar/0070.html>

[13] [CITE[Customizing Scroll Restoration - Google ドキュメント]]
([TIME[2015-03-20 11:49:21 +09:00]] 版)
<https://docs.google.com/a/chromium.org/document/d/1Tiu8PjvBtNOAgeh6yrs7bOrXxQcavQLiNtRJ_ToLlVM/edit?pli=1#heading=h.j26hb4a80gnf>

[14] [CITE@en[Re: ''''''[''''''whatwg'''''']'''''' Proposal: Allow disabling of default scroll  restoration behavior]]
([[Ian Hickson]] 著, [TIME[2015-04-10 04:05:19 +09:00]] 版)
<https://lists.w3.org/Archives/Public/public-whatwg-archive/2015Apr/0075.html>

[20] [CITE[defunkt/jquery-pjax]]
( ([TIME[2012-03-17 14:22:30 +09:00]] 版))
<https://github.com/defunkt/jquery-pjax>

[38] [CITE[IRC logs: freenode / #whatwg / 20090818]]
([TIME[2009-10-12 16:14:46 +09:00]] 版)
<http://krijnhoetmer.nl/irc-logs/whatwg/20090818#l-118>

[39] [CITE[''''''[''''''whatwg'''''']'''''' history.popstate in Firefox4]]
( ([TIME[2012-01-31 08:28:43 +09:00]] 版))
<http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2012-January/034570.html>

[46] [CITE@en[Add scroll restoration preference to history traversal · whatwg/html@dd6a34e]] ([TIME[2015-11-17 21:03:22 +09:00]] 版) <https://github.com/whatwg/html/commit/dd6a34ec3c191ee1968a0fc8d174b4ce3b615916>


[49] [CITE@en[Fix #521: all URLs can be relative · whatwg/html@e99f0bd]]
([TIME[2016-01-19 10:45:36 +09:00]] 版)
<https://github.com/whatwg/html/commit/e99f0bd112e0216698bf9397b64b3f8064995756>

[54] [CITE@en[Write structured clone algorithm in terms of ECMAScript · whatwg/html@bfb960c]]
([TIME[2016-03-02 16:40:50 +09:00]] 版)
<https://github.com/whatwg/html/commit/bfb960c938580c95e77365e614218b952f96375b>

[55] [CITE@en[Editorial: define Event attribute defaults through IDL · whatwg/html@0be0229]]
([TIME[2016-03-23 21:15:19 +09:00]] 版)
<https://github.com/whatwg/html/commit/0be02299f128cbb7c65a7ce259fdd838fd44a4b5>

[56] [CITE@en[Make it explicit that user agents can ignore replaceState/pushState · whatwg/html@38139b4]]
([TIME[2016-04-07 17:55:02 +09:00]] 版)
<https://github.com/whatwg/html/commit/38139b40d9b808c9bc8646df74c5c075ad7584fe>

[72] [CITE[チェンジセット 198687 – WebKit]]
([TIME[2016-04-17 13:52:37 +09:00]] 版)
<https://trac.webkit.org/changeset/198687/>

[73] [CITE@en[Bug 155901 – Soften push/replaceState frequency restrictions]]
([TIME[2016-04-17 13:53:01 +09:00]] 版)
<https://bugs.webkit.org/show_bug.cgi?id=155901>

[74] [CITE@en[Rename document's address to URL]]
( ([[annevk]]著, [TIME[2016-05-06 02:06:55 +09:00]]))
<https://github.com/whatwg/html/commit/16dd60cfb1cbd4ea3dcacc91f58b368b021ded33>

[75] [CITE@en[Use USVString for all URLs]]
( ([[domenic]]著, [TIME[2016-05-20 22:02:29 +09:00]]))
<https://github.com/whatwg/html/commit/018b983b77b2cd908f6d00100e7e0abe893dd2c3>

[76] [CITE@en['''['''giow''']''' (2) Make sure to fire the relevant events on the Window object…]]
( ([[Hixie]]著, [TIME[2009-01-21 20:34:10 +09:00]]))
<https://github.com/whatwg/html/commit/afdba55e4ae45f4c9d42c00902da1a0df7381aad>

[82] [CITE@en[Use relevant settings object for pushState/replaceState URL parsing]]
([[domenic]]著, [TIME[2016-07-07 01:48:15 +09:00]])
<https://github.com/whatwg/html/commit/b568cf93ac374c440e29e01a2619365dde7c45c8>

[84] [CITE@en[Use the associated document for pushState/replaceState's origin check]]
([[domenic]]著, [TIME[2016-07-13 06:50:14 +09:00]])
<https://github.com/whatwg/html/commit/60f84adcae2d252fc63afa6b65686a4590d28734>

[85] [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>

[88] [CITE@en[Breaking: refactor structured clone into serialize/deserialize]]
([[domenic]]著, [TIME[2017-03-21 06:09:33 +09:00]])
<https://github.com/whatwg/html/commit/97d644c97335956610a31e8ad98d1a388c063e84>

[89] [CITE@en[Serializing and deserializing SharedArrayBuffer]]
([[annevk]]著, [TIME[2017-04-28 16:38:26 +09:00]])
<https://github.com/whatwg/html/commit/b3f8779c76de7718bc970fdbccda18a5164d2f98>

[90] [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>

[91] [CITE@en[Make popstate and hashchange events not bubble]]
([[foolip]]著, [TIME[2018-06-07 21:23:33 +09:00]])
<https://github.com/whatwg/html/commit/85e1e724cf09574dba47d5eae689bdb5b7fd6502>

[92] [CITE@en[Let the popstate and hashchange events not bubble by foolip · Pull Request #3737 · whatwg/html]]
([TIME[2018-06-08 14:37:55 +09:00]])
<https://github.com/whatwg/html/pull/3737>

[93] [CITE@en[document.open() can make a document's URL go out of sync with its history entry · Issue #3885 · whatwg/html]]
([TIME[2018-09-05 22:41:47 +09:00]])
<https://github.com/whatwg/html/issues/3885>

[94] [CITE@en[document.open() simplifications, part 2 by TimothyGu · Pull Request #3946 · whatwg/html]]
([TIME[2018-09-05 22:43:43 +09:00]])
<https://github.com/whatwg/html/pull/3946>

[95] [CITE@en[Editorial: Factor out history.push/replaceState() steps]]
([[TimothyGu]]著, [TIME[2018-08-18 08:04:45 +09:00]])
<https://github.com/whatwg/html/commit/f42dcf0bf2ebf86bc5ca79aecea098a8fed6cf3e>

[77] [CITE@en[Editorial: Factor out history.push/replaceState() steps]]
([[TimothyGu]]著, [TIME[2018-08-18 08:04:45 +09:00]])
<https://github.com/whatwg/html/commit/f42dcf0bf2ebf86bc5ca79aecea098a8fed6cf3e>

[87] 
[CITE@ja[リンクをクリックするだけでiOSデバイスが再起動してしまうWebサイト | スラド アップル]], [TIME[2024-01-22T13:45:44.000Z]] <https://apple.srad.jp/story/16/01/29/0549256/>
