aboutsummaryrefslogtreecommitdiff
path: root/node_modules/jsdom/lib/jsdom/living/window/History-impl.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/jsdom/lib/jsdom/living/window/History-impl.js')
-rw-r--r--node_modules/jsdom/lib/jsdom/living/window/History-impl.js134
1 files changed, 134 insertions, 0 deletions
diff --git a/node_modules/jsdom/lib/jsdom/living/window/History-impl.js b/node_modules/jsdom/lib/jsdom/living/window/History-impl.js
new file mode 100644
index 0000000..f8e764a
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/window/History-impl.js
@@ -0,0 +1,134 @@
+"use strict";
+const DOMException = require("domexception/webidl2js-wrapper");
+const { documentBaseURLSerialized, parseURLToResultingURLRecord } = require("../helpers/document-base-url.js");
+
+// https://html.spec.whatwg.org/#history-3
+exports.implementation = class HistoryImpl {
+ constructor(globalObject, args, privateData) {
+ this._window = privateData.window;
+ this._document = privateData.document;
+ this._actAsIfLocationReloadCalled = privateData.actAsIfLocationReloadCalled;
+ this._state = null;
+
+ this._globalObject = globalObject;
+ }
+
+ _guardAgainstInactiveDocuments() {
+ if (!this._window) {
+ throw DOMException.create(this._globalObject, [
+ "History object is associated with a document that is not fully active.",
+ "SecurityError"
+ ]);
+ }
+ }
+
+ get length() {
+ this._guardAgainstInactiveDocuments();
+
+ return this._window._sessionHistory.length;
+ }
+
+ get state() {
+ this._guardAgainstInactiveDocuments();
+
+ return this._state;
+ }
+
+ go(delta) {
+ this._guardAgainstInactiveDocuments();
+
+ if (delta === 0) {
+ // When the go(delta) method is invoked, if delta is zero, the user agent must act as
+ // if the location.reload() method was called instead.
+ this._actAsIfLocationReloadCalled();
+ } else {
+ // Otherwise, the user agent must traverse the history by a delta whose value is delta
+ this._window._sessionHistory.traverseByDelta(delta);
+ }
+ }
+
+ back() {
+ this.go(-1);
+ }
+
+ forward() {
+ this.go(+1);
+ }
+
+ pushState(data, title, url) {
+ this._sharedPushAndReplaceState(data, title, url, "pushState");
+ }
+ replaceState(data, title, url) {
+ this._sharedPushAndReplaceState(data, title, url, "replaceState");
+ }
+
+ // https://html.spec.whatwg.org/#dom-history-pushstate
+ _sharedPushAndReplaceState(data, title, url, methodName) {
+ this._guardAgainstInactiveDocuments();
+
+ // TODO structured clone data
+
+ let newURL;
+ if (url !== null) {
+ // Not implemented: use of entry settings object's API base URL. Instead we just use the document base URL. The
+ // difference matters in the case of cross-frame calls.
+ newURL = parseURLToResultingURLRecord(url, this._document);
+
+ if (newURL === null) {
+ throw DOMException.create(this._globalObject, [
+ `Could not parse url argument "${url}" to ${methodName} against base URL ` +
+ `"${documentBaseURLSerialized(this._document)}".`,
+ "SecurityError"
+ ]);
+ }
+
+ if (newURL.scheme !== this._document._URL.scheme ||
+ newURL.username !== this._document._URL.username ||
+ newURL.password !== this._document._URL.password ||
+ newURL.host !== this._document._URL.host ||
+ newURL.port !== this._document._URL.port ||
+ newURL.cannotBeABaseURL !== this._document._URL.cannotBeABaseURL) {
+ throw DOMException.create(this._globalObject, [
+ `${methodName} cannot update history to a URL which differs in components other than in ` +
+ `path, query, or fragment.`,
+ "SecurityError"
+ ]);
+ }
+
+ // Not implemented: origin check (seems to only apply to documents with weird origins, e.g. sandboxed ones)
+ } else {
+ newURL = this._window._sessionHistory.currentEntry.url;
+ }
+
+ if (methodName === "pushState") {
+ this._window._sessionHistory.removeAllEntriesAfterCurrentEntry();
+
+ this._window._sessionHistory.clearHistoryTraversalTasks();
+
+ const newEntry = {
+ document: this._document,
+ stateObject: data,
+ title,
+ url: newURL
+ };
+ this._window._sessionHistory.addEntryAfterCurrentEntry(newEntry);
+ this._window._sessionHistory.updateCurrentEntry(newEntry);
+ } else {
+ const { currentEntry } = this._window._sessionHistory;
+ currentEntry.stateObject = data;
+ currentEntry.title = title;
+ currentEntry.url = newURL;
+ }
+
+ // TODO: If the current entry in the session history represents a non-GET request
+ // (e.g. it was the result of a POST submission) then update it to instead represent
+ // a GET request.
+
+ this._document._URL = newURL;
+
+ // arguably it's a bit odd that the state and latestEntry do not belong to the SessionHistory
+ // but the spec gives them to "History" and "Document" respecively.
+ this._state = data; // TODO clone again!! O_o
+ this._document._latestEntry = this._window._sessionHistory.currentEntry;
+ }
+};