1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
let checkState = Task.async(function*(browser) {
// Go back and then forward, and make sure that the state objects received
// from the popState event are as we expect them to be.
//
// We also add a node to the document's body when after going back and make
// sure it's still there after we go forward -- this is to test that the two
// history entries correspond to the same document.
let deferred = {};
deferred.promise = new Promise(resolve => deferred.resolve = resolve);
let popStateCount = 0;
browser.addEventListener("popstate", function(aEvent) {
if (popStateCount == 0) {
popStateCount++;
ok(aEvent.state, "Event should have a state property.");
ContentTask.spawn(browser, null, function() {
is(content.testState, "foo",
"testState after going back");
is(JSON.stringify(content.history.state), JSON.stringify({obj1:1}),
"first popstate object.");
// Add a node with id "new-elem" to the document.
let doc = content.document;
ok(!doc.getElementById("new-elem"),
"doc shouldn't contain new-elem before we add it.");
let elem = doc.createElement("div");
elem.id = "new-elem";
doc.body.appendChild(elem);
}).then(() => {
browser.goForward();
});
} else if (popStateCount == 1) {
popStateCount++;
// When content fires a PopStateEvent and we observe it from a chrome event
// listener (as we do here, and, thankfully, nowhere else in the tree), the
// state object will be a cross-compartment wrapper to an object that was
// deserialized in the content scope. And in this case, since RegExps are
// not currently Xrayable (see bug 1014991), trying to pull |obj3| (a RegExp)
// off of an Xrayed Object won't work. So we need to waive.
ContentTask.spawn(browser, aEvent.state, function(state) {
Assert.equal(Cu.waiveXrays(state).obj3.toString(),
"/^a$/", "second popstate object.");
// Make sure that the new-elem node is present in the document. If it's
// not, then this history entry has a different doc identifier than the
// previous entry, which is bad.
let doc = content.document;
let newElem = doc.getElementById("new-elem");
ok(newElem, "doc should contain new-elem.");
newElem.parentNode.removeChild(newElem);
ok(!doc.getElementById("new-elem"), "new-elem should be removed.");
}).then(() => {
browser.removeEventListener("popstate", arguments.callee, true);
deferred.resolve();
});
}
});
// Set some state in the page's window. When we go back(), the page should
// be retrieved from bfcache, and this state should still be there.
yield ContentTask.spawn(browser, null, function() {
content.testState = "foo";
});
// Now go back. This should trigger the popstate event handler above.
browser.goBack();
yield deferred.promise;
});
add_task(function* test() {
// Tests session restore functionality of history.pushState and
// history.replaceState(). (Bug 500328)
// We open a new blank window, let it load, and then load in
// http://example.com. We need to load the blank window first, otherwise the
// docshell gets confused and doesn't have a current history entry.
let state;
yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank" }, function* (browser) {
BrowserTestUtils.loadURI(browser, "http://example.com");
yield BrowserTestUtils.browserLoaded(browser);
// After these push/replaceState calls, the window should have three
// history entries:
// testURL (state object: null) <-- oldest
// testURL (state object: {obj1:1})
// testURL?page2 (state object: {obj3:/^a$/}) <-- newest
function contentTest() {
let history = content.window.history;
history.pushState({obj1:1}, "title-obj1");
history.pushState({obj2:2}, "title-obj2", "?page2");
history.replaceState({obj3:/^a$/}, "title-obj3");
}
yield ContentTask.spawn(browser, null, contentTest);
yield TabStateFlusher.flush(browser);
state = ss.getTabState(gBrowser.getTabForBrowser(browser));
});
// Restore the state into a new tab. Things don't work well when we
// restore into the old tab, but that's not a real use case anyway.
yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank" }, function* (browser) {
let tab2 = gBrowser.getTabForBrowser(browser);
let tabRestoredPromise = promiseTabRestored(tab2);
ss.setTabState(tab2, state, true);
// Run checkState() once the tab finishes loading its restored state.
yield tabRestoredPromise;
yield checkState(browser);
});
});
|