summaryrefslogtreecommitdiffstats
path: root/comm/suite/components/tests
diff options
context:
space:
mode:
Diffstat (limited to 'comm/suite/components/tests')
-rw-r--r--comm/suite/components/tests/browser/browser.ini72
-rw-r--r--comm/suite/components/tests/browser/browser_339445.js34
-rw-r--r--comm/suite/components/tests/browser/browser_339445_sample.html17
-rw-r--r--comm/suite/components/tests/browser/browser_345898.js45
-rw-r--r--comm/suite/components/tests/browser/browser_346337.js122
-rw-r--r--comm/suite/components/tests/browser/browser_346337_sample.html37
-rw-r--r--comm/suite/components/tests/browser/browser_350525.js100
-rw-r--r--comm/suite/components/tests/browser/browser_354894.js459
-rw-r--r--comm/suite/components/tests/browser/browser_367052.js38
-rw-r--r--comm/suite/components/tests/browser/browser_393716.js74
-rw-r--r--comm/suite/components/tests/browser/browser_394759_basic.js77
-rw-r--r--comm/suite/components/tests/browser/browser_394759_behavior.js66
-rw-r--r--comm/suite/components/tests/browser/browser_408470.js57
-rw-r--r--comm/suite/components/tests/browser/browser_408470_sample.html19
-rw-r--r--comm/suite/components/tests/browser/browser_423132.js86
-rw-r--r--comm/suite/components/tests/browser/browser_423132_sample.html13
-rw-r--r--comm/suite/components/tests/browser/browser_447951.js50
-rw-r--r--comm/suite/components/tests/browser/browser_447951_sample.html4
-rw-r--r--comm/suite/components/tests/browser/browser_448741.js62
-rw-r--r--comm/suite/components/tests/browser/browser_454908.js52
-rw-r--r--comm/suite/components/tests/browser/browser_454908_sample.html8
-rw-r--r--comm/suite/components/tests/browser/browser_456342.js47
-rw-r--r--comm/suite/components/tests/browser/browser_456342_sample.xhtml28
-rw-r--r--comm/suite/components/tests/browser/browser_461634.js88
-rw-r--r--comm/suite/components/tests/browser/browser_463206.js64
-rw-r--r--comm/suite/components/tests/browser/browser_463206_sample.html10
-rw-r--r--comm/suite/components/tests/browser/browser_465215.js39
-rw-r--r--comm/suite/components/tests/browser/browser_465223.js60
-rw-r--r--comm/suite/components/tests/browser/browser_466937.js43
-rw-r--r--comm/suite/components/tests/browser/browser_466937_sample.html21
-rw-r--r--comm/suite/components/tests/browser/browser_477657.js85
-rw-r--r--comm/suite/components/tests/browser/browser_480893.js58
-rw-r--r--comm/suite/components/tests/browser/browser_483330.js37
-rw-r--r--comm/suite/components/tests/browser/browser_485482.js36
-rw-r--r--comm/suite/components/tests/browser/browser_485482_sample.html12
-rw-r--r--comm/suite/components/tests/browser/browser_490040.js139
-rw-r--r--comm/suite/components/tests/browser/browser_491168.js64
-rw-r--r--comm/suite/components/tests/browser/browser_491577.js119
-rw-r--r--comm/suite/components/tests/browser/browser_493467.js48
-rw-r--r--comm/suite/components/tests/browser/browser_500328.js115
-rw-r--r--comm/suite/components/tests/browser/browser_514751.js59
-rw-r--r--comm/suite/components/tests/browser/browser_522545.js280
-rw-r--r--comm/suite/components/tests/browser/browser_524745.js59
-rw-r--r--comm/suite/components/tests/browser/browser_526613.js71
-rw-r--r--comm/suite/components/tests/browser/browser_528776.js29
-rw-r--r--comm/suite/components/tests/browser/browser_581937.js38
-rw-r--r--comm/suite/components/tests/browser/browser_586068-cascaded_restore.js730
-rw-r--r--comm/suite/components/tests/browser/browser_597315.js64
-rwxr-xr-xcomm/suite/components/tests/browser/browser_597315_a.html5
-rwxr-xr-xcomm/suite/components/tests/browser/browser_597315_b.html10
-rwxr-xr-xcomm/suite/components/tests/browser/browser_597315_c.html5
-rwxr-xr-xcomm/suite/components/tests/browser/browser_597315_c1.html5
-rwxr-xr-xcomm/suite/components/tests/browser/browser_597315_c2.html5
-rw-r--r--comm/suite/components/tests/browser/browser_597315_index.html10
-rw-r--r--comm/suite/components/tests/browser/browser_607016.js120
-rw-r--r--comm/suite/components/tests/browser/browser_615394-SSWindowState_events.js362
-rw-r--r--comm/suite/components/tests/browser/browser_625257.js87
-rw-r--r--comm/suite/components/tests/browser/browser_636279.js102
-rw-r--r--comm/suite/components/tests/browser/browser_637020.js65
-rw-r--r--comm/suite/components/tests/browser/browser_637020_slow.sjs18
-rw-r--r--comm/suite/components/tests/browser/browser_645428.js22
-rw-r--r--comm/suite/components/tests/browser/browser_665702-state_session.js25
-rw-r--r--comm/suite/components/tests/browser/browser_687710.js44
-rw-r--r--comm/suite/components/tests/browser/browser_687710_2.js64
-rw-r--r--comm/suite/components/tests/browser/browser_694378.js33
-rw-r--r--comm/suite/components/tests/browser/browser_bug431826.js42
-rw-r--r--comm/suite/components/tests/browser/browser_isempty.js28
-rw-r--r--comm/suite/components/tests/browser/browser_markPageAsFollowedLink.js87
-rw-r--r--comm/suite/components/tests/browser/frameLeft.html8
-rw-r--r--comm/suite/components/tests/browser/frameRight.html8
-rw-r--r--comm/suite/components/tests/browser/framedPage.html9
-rw-r--r--comm/suite/components/tests/browser/head.js151
-rw-r--r--comm/suite/components/tests/chrome/chrome.ini4
-rw-r--r--comm/suite/components/tests/chrome/test_idcheck.xul302
74 files changed, 5556 insertions, 0 deletions
diff --git a/comm/suite/components/tests/browser/browser.ini b/comm/suite/components/tests/browser/browser.ini
new file mode 100644
index 0000000000..96c17932c0
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser.ini
@@ -0,0 +1,72 @@
+[DEFAULT]
+support-files = head.js
+
+[browser_339445.js]
+support-files = browser_339445_sample.html
+[browser_345898.js]
+[browser_346337.js]
+support-files = browser_346337_sample.html
+[browser_350525.js]
+[browser_354894.js]
+[browser_367052.js]
+[browser_393716.js]
+[browser_394759_basic.js]
+[browser_394759_behavior.js]
+[browser_408470.js]
+support-files = browser_408470_sample.html
+[browser_423132.js]
+support-files = browser_423132_sample.html
+[browser_447951.js]
+support-files = browser_447951_sample.html
+[browser_448741.js]
+[browser_454908.js]
+support-files = browser_454908_sample.html
+[browser_456342.js]
+support-files = browser_456342_sample.xhtml
+[browser_461634.js]
+[browser_463206.js]
+support-files = browser_463206_sample.html
+[browser_465215.js]
+[browser_465223.js]
+[browser_466937.js]
+support-files = browser_466937_sample.html
+[browser_477657.js]
+[browser_480893.js]
+[browser_483330.js]
+[browser_485482.js]
+support-files = browser_485482_sample.html
+[browser_490040.js]
+[browser_491168.js]
+[browser_491577.js]
+[browser_493467.js]
+[browser_500328.js]
+[browser_514751.js]
+[browser_522545.js]
+[browser_524745.js]
+[browser_526613.js]
+[browser_528776.js]
+[browser_581937.js]
+[browser_586068-cascaded_restore.js]
+[browser_597315.js]
+support-files =
+ browser_597315_index.html
+ browser_597315_a.html
+ browser_597315_b.html
+ browser_597315_c.html
+ browser_597315_c1.html
+ browser_597315_c2.html
+[browser_607016.js]
+[browser_615394-SSWindowState_events.js]
+[browser_625257.js]
+[browser_636279.js]
+[browser_637020.js]
+support-files = browser_637020_slow.sjs
+[browser_645428.js]
+[browser_665702-state_session.js]
+[browser_687710.js]
+[browser_687710_2.js]
+[browser_694378.js]
+[browser_bug431826.js]
+[browser_isempty.js]
+[browser_markPageAsFollowedLink.js]
+support-files = framedPage.html frameLeft.html frameRight.html
diff --git a/comm/suite/components/tests/browser/browser_339445.js b/comm/suite/components/tests/browser/browser_339445.js
new file mode 100644
index 0000000000..fb41aebb24
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_339445.js
@@ -0,0 +1,34 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 339445 **/
+
+ waitForExplicitFinish();
+
+ let testURL = "http://mochi.test:8888/browser/" +
+ "suite/common/tests/browser/browser_339445_sample.html";
+
+ let tab = getBrowser().addTab(testURL);
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", testTabLBLoad, true);
+ let doc = tab.linkedBrowser.contentDocument;
+ is(doc.getElementById("storageTestItem").textContent, "PENDING",
+ "sessionStorage value has been set");
+
+ let tab2 = ss.duplicateTab(window,tab);
+ tab2.linkedBrowser.addEventListener("load", function testTab2LBLoad(aEvent) {
+ this.removeEventListener("load", testTab2LBLoad, true);
+ let doc2 = tab2.linkedBrowser.contentDocument;
+ is(doc2.getElementById("storageTestItem").textContent, "SUCCESS",
+ "sessionStorage value has been duplicated");
+
+ // clean up
+ getBrowser().removeTab(tab2);
+ getBrowser().removeTab(tab);
+
+ finish();
+ }, true);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_339445_sample.html b/comm/suite/components/tests/browser/browser_339445_sample.html
new file mode 100644
index 0000000000..1fd7d5f032
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_339445_sample.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<title>Test for bug 339445</title>
+
+storageTestItem = <span id="storageTestItem">FAIL</span>
+
+<!--
+ storageTestItem's textContent will be one of the following:
+ * FAIL : sessionStorage wasn't available
+ * PENDING : the test value has been initialized on first load
+ * SUCCESS : the test value was correctly retrieved
+-->
+
+<script>
+ document.getElementById("storageTestItem").textContent =
+ sessionStorage["storageTestItem"] || "PENDING";
+ sessionStorage["storageTestItem"] = "SUCCESS";
+</script>
diff --git a/comm/suite/components/tests/browser/browser_345898.js b/comm/suite/components/tests/browser/browser_345898.js
new file mode 100644
index 0000000000..7f8c26f1a6
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_345898.js
@@ -0,0 +1,45 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 345898 **/
+
+ function test(aLambda) {
+ try {
+ aLambda();
+ return false;
+ }
+ catch (ex) {
+ return ex.name == "NS_ERROR_ILLEGAL_VALUE";
+ }
+ }
+
+ // all of the following calls with illegal arguments should throw NS_ERROR_ILLEGAL_VALUE
+ ok(test(() => ss.getWindowState({})),
+ "Invalid window for getWindowState throws");
+ ok(test(() => ss.setWindowState({}, "", false)),
+ "Invalid window for setWindowState throws");
+ ok(test(() => ss.getTabState({})),
+ "Invalid tab for getTabState throws");
+ ok(test(() => ss.setTabState({}, "{}")),
+ "Invalid tab state for setTabState throws");
+ ok(test(() => ss.setTabState({}, '{ "entries": [] }')),
+ "Invalid tab for setTabState throws");
+ ok(test(() => ss.duplicateTab({}, {})),
+ "Invalid tab for duplicateTab throws");
+ ok(test(() => ss.duplicateTab({}, getBrowser().selectedTab)),
+ "Invalid window for duplicateTab throws");
+ ok(test(() => ss.getClosedTabData({})),
+ "Invalid window for getClosedTabData throws");
+ ok(test(() => ss.undoCloseTab({}, 0)),
+ "Invalid window for undoCloseTab throws");
+ ok(test(() => ss.undoCloseTab(window, -1)),
+ "Invalid index for undoCloseTab throws");
+ ok(test(() => ss.getWindowValue({}, "")),
+ "Invalid window for getWindowValue throws");
+ ok(test(() => ss.getWindowValue({}, "")),
+ "Invalid window for getWindowValue throws");
+ ok(test(() => ss.getWindowValue({}, "", "")),
+ "Invalid window for setWindowValue throws");
+}
diff --git a/comm/suite/components/tests/browser/browser_346337.js b/comm/suite/components/tests/browser/browser_346337.js
new file mode 100644
index 0000000000..f97e36cc24
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_346337.js
@@ -0,0 +1,122 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 346337 **/
+
+ var file = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ file.append("346337_test1.file");
+ let filePath1 = file.path;
+ file = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ file.append("346337_test2.file");
+ let filePath2 = file.path;
+
+ let fieldList = {
+ "//input[@name='input']": Date.now().toString(),
+ "//input[@name='spaced 1']": Math.random().toString(),
+ "//input[3]": "three",
+ "//input[@type='checkbox']": true,
+ "//input[@name='uncheck']": false,
+ "//input[@type='radio'][1]": false,
+ "//input[@type='radio'][2]": true,
+ "//input[@type='radio'][3]": false,
+ "//select": 2,
+ "//select[@multiple]": [1, 3],
+ "//textarea[1]": "",
+ "//textarea[2]": "Some text... " + Math.random(),
+ "//textarea[3]": "Some more text\n" + new Date(),
+ "//input[@type='file'][1]": [filePath1],
+ "//input[@type='file'][2]": [filePath1, filePath2]
+ };
+
+ function getElementByXPath(aTab, aQuery) {
+ let doc = aTab.linkedBrowser.contentDocument;
+ let xptype = doc.defaultView.XPathResult.FIRST_ORDERED_NODE_TYPE;
+ return doc.evaluate(aQuery, doc, null, xptype, null).singleNodeValue;
+ }
+
+ function setFormValue(aTab, aQuery, aValue) {
+ let node = getElementByXPath(aTab, aQuery);
+ if (typeof aValue == "string")
+ node.value = aValue;
+ else if (typeof aValue == "boolean")
+ node.checked = aValue;
+ else if (typeof aValue == "number")
+ node.selectedIndex = aValue;
+ else if (ChromeUtils.getClassName(node) === "HTMLInputElement" && node.type == "file")
+ node.mozSetFileNameArray(aValue, aValue.length);
+ else
+ Array.from(node.options).forEach((aOpt, aIx) =>
+ aOpt.selected = aValue.includes(aIx));
+ }
+
+ function compareFormValue(aTab, aQuery, aValue) {
+ let node = getElementByXPath(aTab, aQuery);
+ if (!node)
+ return false;
+ if (ChromeUtils.getClassName(node) === "HTMLInputElement") {
+ if (node.type == "file") {
+ let fileNames = node.mozGetFileNameArray();
+ return fileNames.length == aValue.length &&
+ Array.from(fileNames).every(aFile => aValue.includes(aFile));
+ }
+ return aValue == (node.type == "checkbox" || node.type == "radio" ?
+ node.checked : node.value);
+ }
+ if (ChromeUtils.getClassName(node) === "HTMLTextAreaElement")
+ return aValue == node.value;
+ if (!node.multiple)
+ return aValue == node.selectedIndex;
+ return Array.from(node.options).every((aOpt, aIx) =>
+ aValue.includes(aIx) == aOpt.selected);
+ }
+
+ // test setup
+ let tabbrowser = getBrowser();
+ waitForExplicitFinish();
+
+ // make sure we don't save form data at all (except for tab duplication)
+ Services.prefs.setIntPref("browser.sessionstore.privacy_level", 2);
+
+ let rootDir = getRootDirectory(gTestPath);
+ let testURL = rootDir + "browser_346337_sample.html";
+ let tab = tabbrowser.addTab(testURL);
+ tab.linkedBrowser.addEventListener("load", function loadListener1(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", loadListener1, true);
+ for (let xpath in fieldList)
+ setFormValue(tab, xpath, fieldList[xpath]);
+
+ let tab2 = ss.duplicateTab(window,tab);
+ tab2.linkedBrowser.addEventListener("pageshow", function pageshowListener2(aEvent) {
+ tab2.linkedBrowser.removeEventListener("pageshow", pageshowListener2, true);
+ for (let xpath in fieldList)
+ ok(compareFormValue(tab2, xpath, fieldList[xpath]),
+ "The value for \"" + xpath + "\" was correctly restored");
+ let browser = tab.linkedBrowser;
+ browser.addEventListener("load", function pageshowListener3(aEvent) {
+ browser.removeEventListener("load", pageshowListener3, true);
+ let tab3 = tabbrowser.undoCloseTab(0);
+ tab3.linkedBrowser.addEventListener("pageshow", function pageshowListener4(aEvent) {
+ tab3.linkedBrowser.removeEventListener("pageshow", pageshowListener4, true);
+ for (let xpath in fieldList)
+ if (fieldList[xpath])
+ ok(!compareFormValue(tab3, xpath, fieldList[xpath]),
+ "The value for \"" + xpath + "\" was correctly discarded");
+
+ if (Services.prefs.prefHasUserValue("browser.sessionstore.privacy_level"))
+ Services.prefs.clearUserPref("browser.sessionstore.privacy_level");
+ // undoCloseTab can reuse a single blank tab, so we have to
+ // make sure not to close the window when closing our last tab
+ if (tabbrowser.tabContainer.childNodes.length == 1)
+ tabbrowser.addTab();
+ tabbrowser.removeTab(tab3);
+ finish();
+ }, true);
+ }, true);
+ // clean up
+ tabbrowser.removeTab(tab2);
+ tabbrowser.removeTab(tab);
+ }, true);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_346337_sample.html b/comm/suite/components/tests/browser/browser_346337_sample.html
new file mode 100644
index 0000000000..b0c305775e
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_346337_sample.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<title>Test for bug 346337</title>
+
+<h3>Text Fields</h3>
+<input type="text" name="input">
+<input type="text" name="spaced 1">
+<input>
+
+<h3>Checkboxes and Radio buttons</h3>
+<input type="checkbox" name="check"> Check 1
+<input type="checkbox" name="uncheck" checked> Check 2
+<p>
+<input type="radio" name="group" value="1"> Radio 1
+<input type="radio" name="group" value="some"> Radio 2
+<input type="radio" name="group" checked> Radio 3
+
+<h3>Selects</h3>
+<select name="any">
+ <option value="1"> Select 1
+ <option value="some"> Select 2
+ <option>Select 3
+</select>
+<select multiple="multiple">
+ <option value=1> Multi-select 1
+ <option value=2> Multi-select 2
+ <option value=3> Multi-select 3
+ <option value=4> Multi-select 4
+</select>
+
+<h3>Text Areas</h3>
+<textarea name="testarea"></textarea>
+<textarea name="sized one" rows="5" cols="25"></textarea>
+<textarea></textarea>
+
+<h3>File Selector</h3>
+<input type="file">
+<input type="file" multiple>
diff --git a/comm/suite/components/tests/browser/browser_350525.js b/comm/suite/components/tests/browser/browser_350525.js
new file mode 100644
index 0000000000..c5fb0e11e7
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_350525.js
@@ -0,0 +1,100 @@
+function test() {
+ /** Test for Bug 350525 **/
+
+ function test(aLambda) {
+ try {
+ return aLambda() || true;
+ }
+ catch (ex) { }
+ return false;
+ }
+
+ waitForExplicitFinish();
+
+ ////////////////////////////
+ // setWindowValue, et al. //
+ ////////////////////////////
+ let key = "Unique name: " + Date.now();
+ let value = "Unique value: " + Math.random();
+
+ // test adding
+ ok(test(() => ss.setWindowValue(window, key, value)), "set a window value");
+
+ // test retrieving
+ is(ss.getWindowValue(window, key), value, "stored window value matches original");
+
+ // test deleting
+ ok(test(() => ss.deleteWindowValue(window, key)), "delete the window value");
+
+ // value should not exist post-delete
+ is(ss.getWindowValue(window, key), "", "window value was deleted");
+
+ // test deleting a non-existent value
+ ok(test(() => ss.deleteWindowValue(window, key)), "delete non-existent window value");
+
+ /////////////////////////
+ // setTabValue, et al. //
+ /////////////////////////
+ key = "Unique name: " + Math.random();
+ value = "Unique value: " + Date.now();
+ let tab = getBrowser().addTab();
+ tab.linkedBrowser.stop();
+
+ // test adding
+ ok(test(() => ss.setTabValue(tab, key, value)), "store a tab value");
+
+ // test retrieving
+ is(ss.getTabValue(tab, key), value, "stored tab value match original");
+
+ // test deleting
+ ok(test(() => ss.deleteTabValue(tab, key)), "delete the tab value");
+ // value should not exist post-delete
+ is(ss.getTabValue(tab, key), "", "tab value was deleted");
+
+ // test deleting a non-existent value
+ ok(test(() => ss.deleteTabValue(tab, key)), "delete non-existent tab value");
+
+ // clean up
+ getBrowser().removeTab(tab);
+
+ /////////////////////////////////////
+ // getClosedTabCount, undoCloseTab //
+ /////////////////////////////////////
+
+ // get closed tab count
+ let count = ss.getClosedTabCount(window);
+ let max_tabs_undo = Services.prefs.getIntPref("browser.sessionstore.max_tabs_undo");
+ ok(0 <= count && count <= max_tabs_undo,
+ "getClosedTabCount returns zero or at most max_tabs_undo");
+
+ // create a new tab
+ let testURL = "about:";
+ tab = getBrowser().addTab(testURL);
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad(aEvent) {
+ this.removeEventListener("load", testTabLBLoad, true);
+ // make sure that the next closed tab will increase getClosedTabCount
+ Services.prefs.setIntPref("browser.sessionstore.max_tabs_undo", max_tabs_undo + 1);
+
+ // remove tab
+ getBrowser().removeTab(tab);
+
+ // getClosedTabCount
+ var newcount = ss.getClosedTabCount(window);
+ ok(newcount > count, "after closing a tab, getClosedTabCount has been incremented");
+
+ // undoCloseTab
+ tab = test(() => ss.undoCloseTab(window, 0));
+ ok(tab, "undoCloseTab doesn't throw")
+
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad2(aEvent) {
+ this.removeEventListener("load", testTabLBLoad2, true);
+ is(this.currentURI.spec, testURL, "correct tab was reopened");
+
+ // clean up
+ if (Services.prefs.prefHasUserValue("browser.sessionstore.max_tabs_undo"))
+ Services.prefs.clearUserPref("browser.sessionstore.max_tabs_undo");
+ getBrowser().removeTab(tab);
+ finish();
+ }, true);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_354894.js b/comm/suite/components/tests/browser/browser_354894.js
new file mode 100644
index 0000000000..8fbc5330d0
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_354894.js
@@ -0,0 +1,459 @@
+/* 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/. */
+
+/**
+ * Checks that restoring the last browser window in session is actually
+ * working:
+ * 1.1) Open a new browser window
+ * 1.2) Add some tabs
+ * 1.3) Close that window
+ * 1.4) Opening another window
+ * --> State is restored
+ *
+ * 2.1) Open a new browser window
+ * 2.2) Add some tabs
+ * 2.4) Open some popups
+ * 2.5) Add another tab to one popup (so that it gets stored) and close it again
+ * 2.5) Close the browser window
+ * 2.6) Open another browser window
+ * --> State of the closed browser window, but not of the popup, is restored
+ *
+ * 3.1) Open a popup
+ * 3.2) Add another tab to the popup (so that it gets stored) and close it again
+ * 3.3) Open a window
+ * --> Nothing at all should be restored
+ *
+ * 4.1) Open two browser windows and close them again
+ * 4.2) undoCloseWindow() one
+ * 4.3) Open another browser window
+ * --> Nothing at all should be restored
+ *
+ * Checks the new notifications are correctly posted and processed, that is
+ * for each successful -requested a -granted is received, but omitted if
+ * -requested was cnceled
+ * Said notifications are:
+ * - browser-lastwindow-close-requested
+ * - browser-lastwindow-close-granted
+ * Tests are:
+ * 5) Cancel closing when first observe a -requested
+ * --> Window is kept open
+ * 6) Count the number of notifications
+ * --> count(-requested) == count(-granted) + 1
+ * --> (The first -requested was canceled, so off-by-one)
+ * 7) (Mac only) Mac version of Test 5 additionally preparing Test 6
+ *
+ * @see https://bugzilla.mozilla.org/show_bug.cgi?id=354894
+ * @note It is implicitly tested that restoring the last window works when
+ * non-browser windows are around. The "Run Tests" window as well as the main
+ * browser window (wherein the test code gets executed) won't be considered
+ * browser windows. To achiveve this said main browser window has it's windowtype
+ * attribute modified so that it's not considered a browser window any longer.
+ * This is crucial, because otherwise there would be two browser windows around,
+ * said main test window and the one opened by the tests, and hence the new
+ * logic wouldn't be executed at all.
+ * @note Mac only tests the new notifications, as restoring the last window is
+ * not enabled on that platform (platform shim; the application is kept running
+ * although there are no windows left)
+ * @note There is a difference when closing a browser window with
+ * BrowserTryToCloseWindow() as opposed to close(). The former will make
+ * nsSessionStore restore a window next time it gets a chance and will post
+ * notifications. The latter won't.
+ */
+
+function browserWindowsCount(expected, msg) {
+ if (typeof expected == "number")
+ expected = [expected, expected];
+ let count = 0;
+ let e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ if (!e.getNext().closed)
+ ++count;
+ }
+ is(count, expected[0], msg + " (nsIWindowMediator)");
+ let state = Cc["@mozilla.org/suite/sessionstore;1"]
+ .getService(Ci.nsISessionStore)
+ .getBrowserState();
+ is(JSON.parse(state).windows.length, expected[1], msg + " (getBrowserState)");
+}
+
+function test() {
+ browserWindowsCount(1, "Only one browser window should be open initially");
+
+ if (AppConstants.platform == "macosx") {
+ todo(false, "Test disabled on MacOSX. (Bug 520787)");
+ return;
+ }
+
+ waitForExplicitFinish();
+ // This test takes some time to run, and it could timeout randomly.
+ // So we require a longer timeout. See bug 528219.
+ requestLongerTimeout(2);
+
+ // Some urls that might be opened in tabs and/or popups
+ // Do not use about:blank:
+ // That one is reserved for special purposes in the tests
+ const TEST_URLS = ["about:mozilla", "about:buildconfig"];
+
+ // Number of -request notifications to except
+ // remember to adjust when adding new tests
+ const NOTIFICATIONS_EXPECTED = 4;
+
+ // Window features of popup windows
+ const POPUP_FEATURES = "toolbar=no,resizable=no,status=no";
+
+ // Window features of browser windows
+ const CHROME_FEATURES = "chrome,all,dialog=no";
+
+ // Store the old window type for cleanup
+ var oldWinType = "";
+ // Store the old tabs.warnOnClose pref so that we may reset it during
+ // cleanup
+ var oldWarnTabsOnClose = Services.prefs.getBoolPref("browser.tabs.warnOnClose");
+
+ // Observe these, and also use to count the number of hits
+ var observing = {
+ "browser-lastwindow-close-requested": 0,
+ "browser-lastwindow-close-granted": 0
+ };
+
+ /**
+ * Helper: Will observe and handle the notifications for us
+ */
+ var observer = {
+ hitCount: 0,
+
+ observe: function(aCancel, aTopic, aData) {
+ // count so that we later may compare
+ observing[aTopic]++;
+
+ // handle some tests
+ if (++this.hitCount == 1) {
+ // Test 6
+ aCancel.QueryInterface(Ci.nsISupportsPRBool).data = true;
+ }
+ }
+ };
+
+ /**
+ * Helper: Sets prefs as the testsuite requires
+ * @note Will be reset in cleanTestSuite just before finishing the tests
+ */
+ function setPrefs() {
+ Services.prefs.setIntPref("browser.startup.page", 3);
+ Services.prefs.setBoolPref("browser.tabs.warnOnClose", false);
+ }
+
+ /**
+ * Helper: Sets up this testsuite
+ */
+ function setupTestsuite(testFn) {
+ // Register our observers
+ for (let o in observing)
+ Services.obs.addObserver(observer, o);
+
+ // Make the main test window not count as a browser window any longer
+ oldWinType = document.documentElement.getAttribute("windowtype");
+ document.documentElement.setAttribute("windowtype", "navigator:testrunner");
+ }
+
+ /**
+ * Helper: Cleans up behind the testsuite
+ */
+ function cleanupTestsuite(callback) {
+ // Finally remove observers again
+ for (let o in observing)
+ Services.obs.removeObserver(observer, o, false);
+
+ // Reset the prefs we touched
+ for (let pref of [
+ "browser.startup.page"
+ ]) {
+ if (Services.prefs.prefHasUserValue(pref))
+ Services.prefs.clearUserPref(pref);
+ }
+ Services.prefs.setBoolPref("browser.tabs.warnOnClose", oldWarnTabsOnClose);
+
+ // Reset the window type
+ document.documentElement.setAttribute("windowtype", oldWinType);
+ }
+
+ /**
+ * Helper: sets the prefs and a new window with our test tabs
+ */
+ function setupTestAndRun(testFn) {
+ // Prepare the prefs
+ setPrefs();
+
+ // Prepare a window; open it and add more tabs
+ let newWin = openDialog(location, "_blank", CHROME_FEATURES, "about:config");
+ newWin.addEventListener("load", function loadListener1(aEvent) {
+ newWin.removeEventListener("load", loadListener1);
+ newWin.getBrowser().addEventListener("pageshow", function pageshowListener2(aEvent) {
+ newWin.getBrowser().removeEventListener("pageshow", pageshowListener2, true);
+ for (let url of TEST_URLS) {
+ newWin.getBrowser().addTab(url);
+ }
+
+ executeSoon(() => testFn(newWin));
+ }, true);
+ });
+ }
+
+ /**
+ * Test 1: Normal in-session restore
+ * @note: Non-Mac only
+ */
+ function testOpenCloseNormal(nextFn) {
+ setupTestAndRun(function(newWin) {
+ // Close the window
+ // window.close doesn't push any close events,
+ // so use BrowserTryToCloseWindow
+ newWin.BrowserTryToCloseWindow();
+
+ // The first request to close is denied by our observer (Test 6)
+ ok(!newWin.closed, "First close request was denied");
+ if (!newWin.closed) {
+ newWin.BrowserTryToCloseWindow();
+ ok(newWin.closed, "Second close request was granted");
+ }
+
+ // Open a new window
+ // The previously closed window should be restored
+ newWin = openDialog(location, "_blank", CHROME_FEATURES, "about:blank");
+ newWin.addEventListener("load", function loadListener3() {
+ newWin.removeEventListener("load", loadListener3);
+ executeSoon(function() {
+ is(newWin.getBrowser().browsers.length, TEST_URLS.length + 1,
+ "Restored window in-session with otherpopup windows around");
+
+ // Cleanup
+ newWin.close();
+
+ // Next please
+ executeSoon(nextFn);
+ });
+ }, true);
+ });
+ }
+
+ /**
+ * Test 2: Open some popup windows to check those aren't restored, but
+ * the browser window is
+ * @note: Non-Mac only
+ */
+ function testOpenCloseWindowAndPopup(nextFn) {
+ setupTestAndRun(function(newWin) {
+ // open some popups
+ let popup = openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[0]);
+ let popup2 = openDialog(location, "popup2", POPUP_FEATURES, TEST_URLS[1]);
+ popup2.addEventListener("load", function loadListener4() {
+ popup2.removeEventListener("load", loadListener4);
+ popup2.getBrowser().addEventListener("pageshow", function pageshowListener5() {
+ popup2.getBrowser().removeEventListener("pageshow", pageshowListener5, true);
+ popup2.getBrowser().addTab(TEST_URLS[0]);
+ // close the window
+ newWin.BrowserTryToCloseWindow();
+
+ // Close the popup window
+ // The test is successful when not this popup window is restored
+ // but instead newWin
+ popup2.close();
+
+ // open a new window the previously closed window should be restored to
+ newWin = openDialog(location, "_blank", CHROME_FEATURES, "about:blank");
+ newWin.addEventListener("load", function loadListener6() {
+ newWin.removeEventListener("load", loadListener6);
+ executeSoon(function() {
+ is(newWin.getBrowser().browsers.length, TEST_URLS.length + 1,
+ "Restored window and associated tabs in session");
+
+ // Cleanup
+ newWin.close();
+ popup.close();
+
+ // Next please
+ executeSoon(nextFn);
+ });
+ }, true);
+ }, true);
+ });
+ });
+ }
+
+ /**
+ * Test 3: Open some popup window to check it isn't restored.
+ * Instead nothing at all should be restored
+ * @note: Non-Mac only
+ */
+ function testOpenCloseOnlyPopup(nextFn) {
+ // prepare the prefs
+ setPrefs();
+
+ // This will cause nsSessionStore to restore a window the next time it
+ // gets a chance.
+ let popup = openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[1]);
+ popup.addEventListener("load", function loadListener7() {
+ popup.removeEventListener("load", loadListener7, true);
+ is(popup.getBrowser().browsers.length, 1,
+ "Did not restore the popup window (1)");
+ popup.BrowserTryToCloseWindow();
+
+ // Real tests
+ popup = openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[1]);
+ popup.addEventListener("load", function loadListener8() {
+ popup.removeEventListener("load", loadListener8);
+ popup.getBrowser().addEventListener("pageshow", function pageshowListener9() {
+ popup.getBrowser().removeEventListener("pageshow", pageshowListener9, true);
+ popup.getBrowser().addTab(TEST_URLS[0]);
+
+ is(popup.getBrowser().browsers.length, 2,
+ "Did not restore to the popup window (2)");
+
+ // Close the popup window
+ // The test is successful when not this popup window is restored
+ // but instead a new window is opened without restoring anything
+ popup.close();
+
+ let newWin = openDialog(location, "_blank", CHROME_FEATURES, "about:blank");
+ newWin.addEventListener("load", function loadListener10() {
+ newWin.removeEventListener("load", loadListener10, true);
+ executeSoon(function() {
+ isnot(newWin.getBrowser().browsers.length, 2,
+ "Did not restore the popup window");
+ is(TEST_URLS.indexOf(newWin.getBrowser().browsers[0].currentURI.spec), -1,
+ "Did not restore the popup window (2)");
+
+ // Cleanup
+ newWin.close();
+
+ // Next please
+ executeSoon(nextFn);
+ });
+ }, true);
+ }, true);
+ });
+ }, true);
+ }
+
+ /**
+ * Test 4: Open some windows and do undoCloseWindow. This should prevent any
+ * restoring later in the test
+ * @note: Non-Mac only
+ */
+ function testOpenCloseRestoreFromPopup(nextFn) {
+ setupTestAndRun(function(newWin) {
+ setupTestAndRun(function(newWin2) {
+ newWin.BrowserTryToCloseWindow();
+ newWin2.BrowserTryToCloseWindow();
+
+ browserWindowsCount([0, 1], "browser windows while running testOpenCloseRestoreFromPopup");
+
+ newWin = undoCloseWindow(0);
+
+ newWin2 = openDialog(location, "_blank", CHROME_FEATURES, "about:blank");
+ newWin2.addEventListener("load", function loadListener11() {
+ newWin2.removeEventListener("load", loadListener11, true);
+ executeSoon(function() {
+ is(newWin2.getBrowser().browsers.length, 1,
+ "Did not restore, as undoCloseWindow() was last called");
+ is(TEST_URLS.indexOf(newWin2.getBrowser().browsers[0].currentURI.spec), -1,
+ "Did not restore, as undoCloseWindow() was last called (2)");
+
+ browserWindowsCount([2, 3], "browser windows while running testOpenCloseRestoreFromPopup");
+
+ // Cleanup
+ newWin.close();
+ newWin2.close();
+
+ browserWindowsCount([0, 1], "browser windows while running testOpenCloseRestoreFromPopup");
+
+ // Next please
+ executeSoon(nextFn);
+ });
+ }, true);
+ });
+ });
+ }
+
+ /**
+ * Test 5: Check whether the right number of notifications was received during
+ * the tests
+ */
+ function testNotificationCount(nextFn) {
+ is(observing["browser-lastwindow-close-requested"], NOTIFICATIONS_EXPECTED,
+ "browser-lastwindow-close-requested notifications observed");
+
+ // -request must be one more as we cancel the first one we hit,
+ // and hence won't produce a corresponding -grant
+ // @see observer.observe
+ is(observing["browser-lastwindow-close-requested"],
+ observing["browser-lastwindow-close-granted"] + 1,
+ "Notification count for -request and -grant matches");
+
+ executeSoon(nextFn);
+ }
+
+ /**
+ * Test 6: Test if closing can be denied on Mac
+ * Futhermore prepares the testNotificationCount test (Test 6)
+ * @note: Mac only
+ */
+ function testMacNotifications(nextFn, iteration) {
+ iteration = iteration || 1;
+ setupTestAndRun(function(newWin) {
+ // close the window
+ // window.close doesn't push any close events,
+ // so use BrowserTryToCloseWindow
+ newWin.BrowserTryToCloseWindow();
+ if (iteration == 1) {
+ ok(!newWin.closed, "First close attempt denied");
+ if (!newWin.closed) {
+ newWin.BrowserTryToCloseWindow();
+ ok(newWin.closed, "Second close attempt granted");
+ }
+ }
+
+ if (iteration < NOTIFICATIONS_EXPECTED - 1) {
+ executeSoon(() => testMacNotifications(nextFn, ++iteration));
+ }
+ else {
+ executeSoon(nextFn);
+ }
+ });
+ }
+
+ // Execution starts here
+
+ setupTestsuite();
+ if (AppConstants.platform == "macosx") {
+ // Mac tests
+ testMacNotifications(function () {
+ testNotificationCount(function () {
+ cleanupTestsuite();
+ browserWindowsCount(1, "Only one browser window should be open eventually");
+ finish();
+ });
+ });
+ }
+ else {
+ // Non-Mac Tests
+ testOpenCloseNormal(function () {
+ browserWindowsCount([0, 1], "browser windows after testOpenCloseNormal");
+ testOpenCloseWindowAndPopup(function () {
+ browserWindowsCount([0, 1], "browser windows after testOpenCloseWindowAndPopup");
+ testOpenCloseOnlyPopup(function () {
+ browserWindowsCount([0, 1], "browser windows after testOpenCloseOnlyPopup");
+ testOpenCloseRestoreFromPopup(function () {
+ browserWindowsCount([0, 1], "browser windows after testOpenCloseRestoreFromPopup");
+ testNotificationCount(function () {
+ cleanupTestsuite();
+ browserWindowsCount(1, "browser windows after testNotificationCount");
+ finish();
+ });
+ });
+ });
+ });
+ });
+ }
+}
diff --git a/comm/suite/components/tests/browser/browser_367052.js b/comm/suite/components/tests/browser/browser_367052.js
new file mode 100644
index 0000000000..54ffaf0253
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_367052.js
@@ -0,0 +1,38 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 367052 **/
+
+ waitForExplicitFinish();
+
+ // make sure that the next closed tab will increase getClosedTabCount
+ let max_tabs_undo = Services.prefs.getIntPref("browser.sessionstore.max_tabs_undo");
+ Services.prefs.setIntPref("browser.sessionstore.max_tabs_undo", max_tabs_undo + 1);
+ let closedTabCount = ss.getClosedTabCount(window);
+
+ // restore a blank tab
+ let tab = getBrowser().addTab("about:");
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad(aEvent) {
+ this.removeEventListener("load", testTabLBLoad, true);
+
+ let history = tab.linkedBrowser.webNavigation.sessionHistory;
+ ok(history.count >= 1, "the new tab does have at least one history entry");
+
+ ss.setTabState(tab, '{ "entries": [] }');
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad2(aEvent) {
+ this.removeEventListener("load", testTabLBLoad2, true);
+ ok(history.count == 0, "the tab was restored without any history whatsoever");
+
+ getBrowser().removeTab(tab);
+ ok(ss.getClosedTabCount(window) == closedTabCount,
+ "The closed blank tab wasn't added to Recently Closed Tabs");
+
+ // clean up
+ if (Services.prefs.prefHasUserValue("browser.sessionstore.max_tabs_undo"))
+ Services.prefs.clearUserPref("browser.sessionstore.max_tabs_undo");
+ finish();
+ }, true);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_393716.js b/comm/suite/components/tests/browser/browser_393716.js
new file mode 100644
index 0000000000..ce1d33e167
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_393716.js
@@ -0,0 +1,74 @@
+function test() {
+ /** Test for Bug 393716 **/
+
+ waitForExplicitFinish();
+
+ /////////////////
+ // getTabState //
+ /////////////////
+ let key = "Unique key: " + Date.now();
+ let value = "Unique value: " + Math.random();
+ let testURL = "about:config";
+
+ // create a new tab
+ let tab = getBrowser().addTab(testURL);
+ ss.setTabValue(tab, key, value);
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad(aEvent) {
+ this.removeEventListener("load", testTabLBLoad, true);
+ // get the tab's state
+ let state = ss.getTabState(tab);
+ ok(state, "get the tab's state");
+
+ // verify the tab state's integrity
+ state = eval("(" + state + ")");
+ ok(state instanceof Object && state.entries instanceof Array && state.entries.length > 0,
+ "state object seems valid");
+ ok(state.entries.length == 1 && state.entries[0].url == testURL,
+ "Got the expected state object (test URL)");
+ ok(state.extData && state.extData[key] == value,
+ "Got the expected state object (test manually set tab value)");
+
+ // clean up
+ getBrowser().removeTab(tab);
+ }, true);
+
+ //////////////////////////////////
+ // setTabState and duplicateTab //
+ //////////////////////////////////
+ let key2 = "key2";
+ let value2 = "Value " + Math.random();
+ let value3 = "Another value: " + Date.now();
+ let state = { entries: [{ url: testURL }], extData: { key2: value2 } };
+
+ // create a new tab
+ let tab2 = getBrowser().addTab();
+ // set the tab's state
+ ss.setTabState(tab2, JSON.stringify(state));
+ tab2.linkedBrowser.addEventListener("load", function testTab2LBLoad(aEvent) {
+ this.removeEventListener("load", testTab2LBLoad, true);
+ // verify the correctness of the restored tab
+ ok(ss.getTabValue(tab2, key2) == value2 && this.currentURI.spec == testURL,
+ "the tab's state was correctly restored");
+
+ // add text data
+ let textbox = this.contentDocument.getElementById("textbox");
+ textbox.value = value3;
+
+ // duplicate the tab
+ let duplicateTab = ss.duplicateTab(window, tab2);
+ getBrowser().removeTab(tab2);
+
+ duplicateTab.linkedBrowser.addEventListener("load", function testTab2DupLBLoad(aEvent) {
+ this.removeEventListener("load", testTab2DupLBLoad, true);
+ // verify the correctness of the duplicated tab
+ ok(ss.getTabValue(duplicateTab, key2) == value2 && this.currentURI.spec == testURL,
+ "correctly duplicated the tab's state");
+ let textbox = this.contentDocument.getElementById("textbox");
+ is(textbox.value, value3, "also duplicated text data");
+
+ // clean up
+ getBrowser().removeTab(duplicateTab);
+ finish();
+ }, true);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_394759_basic.js b/comm/suite/components/tests/browser/browser_394759_basic.js
new file mode 100644
index 0000000000..a2137cc60c
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_394759_basic.js
@@ -0,0 +1,77 @@
+/* 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/. */
+
+/** Test for Bug 394759, ported in Bug 510890 **/
+
+function test() {
+ waitForExplicitFinish();
+
+ let testURL = "about:config";
+ let uniqueKey = "bug 394759";
+ let uniqueValue = "unik" + Date.now();
+ let uniqueText = "pi != " + Math.random();
+
+ // Be consistent: let the page actually display, as we are "interacting" with it.
+ Services.prefs.setBoolPref("general.warnOnAboutConfig", false);
+
+ // make sure that the next closed window will increase getClosedWindowCount
+ let max_windows_undo = Services.prefs.getIntPref("browser.sessionstore.max_windows_undo");
+ Services.prefs.setIntPref("browser.sessionstore.max_windows_undo", max_windows_undo + 1);
+ let closedWindowCount = ss.getClosedWindowCount();
+
+ provideWindow(function onTestURLLoaded(newWin) {
+ newWin.getBrowser().addTab().linkedBrowser.stop();
+
+ // mark the window with some unique data to be restored later on
+ ss.setWindowValue(newWin, uniqueKey, uniqueValue);
+ let textbox = newWin.content.document.getElementById("textbox");
+ textbox.value = uniqueText;
+
+ newWin.close();
+
+ is(ss.getClosedWindowCount(), closedWindowCount + 1,
+ "The closed window was added to Recently Closed Windows");
+ let data = JSON.parse(ss.getClosedWindowData())[0];
+ ok(data.title == testURL && JSON.stringify(data).includes(uniqueText),
+ "The closed window data was stored correctly");
+
+ // reopen the closed window and ensure its integrity
+ let newWin2 = ss.undoCloseWindow(0);
+
+ ok(newWin2 instanceof ChromeWindow,
+ "undoCloseWindow actually returned a window");
+ is(ss.getClosedWindowCount(), closedWindowCount,
+ "The reopened window was removed from Recently Closed Windows");
+
+ // SSTabRestored will fire more than once, so we need to make sure we count them
+ let restoredTabs = 0;
+ let expectedTabs = data.tabs.length;
+ newWin2.addEventListener("SSTabRestored", function sstabrestoredListener(aEvent) {
+ ++restoredTabs;
+ info("Restored tab " + restoredTabs + "/" + expectedTabs);
+ if (restoredTabs < expectedTabs) {
+ return;
+ }
+
+ newWin2.removeEventListener("SSTabRestored", sstabrestoredListener, true);
+
+ is(newWin2.getBrowser().tabs.length, 2,
+ "The window correctly restored 2 tabs");
+ is(newWin2.getBrowser().currentURI.spec, testURL,
+ "The window correctly restored the URL");
+
+ let textbox = newWin2.content.document.getElementById("textbox");
+ is(textbox.value, uniqueText,
+ "The window correctly restored the form");
+ is(ss.getWindowValue(newWin2, uniqueKey), uniqueValue,
+ "The window correctly restored the data associated with it");
+
+ // clean up
+ newWin2.close();
+ Services.prefs.clearUserPref("browser.sessionstore.max_windows_undo");
+ Services.prefs.clearUserPref("general.warnOnAboutConfig");
+ finish();
+ }, true);
+ }, testURL);
+}
diff --git a/comm/suite/components/tests/browser/browser_394759_behavior.js b/comm/suite/components/tests/browser/browser_394759_behavior.js
new file mode 100644
index 0000000000..79c70cd937
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_394759_behavior.js
@@ -0,0 +1,66 @@
+/* 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/. */
+
+/** Test for Bug 394759, ported in Bug 510890 **/
+
+function test() {
+ // This test takes quite some time, and timeouts frequently, so we require
+ // more time to run.
+ // See Bug 518970.
+ requestLongerTimeout(2);
+
+ waitForExplicitFinish();
+
+ // helper function that does the actual testing
+ function openWindowRec(windowsToOpen, expectedResults, recCallback) {
+ // do actual checking
+ if (!windowsToOpen.length) {
+ let closedWindowData = JSON.parse(ss.getClosedWindowData());
+ let numPopups = closedWindowData.filter(function(el, i, arr) {
+ return el.isPopup;
+ }).length;
+ let numNormal = ss.getClosedWindowCount() - numPopups;
+
+ let oResults = AppConstants.platform == "macosx" ? expectedResults.mac
+ : expectedResults.other;
+ is(numPopups, oResults.popup,
+ "There were " + oResults.popup + " popup windows to repoen");
+ is(numNormal, oResults.normal,
+ "There were " + oResults.normal + " normal windows to repoen");
+
+ // cleanup & return
+ executeSoon(recCallback);
+ return;
+ }
+
+ // hack to force window to be considered a popup (toolbar=no didn't work)
+ let winData = windowsToOpen.shift();
+ let settings = "chrome,dialog=no," +
+ (winData.isPopup ? "all=no" : "all");
+ let url = "http://example.com/?window=" + windowsToOpen.length;
+
+ provideWindow(function onTestURLLoaded(win) {
+ win.close();
+ openWindowRec(windowsToOpen, expectedResults, recCallback);
+ }, url, settings);
+ }
+
+ let windowsToOpen = [{isPopup: false},
+ {isPopup: false},
+ {isPopup: true},
+ {isPopup: true},
+ {isPopup: true}];
+ let expectedResults = {mac: {popup: 3, normal: 0},
+ other: {popup: 3, normal: 1}};
+ let windowsToOpen2 = [{isPopup: false},
+ {isPopup: false},
+ {isPopup: false},
+ {isPopup: false},
+ {isPopup: false}];
+ let expectedResults2 = {mac: {popup: 0, normal: 3},
+ other: {popup: 0, normal: 3}};
+ openWindowRec(windowsToOpen, expectedResults, function() {
+ openWindowRec(windowsToOpen2, expectedResults2, finish);
+ });
+}
diff --git a/comm/suite/components/tests/browser/browser_408470.js b/comm/suite/components/tests/browser/browser_408470.js
new file mode 100644
index 0000000000..099aa7cbf4
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_408470.js
@@ -0,0 +1,57 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 408470 **/
+
+ waitForExplicitFinish();
+
+ let pendingCount = 1;
+ let rootDir = getRootDirectory(gTestPath);
+ let testURL = rootDir + "browser_408470_sample.html";
+ let tab = getBrowser().addTab(testURL);
+ let window = tab.ownerDocument.defaultView;
+
+ tab.linkedBrowser.addEventListener("load", function loadListener1(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", loadListener1, true);
+ // enable all stylesheets and verify that they're correctly persisted
+ Array.from(tab.linkedBrowser.contentDocument.styleSheets).forEach(function(aSS, aIx) {
+ pendingCount++;
+ let ssTitle = aSS.title;
+ stylesheetSwitchAll(tab.linkedBrowser.contentWindow, ssTitle);
+
+ let newTab = ss.duplicateTab(window,tab);
+ newTab.linkedBrowser.addEventListener("load", function loadListener2(aEvent) {
+ newTab.linkedBrowser.removeEventListener("load", loadListener2, true);
+ let states = Array.from(newTab.linkedBrowser.contentDocument.styleSheets,
+ aSS => !aSS.disabled);
+ let correct = states.indexOf(true) == aIx && !states.includes(true, aIx + 1);
+
+ if (/^fail_/.test(ssTitle))
+ ok(!correct, "didn't restore stylesheet " + ssTitle);
+ else
+ ok(correct, "restored stylesheet " + ssTitle);
+
+ getBrowser().removeTab(newTab);
+ if (--pendingCount == 0)
+ finish();
+ }, true);
+ });
+
+ // disable all styles and verify that this is correctly persisted
+ tab.linkedBrowser.markupDocumentViewer.authorStyleDisabled = true;
+ let newTab = ss.duplicateTab(window,tab);
+ newTab.linkedBrowser.addEventListener("load", function loadListener3(aEvent) {
+ newTab.linkedBrowser.removeEventListener("load", loadListener3, true);
+ is(newTab.linkedBrowser.markupDocumentViewer.authorStyleDisabled, true,
+ "disabled all stylesheets");
+
+ getBrowser().removeTab(newTab);
+ if (--pendingCount == 0)
+ finish();
+ }, true);
+
+ getBrowser().removeTab(tab);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_408470_sample.html b/comm/suite/components/tests/browser/browser_408470_sample.html
new file mode 100644
index 0000000000..44122b9453
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_408470_sample.html
@@ -0,0 +1,19 @@
+<html>
+<head>
+<title>Test for bug 408470</title>
+
+<link href="404.css" title="default" rel="stylesheet">
+<link href="404.css" title="alternate" rel="alternate stylesheet">
+<link href="404.css" title="altERnate" rel=" styLEsheet altERnate ">
+<link href="404.css" title="media_empty" rel="alternate stylesheet" media="">
+<link href="404.css" title="media_all" rel="alternate stylesheet" media="all">
+<link href="404.css" title="media_ALL" rel="alternate stylesheet" media=" ALL ">
+<link href="404.css" title="media_screen" rel="alternate stylesheet" media="screen">
+<link href="404.css" title="media_print_screen" rel="alternate stylesheet" media="print,screen">
+<link href="404.css" title="fail_media_print" rel="alternate stylesheet" media="print">
+<link href="404.css" title="fail_media_projection" rel="stylesheet" media="projection">
+<link href="404.css" title="fail_media_invalid" rel="alternate stylesheet" media="hallo">
+
+</head>
+<body></body>
+</html>
diff --git a/comm/suite/components/tests/browser/browser_423132.js b/comm/suite/components/tests/browser/browser_423132.js
new file mode 100644
index 0000000000..87108f6c6f
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_423132.js
@@ -0,0 +1,86 @@
+/* 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/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+function browserWindowsCount() {
+ let count = 0;
+ let e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ if (!e.getNext().closed)
+ ++count;
+ }
+ return count;
+}
+
+function test() {
+ // test that cookies are stored and restored correctly by sessionstore,
+ // bug 423132, ported by bug 524371.
+ is(browserWindowsCount(), 1, "Only one browser window should be open initially");
+
+ waitForExplicitFinish();
+
+ let cs = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager);
+ cs.removeAll();
+
+ // make sure that sessionstore.js can be forced to be created by setting
+ // the interval pref to 0
+ Services.prefs.setIntPref("browser.sessionstore.interval", 0);
+
+ const testURL = "http://mochi.test:8888/browser/" +
+ "suite/common/tests/browser/browser_423132_sample.html";
+
+ // open a new window
+ let newWin = openDialog(location, "_blank", "chrome,all,dialog=no", "about:blank");
+
+ // make sure sessionstore saves the cookie data, then close the window
+ newWin.addEventListener("load", function testNewWinLoad(aEvent) {
+ newWin.removeEventListener("load", testNewWinLoad);
+
+ newWin.getBrowser().selectedBrowser.loadURI(testURL, null, null);
+
+ newWin.getBrowser().addEventListener("pageshow", function testNewWinPageShow(aEvent) {
+ newWin.getBrowser().removeEventListener("pageshow", testNewWinPageShow, true);
+
+ // get the sessionstore state for the window
+ let state = ss.getWindowState(newWin);
+
+ // verify our cookie got set during pageload
+ let e = cs.enumerator;
+ let cookie;
+ let i = 0;
+ while (e.hasMoreElements()) {
+ cookie = e.getNext().QueryInterface(Ci.nsICookie);
+ i++;
+ }
+ is(i, 1, "expected one cookie");
+
+ // remove the cookie
+ cs.removeAll();
+
+ // restore the window state
+ ss.setWindowState(newWin, state, true);
+
+ // at this point, the cookie should be restored...
+ e = cs.enumerator;
+ let cookie2;
+ while (e.hasMoreElements()) {
+ cookie2 = e.getNext().QueryInterface(Ci.nsICookie);
+ if (cookie.name == cookie2.name)
+ break;
+ }
+ is(cookie.name, cookie2.name, "cookie name successfully restored");
+ is(cookie.value, cookie2.value, "cookie value successfully restored");
+ is(cookie.path, cookie2.path, "cookie path successfully restored");
+
+ // clean up
+ if (Services.prefs.prefHasUserValue("browser.sessionstore.interval"))
+ Services.prefs.clearUserPref("browser.sessionstore.interval");
+ cs.removeAll();
+ newWin.close();
+ is(browserWindowsCount(), 1, "Only one browser window should be open eventually");
+ finish();
+ }, true);
+ });
+}
diff --git a/comm/suite/components/tests/browser/browser_423132_sample.html b/comm/suite/components/tests/browser/browser_423132_sample.html
new file mode 100644
index 0000000000..bac1866cbc
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_423132_sample.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script>
+ // generate an enormous random number...
+ var r = Math.floor(Math.random() * Math.pow(2, 62)).toString();
+
+ // ... and use it to set a randomly named cookie
+ document.cookie = r + "=value; path=/ohai";
+ </script>
+<body>
+</body>
+</html>
diff --git a/comm/suite/components/tests/browser/browser_447951.js b/comm/suite/components/tests/browser/browser_447951.js
new file mode 100644
index 0000000000..259d49a0fa
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_447951.js
@@ -0,0 +1,50 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 447951 **/
+
+ waitForExplicitFinish();
+ const baseURL = "http://mochi.test:8888/browser/" +
+ "suite/common/tests/browser/browser_447951_sample.html#";
+
+ let tab = getBrowser().addTab();
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", testTabLBLoad, true);
+
+ let tabState = { entries: [] };
+ let max_entries = Services.prefs.getIntPref("browser.sessionhistory.max_entries");
+ for (let i = 0; i < max_entries; i++)
+ tabState.entries.push({ url: baseURL + i });
+
+ ss.setTabState(tab, JSON.stringify(tabState));
+ tab.addEventListener("SSTabRestored", function testTabSSTabRestored(aEvent) {
+ tab.removeEventListener("SSTabRestored", testTabSSTabRestored);
+ tabState = JSON.parse(ss.getTabState(tab));
+ is(tabState.entries.length, max_entries, "session history filled to the limit");
+ is(tabState.entries[0].url, baseURL + 0, "... but not more");
+
+ // visit yet another anchor (appending it to session history)
+ let doc = tab.linkedBrowser.contentDocument;
+ let event = doc.createEvent("MouseEvents");
+ event.initMouseEvent("click", true, true, doc.defaultView, 1,
+ 0, 0, 0, 0, false, false, false, false, 0, null);
+ doc.querySelector("a").dispatchEvent(event);
+
+ executeSoon(function() {
+ tabState = JSON.parse(ss.getTabState(tab));
+ is(tab.linkedBrowser.currentURI.spec, baseURL + "end",
+ "the new anchor was loaded");
+ is(tabState.entries[tabState.entries.length - 1].url, baseURL + "end",
+ "... and ignored");
+ is(tabState.entries[0].url, baseURL + 1,
+ "... and the first item was removed");
+
+ // clean up
+ getBrowser().removeTab(tab);
+ finish();
+ });
+ });
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_447951_sample.html b/comm/suite/components/tests/browser/browser_447951_sample.html
new file mode 100644
index 0000000000..b9ad7bf1f1
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_447951_sample.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<title>Testcase for bug 447951</title>
+
+<a href="#end">click me</a>
diff --git a/comm/suite/components/tests/browser/browser_448741.js b/comm/suite/components/tests/browser/browser_448741.js
new file mode 100644
index 0000000000..85aee816f2
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_448741.js
@@ -0,0 +1,62 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 448741 **/
+
+ waitForExplicitFinish();
+
+ let uniqueName = "bug 448741";
+ let uniqueValue = "as good as unique: " + Date.now();
+
+ // set a unique value on a new, blank tab
+ var tab = getBrowser().addTab();
+ tab.linkedBrowser.stop();
+ ss.setTabValue(tab, uniqueName, uniqueValue);
+ let valueWasCleaned = false;
+
+ // prevent our value from being written to disk
+ function cleaningObserver(aSubject, aTopic, aData) {
+ ok(aTopic == "sessionstore-state-write", "observed correct topic?");
+ ok(aSubject instanceof Ci.nsISupportsString, "subject is a string?");
+ ok(aSubject.data.includes(uniqueValue), "data contains our value?");
+
+ // find the data for the newly added tab and delete it
+ let state = JSON.parse(aSubject.data);
+ state.windows.forEach(function (winData) {
+ winData.tabs.forEach(function (tabData) {
+ if (tabData.extData && uniqueName in tabData.extData &&
+ tabData.extData[uniqueName] == uniqueValue) {
+ delete tabData.extData[uniqueName];
+ valueWasCleaned = true;
+ }
+ });
+ });
+
+ ok(valueWasCleaned, "found and removed the specific tab value");
+ aSubject.data = JSON.stringify(state);
+ Services.obs.removeObserver(cleaningObserver, aTopic, false);
+ }
+
+ // make sure that all later observers don't see that value any longer
+ function checkingObserver(aSubject, aTopic, aData) {
+ ok(valueWasCleaned && aSubject instanceof Ci.nsISupportsString,
+ "ready to check the cleaned state?");
+ ok(!aSubject.data.includes(uniqueValue), "data no longer contains our value?");
+
+ // clean up
+ getBrowser().removeTab(tab);
+ Services.obs.removeObserver(checkingObserver, aTopic, false);
+ if (Services.prefs.prefHasUserValue("browser.sessionstore.interval"))
+ Services.prefs.clearUserPref("browser.sessionstore.interval");
+ finish();
+ }
+
+ // last added observers are invoked first
+ Services.obs.addObserver(checkingObserver, "sessionstore-state-write");
+ Services.obs.addObserver(cleaningObserver, "sessionstore-state-write");
+
+ // trigger an immediate save operation
+ Services.prefs.setIntPref("browser.sessionstore.interval", 0);
+}
diff --git a/comm/suite/components/tests/browser/browser_454908.js b/comm/suite/components/tests/browser/browser_454908.js
new file mode 100644
index 0000000000..e5ce5f932e
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_454908.js
@@ -0,0 +1,52 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 454908 **/
+
+ waitForExplicitFinish();
+
+ let fieldValues = {
+ username: "User " + Math.random(),
+ passwd: "pwd" + Date.now()
+ };
+
+ // make sure we do save form data
+ Services.prefs.setIntPref("browser.sessionstore.privacy_level", 0);
+
+ let rootDir = getRootDirectory(gTestPath);
+ let testURL = rootDir + "browser_454908_sample.html";
+ let tab = getBrowser().addTab(testURL);
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", testTabLBLoad, true);
+ let doc = tab.linkedBrowser.contentDocument;
+ for (let id in fieldValues)
+ doc.getElementById(id).value = fieldValues[id];
+
+ getBrowser().removeTab(tab);
+
+ tab = getBrowser().undoCloseTab();
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad2(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", testTabLBLoad2, true);
+ let doc = tab.linkedBrowser.contentDocument;
+ for (let id in fieldValues) {
+ let node = doc.getElementById(id);
+ if (node.type == "password")
+ is(node.value, "", "password wasn't saved/restored");
+ else
+ is(node.value, fieldValues[id], "username was saved/restored");
+ }
+
+ // clean up
+ if (Services.prefs.prefHasUserValue("browser.sessionstore.privacy_level"))
+ Services.prefs.clearUserPref("browser.sessionstore.privacy_level");
+ // undoCloseTab can reuse a single blank tab, so we have to
+ // make sure not to close the window when closing our last tab
+ if (gBrowser.tabContainer.childNodes.length == 1)
+ gBrowser.addTab();
+ gBrowser.removeTab(tab);
+ finish();
+ }, true);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_454908_sample.html b/comm/suite/components/tests/browser/browser_454908_sample.html
new file mode 100644
index 0000000000..02f40bf20b
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_454908_sample.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<title>Test for bug 454908</title>
+
+<h3>Dummy Login</h3>
+<form>
+<p>Username: <input type="text" id="username">
+<p>Password: <input type="password" id="passwd">
+</form>
diff --git a/comm/suite/components/tests/browser/browser_456342.js b/comm/suite/components/tests/browser/browser_456342.js
new file mode 100644
index 0000000000..86bcb0ef06
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_456342.js
@@ -0,0 +1,47 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 456342 **/
+
+ waitForExplicitFinish();
+
+ // make sure we do save form data
+ Services.prefs.setIntPref("browser.sessionstore.privacy_level", 0);
+
+ let rootDir = getRootDirectory(gTestPath);
+ let testURL = rootDir + "browser_456342_sample.xhtml";
+ let tab = getBrowser().addTab(testURL);
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad(aEvent) {
+ this.removeEventListener("load", testTabLBLoad, true);
+
+ let expectedValue = "try to save me";
+ // Since bug 537289 we only save non-default values, so we need to set each
+ // form field's value after load.
+ let formEls = aEvent.originalTarget.forms[0].elements;
+ for (let i = 0; i < formEls.length; i++)
+ formEls[i].value = expectedValue;
+
+ getBrowser().removeTab(tab);
+
+ let undoItems = JSON.parse(ss.getClosedTabData(window));
+ let savedFormData = undoItems[0].state.entries[0].formdata;
+
+ let countGood = 0, countBad = 0;
+ for (let value of Object.values(savedFormData)) {
+ if (value == expectedValue)
+ countGood++;
+ else
+ countBad++;
+ }
+
+ is(countGood, 4, "Saved text for non-standard input fields");
+ is(countBad, 0, "Didn't save text for ignored field types");
+
+ // clean up
+ if (Services.prefs.prefHasUserValue("browser.sessionstore.privacy_level"))
+ Services.prefs.clearUserPref("browser.sessionstore.privacy_level");
+ finish();
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_456342_sample.xhtml b/comm/suite/components/tests/browser/browser_456342_sample.xhtml
new file mode 100644
index 0000000000..f0b0005b77
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_456342_sample.xhtml
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head><title>Test for bug 456342</title></head>
+
+<body>
+<form>
+<h3>Non-standard &lt;input&gt;s</h3>
+<p>Search <input type="search" id="searchTerm"/></p>
+<p>Image Search: <input type="image search" /></p>
+<p>Autocomplete: <input type="autocomplete" name="fill-in"/></p>
+<p>Mistyped: <input type="txet" name="mistyped"/></p>
+
+<h3>Ignored types</h3>
+<input type="hidden" name="hideme"/>
+<input type="HIDDEN" name="hideme2"/>
+<input type="submit" name="submit"/>
+<input type="reset" name="reset"/>
+<input type="image" name="image"/>
+<input type="button" name="button"/>
+<input type="password" name="password"/>
+<input type="PassWord" name="password2"/>
+<input type="PASSWORD" name="password3"/>
+</form>
+
+</body>
+</html>
diff --git a/comm/suite/components/tests/browser/browser_461634.js b/comm/suite/components/tests/browser/browser_461634.js
new file mode 100644
index 0000000000..28207e7e6b
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_461634.js
@@ -0,0 +1,88 @@
+/* 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/. */
+
+function browserWindowsCount() {
+ let count = 0;
+ let e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ if (!e.getNext().closed)
+ ++count;
+ }
+ return count;
+}
+
+function test() {
+ /** Test for Bug 461634, ported by Bug 524345 **/
+ is(browserWindowsCount(), 1, "Only one browser window should be open initially");
+
+ waitForExplicitFinish();
+
+ const REMEMBER = Date.now(), FORGET = Math.random();
+ let test_state = { windows: [{ "tabs": [{ "entries": [] }], _closedTabs: [
+ { state: { entries: [{ url: "http://www.example.net/" }] }, title: FORGET },
+ { state: { entries: [{ url: "http://www.example.net/" }] }, title: REMEMBER },
+ { state: { entries: [{ url: "http://www.example.net/" }] }, title: FORGET },
+ { state: { entries: [{ url: "http://www.example.net/" }] }, title: REMEMBER },
+ ] }] };
+ let remember_count = 2;
+
+ function countByTitle(aClosedTabList, aTitle) {
+ return aClosedTabList.filter(aData => aData.title == aTitle).length;
+ }
+
+ function testForError(aFunction) {
+ try {
+ aFunction();
+ return false;
+ }
+ catch (ex) {
+ return ex.name == "NS_ERROR_ILLEGAL_VALUE";
+ }
+ }
+
+ // open a window and add the above closed tab list
+ let newWin = openDialog(location, "", "chrome,all,dialog=no");
+ newWin.addEventListener("load", function loadListener(aEvent) {
+ newWin.removeEventListener("load", loadListener);
+
+ Services.prefs.setIntPref("browser.sessionstore.max_tabs_undo",
+ test_state.windows[0]._closedTabs.length);
+ ss.setWindowState(newWin, JSON.stringify(test_state), true);
+
+ let closedTabs = JSON.parse(ss.getClosedTabData(newWin));
+ is(closedTabs.length, test_state.windows[0]._closedTabs.length,
+ "Closed tab list has the expected length");
+ is(countByTitle(closedTabs, FORGET),
+ test_state.windows[0]._closedTabs.length - remember_count,
+ "The correct amout of tabs are to be forgotten");
+ is(countByTitle(closedTabs, REMEMBER), remember_count,
+ "Everything is set up.");
+
+ // all of the following calls with illegal arguments should throw NS_ERROR_ILLEGAL_VALUE
+ ok(testForError(() => ss.forgetClosedTab({}, 0)),
+ "Invalid window for forgetClosedTab throws");
+ ok(testForError(() => ss.forgetClosedTab(newWin, -1)),
+ "Invalid tab for forgetClosedTab throws");
+ ok(testForError(() => ss.forgetClosedTab(newWin, test_state.windows[0]._closedTabs.length + 1)),
+ "Invalid tab for forgetClosedTab throws");
+
+ // Remove third tab, then first tab
+ ss.forgetClosedTab(newWin, 2);
+ ss.forgetClosedTab(newWin, null);
+
+ closedTabs = JSON.parse(ss.getClosedTabData(newWin));
+ is(closedTabs.length, remember_count,
+ "The correct amout of tabs was removed");
+ is(countByTitle(closedTabs, FORGET), 0,
+ "All tabs specifically forgotten were indeed removed");
+ is(countByTitle(closedTabs, REMEMBER), remember_count,
+ "... and tabs not specifically forgetten weren't.");
+
+ // clean up
+ newWin.close();
+ is(browserWindowsCount(), 1, "Only one browser window should be open eventually");
+ Services.prefs.clearUserPref("browser.sessionstore.max_tabs_undo");
+ finish();
+ });
+}
diff --git a/comm/suite/components/tests/browser/browser_463206.js b/comm/suite/components/tests/browser/browser_463206.js
new file mode 100644
index 0000000000..c044787546
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_463206.js
@@ -0,0 +1,64 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 463206 **/
+
+ waitForExplicitFinish();
+
+ let testURL = "http://mochi.test:8888/browser/" +
+ "suite/common/tests/browser/browser_463206_sample.html";
+
+ var frameCount = 0;
+ let tab = getBrowser().addTab(testURL);
+ let window = tab.ownerDocument.defaultView;
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad(aEvent) {
+ // wait for all frames to load completely
+ if (frameCount++ < 5)
+ return;
+ tab.linkedBrowser.removeEventListener("load", testTabLBLoad, true);
+ function typeText(aTextField, aValue) {
+ aTextField.value = aValue;
+
+ let event = aTextField.ownerDocument.createEvent("UIEvents");
+ event.initUIEvent("input", true, true, aTextField.ownerDocument.defaultView, 0);
+ aTextField.dispatchEvent(event);
+ }
+
+ let doc = tab.linkedBrowser.contentDocument;
+ typeText(doc.getElementById("out1"), Date.now());
+ typeText(doc.getElementsByName("1|#out2")[0], Math.random());
+ typeText(doc.defaultView.frames[0].frames[1].document.getElementById("in1"), new Date());
+
+ frameCount = 0;
+ let tab2 = ss.duplicateTab(window,tab);
+ tab2.linkedBrowser.addEventListener("load", function testTab2LBLoad(aEvent) {
+ // wait for all frames to load completely
+ if (frameCount++ < 5)
+ return;
+ tab2.linkedBrowser.removeEventListener("load", testTab2LBLoad, true);
+
+ let doc = tab2.linkedBrowser.contentDocument;
+ let win = tab2.linkedBrowser.contentWindow;
+ isnot(doc.getElementById("out1").value,
+ win.frames[1].document.getElementById("out1").value,
+ "text isn't reused for frames");
+ isnot(doc.getElementsByName("1|#out2")[0].value, "",
+ "text containing | and # is correctly restored");
+ is(win.frames[1].document.getElementById("out2").value, "",
+ "id prefixes can't be faked");
+ // Disabled for now, Bug 588077
+ // isnot(win.frames[0].frames[1].document.getElementById("in1").value, "",
+ // "id prefixes aren't mixed up");
+ is(win.frames[1].frames[0].document.getElementById("in1").value, "",
+ "id prefixes aren't mixed up");
+
+ // clean up
+ getBrowser().removeTab(tab2);
+ getBrowser().removeTab(tab);
+
+ finish();
+ }, true);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_463206_sample.html b/comm/suite/components/tests/browser/browser_463206_sample.html
new file mode 100644
index 0000000000..48a841ee69
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_463206_sample.html
@@ -0,0 +1,10 @@
+<!-- Testcase originally by <moz_bug_r_a4@yahoo.com> -->
+
+<!DOCTYPE html>
+<title>Test for bug 463206</title>
+
+<iframe src="data:text/html,<iframe></iframe><iframe%20src='data:text/html,<input%2520id=%2522in1%2522>'></iframe>"></iframe>
+<iframe src="data:text/html,<input%20id='out1'><input%20id='out2'><iframe%20src='data:text/html,<input%2520id=%2522in1%2522>'>"></iframe>
+
+<input id="out1">
+<input name="1|#out2">
diff --git a/comm/suite/components/tests/browser/browser_465215.js b/comm/suite/components/tests/browser/browser_465215.js
new file mode 100644
index 0000000000..f34bd780b0
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_465215.js
@@ -0,0 +1,39 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 465215 **/
+
+ waitForExplicitFinish();
+
+ let uniqueName = "bug 465215";
+ let uniqueValue1 = "as good as unique: " + Date.now();
+ let uniqueValue2 = "as good as unique: " + Math.random();
+
+ // set a unique value on a new, blank tab
+ let tab1 = gBrowser.addTab();
+ tab1.linkedBrowser.addEventListener("load", function testTab1LBLoad() {
+ tab1.linkedBrowser.removeEventListener("load", testTab1LBLoad, true);
+ ss.setTabValue(tab1, uniqueName, uniqueValue1);
+
+ // duplicate the tab with that value
+ let tab2 = ss.duplicateTab(window, tab1);
+ is(ss.getTabValue(tab2, uniqueName), uniqueValue1, "tab value was duplicated");
+
+ ss.setTabValue(tab2, uniqueName, uniqueValue2);
+ isnot(ss.getTabValue(tab1, uniqueName), uniqueValue2, "tab values aren't sync'd");
+
+ // overwrite the tab with the value which should remove it
+ ss.setTabState(tab1, JSON.stringify({ entries: [] }));
+ tab1.linkedBrowser.addEventListener("load", function testTab1LBLoad2() {
+ tab1.linkedBrowser.removeEventListener("load", testTab1LBLoad2, true);
+ is(ss.getTabValue(tab1, uniqueName), "", "tab value was cleared");
+
+ // clean up
+ gBrowser.removeTab(tab2);
+ gBrowser.removeTab(tab1);
+ finish();
+ }, true);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_465223.js b/comm/suite/components/tests/browser/browser_465223.js
new file mode 100644
index 0000000000..89e1e69042
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_465223.js
@@ -0,0 +1,60 @@
+/* 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/. */
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+function browserWindowsCount() {
+ let count = 0;
+ let e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ if (!e.getNext().closed)
+ ++count;
+ }
+ return count;
+}
+
+function test() {
+ /** Test for Bug 465223 **/
+ is(browserWindowsCount(), 1, "Only one browser window should be open initially");
+
+ waitForExplicitFinish();
+
+ let uniqueKey1 = "bug 465223.1";
+ let uniqueKey2 = "bug 465223.2";
+ let uniqueValue1 = "unik" + Date.now();
+ let uniqueValue2 = "pi != " + Math.random();
+
+ // open a window and set a value on it
+ let newWin = openDialog(location, "_blank", "chrome,all,dialog=no");
+ newWin.addEventListener("load", function loadListener(aEvent) {
+ newWin.removeEventListener("load", loadListener);
+
+ ss.setWindowValue(newWin, uniqueKey1, uniqueValue1);
+
+ let newState = { windows: [{ tabs:[{ entries: [] }], extData: {} }] };
+ newState.windows[0].extData[uniqueKey2] = uniqueValue2;
+ ss.setWindowState(newWin, JSON.stringify(newState), false);
+
+ is(newWin.gBrowser.tabContainer.childNodes.length, 2,
+ "original tab wasn't overwritten");
+ is(ss.getWindowValue(newWin, uniqueKey1), uniqueValue1,
+ "window value wasn't overwritten when the tabs weren't");
+ is(ss.getWindowValue(newWin, uniqueKey2), uniqueValue2,
+ "new window value was correctly added");
+
+ newState.windows[0].extData[uniqueKey2] = uniqueValue1;
+ ss.setWindowState(newWin, JSON.stringify(newState), true);
+
+ is(newWin.gBrowser.tabContainer.childNodes.length, 1,
+ "original tabs were overwritten");
+ is(ss.getWindowValue(newWin, uniqueKey1), "",
+ "window value was cleared");
+ is(ss.getWindowValue(newWin, uniqueKey2), uniqueValue1,
+ "window value was correctly overwritten");
+
+ // clean up
+ newWin.close();
+ is(browserWindowsCount(), 1, "Only one browser window should be open eventually");
+ finish();
+ });
+}
diff --git a/comm/suite/components/tests/browser/browser_466937.js b/comm/suite/components/tests/browser/browser_466937.js
new file mode 100644
index 0000000000..8d34f65aeb
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_466937.js
@@ -0,0 +1,43 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 466937 **/
+
+ waitForExplicitFinish();
+
+ var file = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ file.append("466937_test.file");
+ let testPath = file.path;
+
+ let testURL = "http://mochi.test:8888/browser/" +
+ "suite/common/tests/browser/browser_466937_sample.html";
+
+ let tab = getBrowser().addTab(testURL);
+ let window = tab.ownerDocument.defaultView;
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", testTabLBLoad, true);
+ let doc = tab.linkedBrowser.contentDocument;
+ doc.getElementById("reverse_thief").value = "/home/user/secret2";
+ doc.getElementById("bystander").value = testPath;
+
+ let tab2 = ss.duplicateTab(window,tab);
+ tab2.linkedBrowser.addEventListener("load", function testTab2LBLoad(aEvent) {
+ tab2.linkedBrowser.removeEventListener("load", testTab2LBLoad, true);
+ doc = tab2.linkedBrowser.contentDocument;
+ is(doc.getElementById("thief").value, "",
+ "file path wasn't set to text field value");
+ is(doc.getElementById("reverse_thief").value, "",
+ "text field value wasn't set to full file path");
+ is(doc.getElementById("bystander").value, testPath,
+ "normal case: file path was correctly preserved");
+
+ // clean up
+ gBrowser.removeTab(tab2);
+ gBrowser.removeTab(tab);
+
+ finish();
+ }, true);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_466937_sample.html b/comm/suite/components/tests/browser/browser_466937_sample.html
new file mode 100644
index 0000000000..f876719987
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_466937_sample.html
@@ -0,0 +1,21 @@
+<!-- Testcase originally by <moz_bug_r_a4@yahoo.com> -->
+
+<!DOCTYPE html>
+<title>Test for bug 466937</title>
+
+<input id="thief" value="/home/user/secret">
+<input type="file" id="reverse_thief">
+<input type="file" id="bystander">
+
+<script>
+ window.addEventListener("DOMContentLoaded", function windowDOMContentLoaded() {
+ window.removeEventListener("DOMContentLoaded", windowDOMContentLoaded);
+ if (!document.location.hash) {
+ document.location.hash = "#ready";
+ }
+ else {
+ document.getElementById("thief").type = "file";
+ document.getElementById("reverse_thief").type = "text";
+ }
+ });
+</script>
diff --git a/comm/suite/components/tests/browser/browser_477657.js b/comm/suite/components/tests/browser/browser_477657.js
new file mode 100644
index 0000000000..23683a8b12
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_477657.js
@@ -0,0 +1,85 @@
+/* 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/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {AppConstants} = ChromeUtils.import(
+ "resource://gre/modules/AppConstants.jsm"
+);
+
+function browserWindowsCount() {
+ let count = 0;
+ let e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ if (!e.getNext().closed)
+ ++count;
+ }
+ return count;
+}
+
+function test() {
+ /** Test for Bug 477657 **/
+ is(browserWindowsCount(), 1, "Only one browser window should be open initially");
+
+ // Test fails randomly on OS X (bug 482975)
+ if ("nsILocalFileMac" in Ci)
+ return;
+
+ waitForExplicitFinish();
+
+ let newWin = openDialog(location, "_blank", "chrome,all,dialog=no");
+ newWin.addEventListener("load", function loadListener(aEvent) {
+ newWin.removeEventListener("load", loadListener);
+
+ let newState = { windows: [{
+ tabs: [{ entries: [] }],
+ _closedTabs: [{
+ state: { entries: [{ url: "about:" }]},
+ title: "About:"
+ }],
+ sizemode: "maximized"
+ }] };
+
+ let uniqueKey = "bug 477657";
+ let uniqueValue = "unik" + Date.now();
+
+ ss.setWindowValue(newWin, uniqueKey, uniqueValue);
+ is(ss.getWindowValue(newWin, uniqueKey), uniqueValue,
+ "window value was set before the window was overwritten");
+ ss.setWindowState(newWin, JSON.stringify(newState), true);
+
+ // use setTimeout(..., 0) to mirror sss_restoreWindowFeatures
+ setTimeout(function() {
+ is(ss.getWindowValue(newWin, uniqueKey), "",
+ "window value was implicitly cleared");
+
+ is(newWin.windowState, newWin.STATE_MAXIMIZED,
+ "the window was maximized");
+
+ is(JSON.parse(ss.getClosedTabData(newWin)).length, 1,
+ "the closed tab was added before the window was overwritten");
+ delete newState.windows[0]._closedTabs;
+ delete newState.windows[0].sizemode;
+ ss.setWindowState(newWin, JSON.stringify(newState), true);
+
+ setTimeout(function() {
+ is(JSON.parse(ss.getClosedTabData(newWin)).length, 0,
+ "closed tabs were implicitly cleared");
+
+ is(newWin.windowState, newWin.STATE_MAXIMIZED,
+ "the window remains maximized");
+ newState.windows[0].sizemode = "normal";
+ ss.setWindowState(newWin, JSON.stringify(newState), true);
+
+ setTimeout(function() {
+ isnot(newWin.windowState, newWin.STATE_MAXIMIZED,
+ "the window was explicitly unmaximized");
+
+ newWin.close();
+ is(browserWindowsCount(), 1, "Only one browser window should be open eventually");
+ finish();
+ }, 0);
+ }, 0);
+ }, 0);
+ });
+}
diff --git a/comm/suite/components/tests/browser/browser_480893.js b/comm/suite/components/tests/browser/browser_480893.js
new file mode 100644
index 0000000000..41de93c8f1
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_480893.js
@@ -0,0 +1,58 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 480893 **/
+
+ waitForExplicitFinish();
+
+ // Test that starting a new session loads a blank page if Firefox is
+ // configured to display a blank page at startup (browser.startup.page = 0)
+ Services.prefs.setIntPref("browser.startup.page", 0);
+ let tab = getBrowser().addTab("about:sessionrestore");
+ getBrowser().selectedTab = tab;
+ let browser = tab.linkedBrowser;
+ browser.addEventListener("load", function testBrowserLoad(aEvent) {
+ browser.removeEventListener("load", testBrowserLoad, true);
+ let doc = browser.contentDocument;
+
+ // click on the "Start New Session" button after about:sessionrestore is loaded
+ doc.getElementById("errorCancel").click();
+ browser.addEventListener("load", function testBrowserLoad2(aEvent) {
+ browser.removeEventListener("load", testBrowserLoad2, true);
+ let doc = browser.contentDocument;
+
+ is(doc.URL, "about:blank", "loaded page is about:blank");
+
+ // Test that starting a new session loads the homepage (set to http://mochi.test:8888)
+ // if Firefox is configured to display a homepage at startup (browser.startup.page = 1)
+ let homepage = "http://mochi.test:8888/";
+ Services.prefs.setCharPref("browser.startup.homepage", homepage);
+ Services.prefs.setIntPref("browser.startup.page", 1);
+ getBrowser().loadURI("about:sessionrestore");
+ browser.addEventListener("load", function testBrowserLoad3(aEvent) {
+ browser.removeEventListener("load", testBrowserLoad3, true);
+ let doc = browser.contentDocument;
+
+ // click on the "Start New Session" button after about:sessionrestore is loaded
+ doc.getElementById("errorCancel").click();
+ browser.addEventListener("load", function testBrowserLoad4(aEvent) {
+ browser.removeEventListener("load", testBrowserLoad4, true);
+ let doc = browser.contentDocument;
+
+ is(doc.URL, homepage, "loaded page is the homepage");
+
+ // close tab, restore default values and finish the test
+ getBrowser().removeTab(tab);
+ // we need this if-statement because if there is no user set value,
+ // clearUserPref throws a uncatched exception and finish is not called
+ if (Services.prefs.prefHasUserValue("browser.startup.page"))
+ Services.prefs.clearUserPref("browser.startup.page");
+ Services.prefs.clearUserPref("browser.startup.homepage");
+ finish();
+ }, true);
+ }, true);
+ }, true);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_483330.js b/comm/suite/components/tests/browser/browser_483330.js
new file mode 100644
index 0000000000..3e650488b7
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_483330.js
@@ -0,0 +1,37 @@
+function test() {
+ /** Test for Bug 483330 **/
+
+ waitForExplicitFinish();
+
+ let tab = getBrowser().addTab();
+ getBrowser().selectedTab = tab;
+
+ let browser = tab.linkedBrowser;
+ browser.addEventListener("load", function loadListener(e) {
+ browser.removeEventListener("load", loadListener, true);
+
+ // Scroll the content document
+ browser.contentWindow.scrollTo(1100, 1200);
+ is(browser.contentWindow.scrollX, 1100, "scrolled horizontally");
+ is(browser.contentWindow.scrollY, 1200, "scrolled vertically");
+
+ getBrowser().removeTab(tab);
+
+ let newTab = ss.undoCloseTab(window, 0);
+ newTab.addEventListener("SSTabRestored", function tabRestored(e) {
+ newTab.removeEventListener("SSTabRestored", tabRestored, true);
+
+ let newBrowser = newTab.linkedBrowser;
+
+ // check that the scroll position was restored
+ is(newBrowser.contentWindow.scrollX, 1100, "still scrolled horizontally");
+ is(newBrowser.contentWindow.scrollY, 1200, "still scrolled vertically");
+
+ getBrowser().removeTab(newTab);
+
+ finish();
+ }, true);
+ }, true);
+
+ browser.loadURI("data:text/html,<body style='width: 100000px; height: 100000px;'><p>top</p></body>");
+}
diff --git a/comm/suite/components/tests/browser/browser_485482.js b/comm/suite/components/tests/browser/browser_485482.js
new file mode 100644
index 0000000000..6e4573f609
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_485482.js
@@ -0,0 +1,36 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 485482, ported by Bug 487922 **/
+
+ waitForExplicitFinish();
+
+ let uniqueValue = Math.random();
+
+ let rootDir = getRootDirectory(gTestPath);
+ let testURL = rootDir + "browser_485482_sample.html";
+ let tab = getBrowser().addTab(testURL);
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", testTabLBLoad, true);
+ let doc = tab.linkedBrowser.contentDocument;
+ doc.querySelector("input[type=text]").value = uniqueValue;
+ doc.querySelector("input[type=checkbox]").checked = true;
+
+ let tab2 = ss.duplicateTab(window, tab);
+ tab2.linkedBrowser.addEventListener("load", function testTab2LBLoad(aEvent) {
+ tab2.linkedBrowser.removeEventListener("load", testTab2LBLoad, true);
+ doc = tab2.linkedBrowser.contentDocument;
+ is(doc.querySelector("input[type=text]").value, uniqueValue,
+ "generated XPath expression was valid");
+ ok(doc.querySelector("input[type=checkbox]").checked,
+ "generated XPath expression was valid");
+
+ // clean up
+ getBrowser().removeTab(tab2);
+ getBrowser().removeTab(tab);
+ finish();
+ }, true);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_485482_sample.html b/comm/suite/components/tests/browser/browser_485482_sample.html
new file mode 100644
index 0000000000..c2097b5930
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_485482_sample.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<title>Test for bug 485482</title>
+
+<bad=name>
+ <input type="text">
+</bad=name>
+
+<worse=name>
+ <l0c@l+na~e"'§>
+ <input type="checkbox" name="check"> Check
+ </l0c@l+na~e"'§>
+</worse=name>
diff --git a/comm/suite/components/tests/browser/browser_490040.js b/comm/suite/components/tests/browser/browser_490040.js
new file mode 100644
index 0000000000..91687058f6
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_490040.js
@@ -0,0 +1,139 @@
+/* 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/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+function browserWindowsCount() {
+ let count = 0;
+ let e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ if (!e.getNext().closed)
+ ++count;
+ }
+ return count;
+}
+
+function test() {
+ /** Test for Bug 490040, ported by Bug 511640 **/
+ is(browserWindowsCount(), 1, "Only one browser window should be open initially");
+
+ waitForExplicitFinish();
+
+ function testWithState(aState) {
+ // Ensure we can store the window if needed.
+ let curClosedWindowCount = ss.getClosedWindowCount();
+ Services.prefs.setIntPref("browser.sessionstore.max_windows_undo",
+ curClosedWindowCount + 1);
+
+ var origWin;
+ function windowObserver(aSubject, aTopic, aData) {
+ let theWin = aSubject.QueryInterface(Ci.nsIDOMWindow);
+ if (origWin && theWin != origWin)
+ return;
+
+ switch (aTopic) {
+ case "domwindowopened":
+ origWin = theWin;
+ theWin.addEventListener("load", function testTheWinLoad() {
+ theWin.removeEventListener("load", testTheWinLoad);
+ executeSoon(function () {
+ // Close the window as soon as the first tab loads, or
+ // immediately if there are no tabs.
+ if (aState.windowState.windows[0].tabs[0].entries.length) {
+ theWin.gBrowser.addEventListener("load",
+ function testTheWinLoad2() {
+ theWin.gBrowser.removeEventListener("load", testTheWinLoad2,
+ true);
+ theWin.close();
+ }, true);
+ } else {
+ executeSoon(function () {
+ theWin.close();
+ });
+ }
+ ss.setWindowState(theWin, JSON.stringify(aState.windowState),
+ true);
+ });
+ });
+ break;
+
+ case "domwindowclosed":
+ Services.ww.unregisterNotification(windowObserver);
+ // Use executeSoon to ensure this happens after SS observer.
+ executeSoon(function () {
+ is(ss.getClosedWindowCount(),
+ curClosedWindowCount + (aState.shouldBeAdded ? 1 : 0),
+ "That window should " + (aState.shouldBeAdded ? "" : "not ") +
+ "be restorable");
+ executeSoon(runNextTest);
+ });
+ break;
+ }
+ }
+ Services.ww.registerNotification(windowObserver);
+ Services.ww.openWindow(null,
+ location,
+ "_blank",
+ "chrome,all,dialog=no",
+ null);
+ }
+
+ // Only windows with open tabs are restorable. Windows where a lone tab is
+ // detached may have _closedTabs, but is left with just an empty tab.
+ let states = [
+ {
+ shouldBeAdded: true,
+ windowState: {
+ windows: [{
+ tabs: [{ entries: [{ url: "http://example.com", title: "example.com" }] }],
+ selected: 1,
+ _closedTabs: []
+ }]
+ }
+ },
+ {
+ shouldBeAdded: false,
+ windowState: {
+ windows: [{
+ tabs: [{ entries: [] }],
+ _closedTabs: []
+ }]
+ }
+ },
+ {
+ shouldBeAdded: false,
+ windowState: {
+ windows: [{
+ tabs: [{ entries: [] }],
+ _closedTabs: [{ state: { entries: [{ url: "http://example.com", index: 1 }] } }]
+ }]
+ }
+ },
+ {
+ shouldBeAdded: false,
+ windowState: {
+ windows: [{
+ tabs: [{ entries: [] }],
+ _closedTabs: [],
+ extData: { keyname: "pi != " + Math.random() }
+ }]
+ }
+ }
+ ];
+
+ function runNextTest() {
+ if (states.length) {
+ let state = states.shift();
+ testWithState(state);
+ }
+ else {
+ if (Services.prefs.prefHasUserValue("browser.sessionstore.max_windows_undo"))
+ Services.prefs.clearUserPref("browser.sessionstore.max_windows_undo");
+ is(browserWindowsCount(), 1, "Only one browser window should be open eventually");
+ finish();
+ }
+ }
+ runNextTest();
+}
+
diff --git a/comm/suite/components/tests/browser/browser_491168.js b/comm/suite/components/tests/browser/browser_491168.js
new file mode 100644
index 0000000000..82ed998e99
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_491168.js
@@ -0,0 +1,64 @@
+/* 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/. */
+
+function browserWindowsCount() {
+ let count = 0;
+ let e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ if (!e.getNext().closed)
+ ++count;
+ }
+ return count;
+}
+
+function test() {
+ // make sure we use sessionstore for undoClosetab
+ Services.prefs.setIntPref("browser.tabs.max_tabs_undo", 0);
+
+ /** Test for Bug 491168, ported by Bug 524369 **/
+ is(browserWindowsCount(), 1, "Only one browser window should be open initially");
+
+ waitForExplicitFinish();
+
+ const REFERRER1 = "http://example.org/?" + Date.now();
+ const REFERRER2 = "http://example.org/?" + Math.random();
+
+ let tab = getBrowser().addTab();
+ getBrowser().selectedTab = tab;
+
+ let browser = tab.linkedBrowser;
+ browser.addEventListener("load", function testBrowserLoad() {
+ browser.removeEventListener("load", testBrowserLoad, true);
+
+ let tabState = JSON.parse(ss.getTabState(tab));
+ is(tabState.entries[0].referrer, REFERRER1,
+ "Referrer retrieved via getTabState matches referrer set via loadURI.");
+
+ tabState.entries[0].referrer = REFERRER2;
+ ss.setTabState(tab, JSON.stringify(tabState));
+
+ tab.addEventListener("SSTabRestored", function testBrowserTabRestored() {
+ tab.removeEventListener("SSTabRestored", testBrowserTabRestored, true);
+ is(window.content.document.referrer, REFERRER2, "document.referrer matches referrer set via setTabState.");
+
+ getBrowser().removeTab(tab);
+ let newTab = ss.undoCloseTab(window, 0);
+ newTab.addEventListener("SSTabRestored", function testBrowserNewTabRest() {
+ newTab.removeEventListener("SSTabRestored", testBrowserNewTabRest, true);
+
+ is(window.content.document.referrer, REFERRER2, "document.referrer is still correct after closing and reopening the tab.");
+ getBrowser().removeTab(newTab);
+
+ is(browserWindowsCount(), 1, "Only one browser window should be open eventually");
+ // clean up
+ if (Services.prefs.prefHasUserValue("browser.tabs.max_tabs_undo"))
+ Services.prefs.clearUserPref("browser.tabs.max_tabs_undo");
+ finish();
+ }, true);
+ }, true);
+ },true);
+
+ let referrerURI = Services.io.newURI(REFERRER1);
+ browser.loadURI("http://example.org", referrerURI, null);
+}
diff --git a/comm/suite/components/tests/browser/browser_491577.js b/comm/suite/components/tests/browser/browser_491577.js
new file mode 100644
index 0000000000..f65590abaf
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_491577.js
@@ -0,0 +1,119 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 491577 **/
+
+ // test setup
+ waitForExplicitFinish();
+
+ const REMEMBER = Date.now(), FORGET = Math.random();
+ let test_state = {
+ windows: [ { tabs: [{ entries: [{ url: "http://example.com/", triggeringPrincipal_base64 }] }], selected: 1 } ],
+ _closedWindows: [
+ // _closedWindows[0]
+ {
+ tabs: [
+ { entries: [{ url: "http://example.com/", triggeringPrincipal_base64, title: "title" }] },
+ { entries: [{ url: "http://mozilla.org/", triggeringPrincipal_base64, title: "title" }] }
+ ],
+ selected: 2,
+ title: FORGET,
+ _closedTabs: []
+ },
+ // _closedWindows[1]
+ {
+ tabs: [
+ { entries: [{ url: "http://mozilla.org/", triggeringPrincipal_base64, title: "title" }] },
+ { entries: [{ url: "http://example.com/", triggeringPrincipal_base64, title: "title" }] },
+ { entries: [{ url: "http://mozilla.org/", triggeringPrincipal_base64, title: "title" }] },
+ ],
+ selected: 3,
+ title: REMEMBER,
+ _closedTabs: []
+ },
+ // _closedWindows[2]
+ {
+ tabs: [
+ { entries: [{ url: "http://example.com/", triggeringPrincipal_base64, title: "title" }] }
+ ],
+ selected: 1,
+ title: FORGET,
+ _closedTabs: [
+ {
+ state: {
+ entries: [
+ { url: "http://mozilla.org/", triggeringPrincipal_base64, title: "title" },
+ { url: "http://mozilla.org/again", triggeringPrincipal_base64, title: "title" }
+ ]
+ },
+ pos: 1,
+ title: "title"
+ },
+ {
+ state: {
+ entries: [
+ { url: "http://example.com", triggeringPrincipal_base64, title: "title" }
+ ]
+ },
+ title: "title"
+ }
+ ]
+ }
+ ]
+ };
+ let remember_count = 1;
+
+ function countByTitle(aClosedWindowList, aTitle) {
+ return aClosedWindowList.filter(aData => aData.title == aTitle).length;
+ }
+
+ function testForError(aFunction) {
+ try {
+ aFunction();
+ return false;
+ } catch (ex) {
+ return ex.name == "NS_ERROR_ILLEGAL_VALUE";
+ }
+ }
+
+ // open a window and add the above closed window list
+ let newWin = openDialog(location, "_blank", "chrome,all,dialog=no");
+ promiseWindowLoaded(newWin).then(() => {
+ gPrefService.setIntPref("browser.sessionstore.max_windows_undo",
+ test_state._closedWindows.length);
+ ss.setWindowState(newWin, JSON.stringify(test_state), true);
+
+ let closedWindows = JSON.parse(ss.getClosedWindowData());
+ is(closedWindows.length, test_state._closedWindows.length,
+ "Closed window list has the expected length");
+ is(countByTitle(closedWindows, FORGET),
+ test_state._closedWindows.length - remember_count,
+ "The correct amount of windows are to be forgotten");
+ is(countByTitle(closedWindows, REMEMBER), remember_count,
+ "Everything is set up.");
+
+ // all of the following calls with illegal arguments should throw NS_ERROR_ILLEGAL_VALUE
+ ok(testForError(() => ss.forgetClosedWindow(-1)),
+ "Invalid window for forgetClosedWindow throws");
+ ok(testForError(() => ss.forgetClosedWindow(test_state._closedWindows.length + 1)),
+ "Invalid window for forgetClosedWindow throws");
+
+ // Remove third window, then first window
+ ss.forgetClosedWindow(2);
+ ss.forgetClosedWindow(null);
+
+ closedWindows = JSON.parse(ss.getClosedWindowData());
+ is(closedWindows.length, remember_count,
+ "The correct amount of windows were removed");
+ is(countByTitle(closedWindows, FORGET), 0,
+ "All windows specifically forgotten were indeed removed");
+ is(countByTitle(closedWindows, REMEMBER), remember_count,
+ "... and windows not specifically forgetten weren't.");
+
+ // clean up
+ gPrefService.clearUserPref("browser.sessionstore.max_windows_undo");
+ BrowserTestUtils.closeWindow(newWin).then(finish);
+ });
+}
diff --git a/comm/suite/components/tests/browser/browser_493467.js b/comm/suite/components/tests/browser/browser_493467.js
new file mode 100644
index 0000000000..1b8f5f78d8
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_493467.js
@@ -0,0 +1,48 @@
+/* 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/. */
+
+function browserWindowsCount() {
+ let count = 0;
+ let e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ if (!e.getNext().closed)
+ ++count;
+ }
+ return count;
+}
+
+function test() {
+ /** Test for Bug 493467, ported by Bug 524365 **/
+
+ is(browserWindowsCount(), 1, "Only one browser window should be open initially");
+
+ let tab = getBrowser().addTab();
+ tab.linkedBrowser.stop();
+ let tabState = JSON.parse(ss.getTabState(tab));
+ is(tabState.disallow || "", "", "Everything is allowed per default");
+
+ // collect all permissions that can be set on a docShell (i.e. all
+ // attributes starting with "allow" such as "allowJavascript") and
+ // disallow them all, as SessionStore only remembers disallowed ones
+ let permissions = [];
+ let docShell = tab.linkedBrowser.docShell;
+ for (let attribute in docShell) {
+ if (/^allow([A-Z].*)/.test(attribute)) {
+ permissions.push(RegExp.$1);
+ docShell[attribute] = false;
+ }
+ }
+
+ // make sure that all available permissions have been remembered
+ tabState = JSON.parse(ss.getTabState(tab));
+ let disallow = tabState.disallow.split(",");
+ permissions.forEach(function(aName) {
+ ok(disallow.includes(aName), "Saved state of allow" + aName);
+ });
+ // IF A TEST FAILS, please add the missing permission's name (without the
+ // leading "allow") to nsSessionStore.js's CAPABILITIES array. Thanks.
+
+ getBrowser().removeTab(tab);
+ is(browserWindowsCount(), 1, "Only one browser window should be open eventually");
+}
diff --git a/comm/suite/components/tests/browser/browser_500328.js b/comm/suite/components/tests/browser/browser_500328.js
new file mode 100644
index 0000000000..2286a5f6c3
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_500328.js
@@ -0,0 +1,115 @@
+/* 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/. */
+
+function checkState(tab) {
+ // 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 popStateCount = 0;
+
+ tab.linkedBrowser.addEventListener('popstate', function checkStateTabPopState(aEvent) {
+ let contentWindow = tab.linkedBrowser.contentWindow;
+ if (popStateCount == 0) {
+ popStateCount++;
+
+ is(tab.linkedBrowser.contentWindow.testState, 'foo',
+ 'testState after going back');
+
+ ok(aEvent.state, "Event should have a state property.");
+ is(JSON.stringify(tab.linkedBrowser.contentWindow.history.state), JSON.stringify({obj1:1}),
+ "first popstate object.");
+
+ // Add a node with id "new-elem" to the document.
+ let doc = contentWindow.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);
+
+ contentWindow.history.forward();
+ }
+ else if (popStateCount == 1) {
+ popStateCount++;
+ is(aEvent.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 = contentWindow.document;
+ let newElem = doc.getElementById("new-elem");
+ ok(newElem, "doc should contain new-elem.");
+ newElem.remove();
+ ok(!doc.getElementById("new-elem"), "new-elem should be removed.");
+
+ // Clean up after ourselves and finish the test.
+ tab.linkedBrowser.removeEventListener("popstate", checkStateTabPopState,
+ true);
+ getBrowser().removeTab(tab);
+ finish();
+ }
+ }, true);
+
+ // 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.
+ tab.linkedBrowser.contentWindow.testState = 'foo';
+
+ // Now go back. This should trigger the popstate event handler above.
+ tab.linkedBrowser.contentWindow.history.back();
+}
+
+function test() {
+ // Tests session restore functionality of history.pushState and
+ // history.replaceState(). (Bug 500328)
+
+ waitForExplicitFinish();
+
+ // 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 tab = getBrowser().addTab("about:blank");
+ let tabBrowser = tab.linkedBrowser;
+
+ tabBrowser.addEventListener("load", function testTabBrowserLoad(aEvent) {
+ tabBrowser.removeEventListener("load", testTabBrowserLoad, true);
+
+ tabBrowser.loadURI("http://example.com", null, null);
+
+ tabBrowser.addEventListener("load", function testTabBrowserLoad2(aEvent) {
+ tabBrowser.removeEventListener("load", testTabBrowserLoad2, true);
+
+ // 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
+ let contentWindow = tab.linkedBrowser.contentWindow;
+ let history = contentWindow.history;
+ history.pushState({obj1:1}, "title-obj1");
+ history.pushState({obj2:2}, "title-obj2", "?page2");
+ history.replaceState({obj3:/^a$/}, "title-obj3");
+
+ let state = ss.getTabState(tab);
+ getBrowser().removeTab(tab);
+
+ // 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.
+ let tab2 = getBrowser().addTab("about:blank");
+ ss.setTabState(tab2, state, true);
+
+ // Run checkState() once the tab finishes loading its restored state.
+ tab2.linkedBrowser.addEventListener("load", function testTBTab2LBLoad() {
+ tab2.linkedBrowser.removeEventListener("load", testTBTab2LBLoad, true);
+ SimpleTest.executeSoon(function() {
+ checkState(tab2);
+ });
+ }, true);
+
+ }, true);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_514751.js b/comm/suite/components/tests/browser/browser_514751.js
new file mode 100644
index 0000000000..2290814aa9
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_514751.js
@@ -0,0 +1,59 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 509315 (Wallpaper) **/
+
+ waitForExplicitFinish();
+
+ let state = {
+ windows: [{
+ tabs: [{
+ entries: [
+ { url: "http://www.mozilla.org/projects/minefield/", title: "Minefield Start Page" },
+ {}
+ ]
+ }]
+ }]
+ };
+
+ let windowObserver = {
+ observe: function(aSubject, aTopic, aData) {
+ let theWin = aSubject.QueryInterface(Ci.nsIDOMWindow);
+
+ switch(aTopic) {
+ case "domwindowopened":
+ theWin.addEventListener("load", function testTheWinLoad() {
+ theWin.removeEventListener("load", testTheWinLoad);
+ executeSoon(function() {
+ var gotError = false;
+ try {
+ ss.setWindowState(theWin, JSON.stringify(state), true);
+ } catch (e) {
+ if (/NS_ERROR_MALFORMED_URI/.test(e))
+ gotError = true;
+ }
+ ok(!gotError, "Didn't get a malformed URI error.");
+ executeSoon(function() {
+ theWin.close();
+ });
+ });
+ });
+ break;
+
+ case "domwindowclosed":
+ Services.ww.unregisterNotification(this);
+ finish();
+ break;
+ }
+ }
+ }
+ Services.ww.registerNotification(windowObserver);
+ Services.ww.openWindow(null,
+ location,
+ "_blank",
+ "chrome,all,dialog=no",
+ null);
+
+}
diff --git a/comm/suite/components/tests/browser/browser_522545.js b/comm/suite/components/tests/browser/browser_522545.js
new file mode 100644
index 0000000000..9088c88c81
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_522545.js
@@ -0,0 +1,280 @@
+/* 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/. */
+
+function browserWindowsCount() {
+ let count = 0;
+ let e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ if (!e.getNext().closed)
+ ++count;
+ }
+ return count;
+}
+
+function test() {
+ /** Test for Bug 522545 **/
+ is(browserWindowsCount(), 1, "Only one browser window should be open initially");
+
+ waitForExplicitFinish();
+ requestLongerTimeout(2);
+
+ // This tests the following use case:
+ // User opens a new tab which gets focus. The user types something into the
+ // address bar, then crashes or quits.
+ function test_newTabFocused() {
+ let state = {
+ windows: [{
+ tabs: [
+ { entries: [{ url: "about:mozilla" }] },
+ { entries: [], userTypedValue: "example.com", userTypedClear: 0 }
+ ],
+ selected: 2
+ }]
+ };
+
+ waitForBrowserState(state, function() {
+ let browser = getBrowser().selectedBrowser;
+ is(browser.currentURI.spec, "about:blank",
+ "No history entries still sets currentURI to about:blank");
+ is(browser.userTypedValue, "example.com",
+ "userTypedValue was correctly restored");
+ is(browser.userTypedClear, 0,
+ "userTypeClear restored as expected");
+ is(gURLBar.value, "example.com",
+ "Address bar's value correctly restored");
+ // Change tabs to make sure address bar value gets updated
+ getBrowser().selectedTab = getBrowser().tabContainer.getItemAtIndex(0);
+ is(gURLBar.value, "about:mozilla",
+ "Address bar's value correctly updated");
+ runNextTest();
+ });
+ }
+
+ // This tests the following use case:
+ // User opens a new tab which gets focus. The user types something into the
+ // address bar, switches back to the first tab, then crashes or quits.
+ function test_newTabNotFocused() {
+ let state = {
+ windows: [{
+ tabs: [
+ { entries: [{ url: "about:mozilla" }] },
+ { entries: [], userTypedValue: "example.org", userTypedClear: 0 }
+ ],
+ selected: 1
+ }]
+ };
+
+ waitForBrowserState(state, function() {
+ let browser = getBrowser().getBrowserAtIndex(1);
+ is(browser.currentURI.spec, "about:blank",
+ "No history entries still sets currentURI to about:blank");
+ is(browser.userTypedValue, "example.org",
+ "userTypedValue was correctly restored");
+ is(browser.userTypedClear, 0,
+ "userTypeClear restored as expected");
+ is(gURLBar.value, "about:mozilla",
+ "Address bar's value correctly restored");
+ // Change tabs to make sure address bar value gets updated
+ getBrowser().selectedTab = getBrowser().tabContainer.getItemAtIndex(1);
+ is(gURLBar.value, "example.org",
+ "Address bar's value correctly updated");
+ runNextTest();
+ });
+ }
+
+ // This tests the following use case:
+ // User is in a tab with session history, then types something in the
+ // address bar, then crashes or quits.
+ function test_existingSHEnd_noClear() {
+ let state = {
+ windows: [{
+ tabs: [{
+ entries: [{ url: "about:mozilla" }, { url: "about:config" }],
+ index: 2,
+ userTypedValue: "example.com",
+ userTypedClear: 0
+ }]
+ }]
+ };
+
+ waitForBrowserState(state, function() {
+ let browser = getBrowser().selectedBrowser;
+ is(browser.currentURI.spec, "about:config",
+ "browser.currentURI set to current entry in SH");
+ is(browser.userTypedValue, "example.com",
+ "userTypedValue was correctly restored");
+ is(browser.userTypedClear, 0,
+ "userTypeClear restored as expected");
+ is(gURLBar.value, "example.com",
+ "Address bar's value correctly restored to userTypedValue");
+ runNextTest();
+ });
+ }
+
+ // This tests the following use case:
+ // User is in a tab with session history, presses back at some point, then
+ // types something in the address bar, then crashes or quits.
+ function test_existingSHMiddle_noClear() {
+ let state = {
+ windows: [{
+ tabs: [{
+ entries: [{ url: "about:mozilla" }, { url: "about:config" }],
+ index: 1,
+ userTypedValue: "example.org",
+ userTypedClear: 0
+ }]
+ }]
+ };
+
+ waitForBrowserState(state, function() {
+ let browser = getBrowser().selectedBrowser;
+ is(browser.currentURI.spec, "about:mozilla",
+ "browser.currentURI set to current entry in SH");
+ is(browser.userTypedValue, "example.org",
+ "userTypedValue was correctly restored");
+ is(browser.userTypedClear, 0,
+ "userTypeClear restored as expected");
+ is(gURLBar.value, "example.org",
+ "Address bar's value correctly restored to userTypedValue");
+ runNextTest();
+ });
+ }
+
+ // This test simulates lots of tabs opening at once and then quitting/crashing.
+ function test_getBrowserState_lotsOfTabsOpening() {
+ getBrowser().stop();
+
+ let uris = [];
+ for (let i = 0; i < 25; i++)
+ uris.push("http://example.com/" + i);
+
+ // We're waiting for the first location change, which should indicate
+ // one of the tabs has loaded and the others haven't. So one should
+ // be in a non-userTypedValue case, while others should still have
+ // userTypedValue and userTypedClear set.
+ getBrowser().addTabsProgressListener({
+ onLocationChange: function (aBrowser) {
+ if (uris.includes(aBrowser.currentURI.spec)) {
+ getBrowser().removeTabsProgressListener(this);
+ firstLocationChange();
+ }
+ }
+ });
+
+ function firstLocationChange() {
+ let state = JSON.parse(ss.getBrowserState());
+ let hasUTV = state.windows[0].tabs.some(function(aTab) {
+ return aTab.userTypedValue && aTab.userTypedClear && !aTab.entries.length;
+ });
+
+ ok(hasUTV, "At least one tab has a userTypedValue with userTypedClear with no loaded URL");
+
+ getBrowser().addEventListener("load", firstLoad, true);
+ }
+
+ function firstLoad() {
+ getBrowser().removeEventListener("load", firstLoad, true);
+
+ let state = JSON.parse(ss.getBrowserState());
+ let hasSH = state.windows[0].tabs.some(function(aTab) {
+ return !("userTypedValue" in aTab) && aTab.entries[0].url;
+ });
+
+ ok(hasSH, "At least one tab has its entry in SH");
+
+ runNextTest();
+ }
+
+ getBrowser().loadTabs(uris);
+ }
+
+ // This simulates setting a userTypedValue and ensures that just typing in the
+ // URL bar doesn't set userTypedClear as well.
+ function test_getBrowserState_userTypedValue() {
+ let state = {
+ windows: [{
+ tabs: [{ entries: [] }]
+ }]
+ };
+
+ waitForBrowserState(state, function() {
+ let browser = getBrowser().selectedBrowser;
+ // Make sure this tab isn't loading and state is clear before we test.
+ is(browser.userTypedValue, null, "userTypedValue is empty to start");
+ is(browser.userTypedClear, 0, "userTypedClear is 0 to start");
+
+ gURLBar.value = "example.org";
+ let event = document.createEvent("Events");
+ event.initEvent("input", true, false);
+ gURLBar.dispatchEvent(event);
+
+ executeSoon(function() {
+ is(browser.userTypedValue, "example.org",
+ "userTypedValue was set when changing gURLBar.value");
+ is(browser.userTypedClear, 0,
+ "userTypedClear was not changed when changing gURLBar.value");
+
+ // Now make sure ss gets these values too
+ let newState = JSON.parse(ss.getBrowserState());
+ is(newState.windows[0].tabs[0].userTypedValue, "example.org",
+ "sessionstore got correct userTypedValue");
+ is(newState.windows[0].tabs[0].userTypedClear, 0,
+ "sessionstore got correct userTypedClear");
+ runNextTest();
+ });
+ });
+ }
+
+ // test_getBrowserState_lotsOfTabsOpening tested userTypedClear in a few cases,
+ // but not necessarily any that had legitimate URIs in the state of loading
+ // (eg, "http://example.com"), so this test will cover that case.
+ function test_userTypedClearLoadURI() {
+ let state = {
+ windows: [{
+ tabs: [
+ { entries: [], userTypedValue: "http://example.com", userTypedClear: 2 }
+ ]
+ }]
+ };
+
+ waitForBrowserState(state, function() {
+ let browser = gBrowser.selectedBrowser;
+ is(browser.currentURI.spec, "http://example.com/",
+ "userTypedClear=2 caused userTypedValue to be loaded");
+ is(browser.userTypedValue, null,
+ "userTypedValue was null after loading a URI");
+ is(browser.userTypedClear, 0,
+ "userTypeClear reset to 0");
+ is(gURLBar.value, "http://example.com/",
+ "Address bar's value set after loading URI");
+ runNextTest();
+ });
+ }
+
+
+ let tests = [test_newTabFocused, test_newTabNotFocused,
+ test_existingSHEnd_noClear, test_existingSHMiddle_noClear,
+ test_getBrowserState_lotsOfTabsOpening,
+ test_getBrowserState_userTypedValue, test_userTypedClearLoadURI];
+ let originalState = ss.getBrowserState();
+ let state = {
+ windows: [{
+ tabs: [{ entries: [{ url: "about:blank" }] }]
+ }]
+ };
+ function runNextTest() {
+ if (tests.length) {
+ waitForBrowserState(state, tests.shift());
+ } else {
+ ss.setBrowserState(originalState);
+ executeSoon(function () {
+ is(browserWindowsCount(), 1, "Only one browser window should be open eventually");
+ finish();
+ });
+ }
+ }
+
+ // Run the tests!
+ runNextTest();
+}
diff --git a/comm/suite/components/tests/browser/browser_524745.js b/comm/suite/components/tests/browser/browser_524745.js
new file mode 100644
index 0000000000..c14b868779
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_524745.js
@@ -0,0 +1,59 @@
+/* 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/. */
+
+function browserWindowsCount() {
+ let count = 0;
+ let e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ if (!e.getNext().closed)
+ ++count;
+ }
+ return count;
+}
+
+function test() {
+ /** Test for Bug 524745, ported by bug 558638 **/
+ is(browserWindowsCount(), 1, "Only one browser window should be open initially");
+
+ let uniqKey = "bug524745";
+ let uniqVal = Date.now();
+
+ waitForExplicitFinish();
+
+ let window_B = openDialog(location, "_blank", "chrome,all,dialog=no");
+ window_B.addEventListener("load", function testWindowBLoad(aEvent) {
+ window_B.removeEventListener("load", testWindowBLoad);
+
+ waitForFocus(function() {
+ // Add identifying information to window_B
+ ss.setWindowValue(window_B, uniqKey, uniqVal);
+ let state = JSON.parse(ss.getBrowserState());
+ let selectedWindow = state.windows[state.selectedWindow - 1];
+ is(selectedWindow.extData && selectedWindow.extData[uniqKey], uniqVal,
+ "selectedWindow is window_B");
+
+ // Now minimize window_B. The selected window shouldn't have the secret data
+ window_B.minimize();
+ waitForFocus(function() {
+ state = JSON.parse(ss.getBrowserState());
+ selectedWindow = state.windows[state.selectedWindow - 1];
+ ok(!selectedWindow.extData || !selectedWindow.extData[uniqKey],
+ "selectedWindow is not window_B after minimizing it");
+
+ // Now minimize the last open window (assumes no other tests left windows open)
+ window.minimize();
+ state = JSON.parse(ss.getBrowserState());
+ is(state.selectedWindow, 0,
+ "selectedWindow should be 0 when all windows are minimized");
+
+ // Cleanup
+ window.restore();
+ window_B.close();
+ is(browserWindowsCount(), 1,
+ "Only one browser window should be open eventually");
+ finish();
+ });
+ }, window_B);
+ });
+}
diff --git a/comm/suite/components/tests/browser/browser_526613.js b/comm/suite/components/tests/browser/browser_526613.js
new file mode 100644
index 0000000000..d7a664403f
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_526613.js
@@ -0,0 +1,71 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 526613, porting done in Bug 548211 **/
+
+ waitForExplicitFinish();
+
+ function browserWindowsCount(expected) {
+ let count = 0;
+ let e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ if (!e.getNext().closed)
+ ++count;
+ }
+ is(count, expected,
+ "number of open browser windows according to nsIWindowMediator");
+ let state = ss.getBrowserState();
+ info(state);
+ is(JSON.parse(state).windows.length, expected,
+ "number of open browser windows according to getBrowserState");
+ }
+
+ browserWindowsCount(1);
+
+ // backup old state
+ let oldState = ss.getBrowserState();
+ // create a new state for testing
+ let testState = {
+ windows: [
+ { tabs: [{ entries: [{ url: "http://example.com/" }] }], selected: 1 },
+ { tabs: [{ entries: [{ url: "about:mozilla" }] }], selected: 1 },
+ ],
+ // make sure the first window is focused, otherwise when restoring the
+ // old state, the first window is closed and the test harness gets unloaded
+ selectedWindow: 1
+ };
+
+ let pass = 1;
+ function observer(aSubject, aTopic, aData) {
+ is(aTopic, "sessionstore-browser-state-restored",
+ "The sessionstore-browser-state-restored notification was observed");
+
+ if (pass++ == 1) {
+ browserWindowsCount(2);
+
+ // let the first window be focused (see above)
+ function pollMostRecentWindow() {
+ if (Services.wm.getMostRecentWindow("navigator:browser") == window) {
+ ss.setBrowserState(oldState);
+ } else {
+ info("waiting for the current window to become active");
+ setTimeout(pollMostRecentWindow, 0);
+ window.focus(); //XXX Why is this needed?
+ }
+ }
+ pollMostRecentWindow();
+ }
+ else {
+ browserWindowsCount(1);
+ ok(!window.closed, "Restoring the old state should have left this window open");
+ Services.obs.removeObserver(observer, "sessionstore-browser-state-restored");
+ finish();
+ }
+ }
+ Services.obs.addObserver(observer, "sessionstore-browser-state-restored");
+
+ // set browser to test state
+ ss.setBrowserState(JSON.stringify(testState));
+}
diff --git a/comm/suite/components/tests/browser/browser_528776.js b/comm/suite/components/tests/browser/browser_528776.js
new file mode 100644
index 0000000000..3f316ace06
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_528776.js
@@ -0,0 +1,29 @@
+function browserWindowsCount(expected) {
+ var count = 0;
+ var e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ if (!e.getNext().closed)
+ ++count;
+ }
+ is(count, expected,
+ "number of open browser windows according to nsIWindowMediator");
+ is(JSON.parse(ss.getBrowserState()).windows.length, expected,
+ "number of open browser windows according to getBrowserState");
+}
+
+function test() {
+ /** Test for Bug 528776, ported by Bug 548228 **/
+
+ waitForExplicitFinish();
+
+ browserWindowsCount(1);
+
+ var win = openDialog(location, "", "chrome,all,dialog=no");
+ win.addEventListener("load", function loadListener() {
+ win.removeEventListener("load", loadListener);
+ browserWindowsCount(2);
+ win.close();
+ browserWindowsCount(1);
+ finish();
+ });
+}
diff --git a/comm/suite/components/tests/browser/browser_581937.js b/comm/suite/components/tests/browser/browser_581937.js
new file mode 100644
index 0000000000..5f807715c5
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_581937.js
@@ -0,0 +1,38 @@
+/* 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/. */
+
+ // Tests that an about:blank tab with no history will not be saved into
+ // session store and thus, it will not show up in Recently Closed Tabs.
+
+var tab;
+function test() {
+ waitForExplicitFinish();
+
+ Services.prefs.setIntPref("browser.sessionstore.max_tabs_undo", 0);
+ Services.prefs.setIntPref("browser.tabs.max_tabs_undo", 0);
+ Services.prefs.clearUserPref("browser.sessionstore.max_tabs_undo");
+
+ is(ss.getClosedTabCount(window), 0, "should be no closed tabs");
+
+ getBrowser().tabContainer.addEventListener("TabOpen", onTabOpen, true);
+
+ tab = getBrowser().addTab();
+}
+
+function onTabOpen(aEvent) {
+ getBrowser().tabContainer.removeEventListener("TabOpen", onTabOpen, true);
+
+ // Let other listeners react to the TabOpen event before removing the tab.
+ executeSoon(function() {
+ is(getBrowser().browsers[1].currentURI.spec, "about:blank",
+ "we will be removing an about:blank tab");
+
+ getBrowser().removeTab(tab);
+
+ is(ss.getClosedTabCount(window), 0, "should still be no closed tabs");
+
+ Services.prefs.clearUserPref("browser.tabs.max_tabs_undo");
+ executeSoon(finish);
+ });
+}
diff --git a/comm/suite/components/tests/browser/browser_586068-cascaded_restore.js b/comm/suite/components/tests/browser/browser_586068-cascaded_restore.js
new file mode 100644
index 0000000000..6389884048
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_586068-cascaded_restore.js
@@ -0,0 +1,730 @@
+/* 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/. */
+
+var stateBackup = ss.getBrowserState();
+
+const TAB_STATE_NEEDS_RESTORE = 1;
+const TAB_STATE_RESTORING = 2;
+
+function test() {
+ /** Test for Bug 586068 - Cascade page loads when restoring **/
+ waitForExplicitFinish();
+ // This test does a lot of window opening / closing and waiting for loads.
+ // In order to prevent timeouts, we'll extend the default that mochitest uses.
+ requestLongerTimeout(4);
+ runNextTest();
+}
+
+// test_reloadCascade, test_reloadReload are generated tests that are run out
+// of cycle (since they depend on current state). They're listed in [tests] here
+// so that it is obvious when they run in respect to the other tests.
+var tests = [test_cascade, test_select, test_multiWindowState,
+ test_setWindowStateNoOverwrite, test_setWindowStateOverwrite,
+ test_setBrowserStateInterrupted, test_reload,
+ /* test_reloadReload, */ test_reloadCascadeSetup,
+ /* test_reloadCascade */];
+function runNextTest() {
+ // Reset the pref
+ try {
+ Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs");
+ } catch (e) {}
+
+ // set an empty state & run the next test, or finish
+ if (tests.length) {
+ // Enumerate windows and close everything but our primary window. We can't
+ // use waitForFocus() because apparently it's buggy. See bug 599253.
+ var windowsEnum = Services.wm.getEnumerator("navigator:browser");
+ while (windowsEnum.hasMoreElements()) {
+ var currentWindow = windowsEnum.getNext();
+ if (currentWindow != window) {
+ currentWindow.close();
+ }
+ }
+
+ ss.setBrowserState(JSON.stringify({ windows: [{ tabs: [{ url: 'about:blank' }] }] }));
+ let currentTest = tests.shift();
+ info("running " + currentTest.name);
+ executeSoon(currentTest);
+ }
+ else {
+ ss.setBrowserState(stateBackup);
+ executeSoon(finish);
+ }
+}
+
+
+function test_cascade() {
+ // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time
+ Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1);
+
+ // We have our own progress listener for this test, which we'll attach before our state is set
+ let progressListener = {
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ dump("\n\nload: " + aBrowser.currentURI.spec + "\n" + JSON.stringify(countTabs()) + "\n\n");
+ if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+ test_cascade_progressCallback();
+ }
+ }
+
+ let state = { windows: [{ tabs: [
+ { entries: [{ url: "http://example.com" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com" }], extData: { "uniq": r() } }
+ ] }] };
+
+ let loadCount = 0;
+ // Since our progress listener is fired before the one in sessionstore, our
+ // expected counts look a little weird. This is because we inspect the state
+ // before sessionstore has marked the tab as finished restoring and before it
+ // starts restoring the next tab
+ let expectedCounts = [
+ [5, 1, 0],
+ [4, 1, 1],
+ [3, 1, 2],
+ [2, 1, 3],
+ [1, 1, 4],
+ [0, 1, 5]
+ ];
+
+ function test_cascade_progressCallback() {
+ loadCount++;
+ let counts = countTabs();
+ let expected = expectedCounts[loadCount - 1];
+
+ is(counts[0], expected[0], "test_cascade: load " + loadCount + " - # tabs that need to be restored");
+ is(counts[1], expected[1], "test_cascade: load " + loadCount + " - # tabs that are restoring");
+ is(counts[2], expected[2], "test_cascade: load " + loadCount + " - # tabs that has been restored");
+
+ if (loadCount < state.windows[0].tabs.length)
+ return;
+
+ window.getBrowser().removeTabsProgressListener(progressListener);
+ runNextTest();
+ }
+
+ // This progress listener will get attached before the listener in session store.
+ window.getBrowser().addTabsProgressListener(progressListener);
+ ss.setBrowserState(JSON.stringify(state));
+}
+
+
+function test_select() {
+ // Set the pref to 0 so we know exactly how many tabs should be restoring at
+ // any given time. This guarantees that a finishing load won't start another.
+ Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 0);
+
+ // We have our own progress listener for this test, which we'll attach before our state is set
+ let progressListener = {
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+ test_select_progressCallback(aBrowser);
+ }
+ }
+
+ let state = { windows: [{ tabs: [
+ { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } }
+ ], selected: 1 }] };
+
+ let loadCount = 0;
+ // expectedCounts looks a little wierd for the test case, but it works. See
+ // comment in test_cascade for an explanation
+ let expectedCounts = [
+ [5, 1, 0],
+ [4, 1, 1],
+ [3, 1, 2],
+ [2, 1, 3],
+ [1, 1, 4],
+ [0, 1, 5]
+ ];
+ let tabOrder = [0, 5, 1, 4, 3, 2];
+
+ function test_select_progressCallback(aBrowser) {
+ loadCount++;
+
+ let counts = countTabs();
+ let expected = expectedCounts[loadCount - 1];
+
+ is(counts[0], expected[0], "test_select: load " + loadCount + " - # tabs that need to be restored");
+ is(counts[1], expected[1], "test_select: load " + loadCount + " - # tabs that are restoring");
+ is(counts[2], expected[2], "test_select: load " + loadCount + " - # tabs that has been restored");
+
+ if (loadCount < state.windows[0].tabs.length) {
+ // double check that this tab was the right one
+ let expectedData = state.windows[0].tabs[tabOrder[loadCount - 1]].extData.uniq;
+ let tab;
+ for (let i = 0; i < window.getBrowser().tabs.length; i++) {
+ if (!tab && window.getBrowser().tabs[i].linkedBrowser == aBrowser)
+ tab = window.getBrowser().tabs[i];
+ }
+ is(ss.getTabValue(tab, "uniq"), expectedData, "test_select: load " + loadCount + " - correct tab was restored");
+
+ // select the next tab
+ window.getBrowser().selectTabAtIndex(tabOrder[loadCount]);
+ return;
+ }
+
+ window.getBrowser().removeTabsProgressListener(progressListener);
+ runNextTest();
+ }
+
+ window.getBrowser().addTabsProgressListener(progressListener);
+ ss.setBrowserState(JSON.stringify(state));
+}
+
+
+function test_multiWindowState() {
+ // We have our own progress listener for this test, which we'll attach before our state is set
+ let progressListener = {
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ // We only care about load events when the tab still has
+ // __SS_restoreState == TAB_STATE_RESTORING on it.
+ // Since our listener is attached before the sessionstore one, this works out.
+ if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+ test_multiWindowState_progressCallback(aBrowser);
+ }
+ }
+
+ // The first window will be put into the already open window and the second
+ // window will be opened with _openWindowWithState, which is the source of the problem.
+ let state = { windows: [
+ {
+ tabs: [
+ { entries: [{ url: "http://example.org#0" }], extData: { "uniq": r() } }
+ ],
+ selected: 1
+ },
+ {
+ tabs: [
+ { entries: [{ url: "http://example.com#1" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#2" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#3" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#4" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#5" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#6" }], extData: { "uniq": r() } }
+ ],
+ selected: 4
+ }
+ ] };
+ let numTabs = state.windows[0].tabs.length + state.windows[1].tabs.length;
+
+ let loadCount = 0;
+ function test_multiWindowState_progressCallback(aBrowser) {
+ loadCount++;
+
+ if (loadCount < numTabs)
+ return;
+
+ // We don't actually care about load order in this test, just that they all
+ // do load.
+ is(loadCount, numTabs, "test_multiWindowState: all tabs were restored");
+ let count = countTabs();
+ is(count[0], 0,
+ "test_multiWindowState: there are no tabs left needing restore");
+
+ // Remove the progress listener from this window, it will be removed from
+ // theWin when that window is closed (in setBrowserState).
+ window.getBrowser().removeTabsProgressListener(progressListener);
+ runNextTest();
+ }
+
+ // We also want to catch the 2nd window, so we need to observe domwindowopened
+ function windowObserver(aSubject, aTopic, aData) {
+ let theWin = aSubject.QueryInterface(Ci.nsIDOMWindow);
+ if (aTopic == "domwindowopened") {
+ theWin.addEventListener("load", function theWinLoad() {
+ theWin.removeEventListener("load", theWinLoad);
+
+ Services.ww.unregisterNotification(windowObserver);
+ theWin.getBrowser().addTabsProgressListener(progressListener);
+ });
+ }
+ }
+ Services.ww.registerNotification(windowObserver);
+
+ window.getBrowser().addTabsProgressListener(progressListener);
+ ss.setBrowserState(JSON.stringify(state));
+}
+
+
+function test_setWindowStateNoOverwrite() {
+ // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time
+ Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1);
+
+ // We have our own progress listener for this test, which we'll attach before our state is set
+ let progressListener = {
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ // We only care about load events when the tab still has
+ // __SS_restoreState == TAB_STATE_RESTORING on it.
+ // Since our listener is attached before the sessionstore one, this works out.
+ if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+ test_setWindowStateNoOverwrite_progressCallback(aBrowser);
+ }
+ }
+
+ // We'll use 2 states so that we can make sure calling setWindowState doesn't
+ // wipe out currently restoring data.
+ let state1 = { windows: [{ tabs: [
+ { entries: [{ url: "http://example.com#1" }] },
+ { entries: [{ url: "http://example.com#2" }] },
+ { entries: [{ url: "http://example.com#3" }] },
+ { entries: [{ url: "http://example.com#4" }] },
+ { entries: [{ url: "http://example.com#5" }] },
+ ] }] };
+ let state2 = { windows: [{ tabs: [
+ { entries: [{ url: "http://example.org#1" }] },
+ { entries: [{ url: "http://example.org#2" }] },
+ { entries: [{ url: "http://example.org#3" }] },
+ { entries: [{ url: "http://example.org#4" }] },
+ { entries: [{ url: "http://example.org#5" }] }
+ ] }] };
+
+ let numTabs = state1.windows[0].tabs.length + state2.windows[0].tabs.length;
+
+ let loadCount = 0;
+ function test_setWindowStateNoOverwrite_progressCallback(aBrowser) {
+ loadCount++;
+
+ // When loadCount == 2, we'll also restore state2 into the window
+ if (loadCount == 2)
+ ss.setWindowState(window, JSON.stringify(state2), false);
+
+ if (loadCount < numTabs)
+ return;
+
+ // We don't actually care about load order in this test, just that they all
+ // do load.
+ is(loadCount, numTabs, "test_setWindowStateNoOverwrite: all tabs were restored");
+ // window.__SS_tabsToRestore isn't decremented until after the progress
+ // listener is called. Since we get in here before that, we still expect
+ // the count to be 1.
+ is(window.__SS_tabsToRestore, 1,
+ "test_setWindowStateNoOverwrite: window doesn't think there are more tabs to restore");
+ let count = countTabs();
+ is(count[0], 0,
+ "test_setWindowStateNoOverwrite: there are no tabs left needing restore");
+
+ // Remove the progress listener from this window, it will be removed from
+ // theWin when that window is closed (in setBrowserState).
+ window.getBrowser().removeTabsProgressListener(progressListener);
+
+ runNextTest();
+ }
+
+ window.getBrowser().addTabsProgressListener(progressListener);
+ ss.setWindowState(window, JSON.stringify(state1), true);
+}
+
+
+function test_setWindowStateOverwrite() {
+ // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time
+ Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1);
+
+ // We have our own progress listener for this test, which we'll attach before our state is set
+ let progressListener = {
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ // We only care about load events when the tab still has
+ // __SS_restoreState == TAB_STATE_RESTORING on it.
+ // Since our listener is attached before the sessionstore one, this works out.
+ if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+ test_setWindowStateOverwrite_progressCallback(aBrowser);
+ }
+ }
+
+ // We'll use 2 states so that we can make sure calling setWindowState doesn't
+ // wipe out currently restoring data.
+ let state1 = { windows: [{ tabs: [
+ { entries: [{ url: "http://example.com#1" }] },
+ { entries: [{ url: "http://example.com#2" }] },
+ { entries: [{ url: "http://example.com#3" }] },
+ { entries: [{ url: "http://example.com#4" }] },
+ { entries: [{ url: "http://example.com#5" }] },
+ ] }] };
+ let state2 = { windows: [{ tabs: [
+ { entries: [{ url: "http://example.org#1" }] },
+ { entries: [{ url: "http://example.org#2" }] },
+ { entries: [{ url: "http://example.org#3" }] },
+ { entries: [{ url: "http://example.org#4" }] },
+ { entries: [{ url: "http://example.org#5" }] }
+ ] }] };
+
+ let numTabs = 2 + state2.windows[0].tabs.length;
+
+ let loadCount = 0;
+ function test_setWindowStateOverwrite_progressCallback(aBrowser) {
+ loadCount++;
+
+ // When loadCount == 2, we'll also restore state2 into the window
+ if (loadCount == 2)
+ ss.setWindowState(window, JSON.stringify(state2), true);
+
+ if (loadCount < numTabs)
+ return;
+
+ // We don't actually care about load order in this test, just that they all
+ // do load.
+ is(loadCount, numTabs, "test_setWindowStateOverwrite: all tabs were restored");
+ // window.__SS_tabsToRestore isn't decremented until after the progress
+ // listener is called. Since we get in here before that, we still expect
+ // the count to be 1.
+ is(window.__SS_tabsToRestore, 1,
+ "test_setWindowStateOverwrite: window doesn't think there are more tabs to restore");
+ let count = countTabs();
+ is(count[0], 0,
+ "test_setWindowStateOverwrite: there are no tabs left needing restore");
+
+ // Remove the progress listener from this window, it will be removed from
+ // theWin when that window is closed (in setBrowserState).
+ window.getBrowser().removeTabsProgressListener(progressListener);
+
+ runNextTest();
+ }
+
+ window.getBrowser().addTabsProgressListener(progressListener);
+ ss.setWindowState(window, JSON.stringify(state1), true);
+}
+
+
+function test_setBrowserStateInterrupted() {
+ // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time
+ Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1);
+
+ // We have our own progress listener for this test, which we'll attach before our state is set
+ let progressListener = {
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ // We only care about load events when the tab still has
+ // __SS_restoreState == TAB_STATE_RESTORING on it.
+ // Since our listener is attached before the sessionstore one, this works out.
+ if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+ test_setBrowserStateInterrupted_progressCallback(aBrowser);
+ }
+ }
+
+ // The first state will be loaded using setBrowserState, followed by the 2nd
+ // state also being loaded using setBrowserState, interrupting the first restore.
+ let state1 = { windows: [
+ {
+ tabs: [
+ { entries: [{ url: "http://example.org#1" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org#2" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org#3" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org#4" }], extData: { "uniq": r() } }
+ ],
+ selected: 1
+ },
+ {
+ tabs: [
+ { entries: [{ url: "http://example.com#1" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#2" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#3" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#4" }], extData: { "uniq": r() } },
+ ],
+ selected: 3
+ }
+ ] };
+ let state2 = { windows: [
+ {
+ tabs: [
+ { entries: [{ url: "http://example.org#5" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org#6" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org#7" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org#8" }], extData: { "uniq": r() } }
+ ],
+ selected: 3
+ },
+ {
+ tabs: [
+ { entries: [{ url: "http://example.com#5" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#6" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#7" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#8" }], extData: { "uniq": r() } },
+ ],
+ selected: 1
+ }
+ ] };
+
+ // interruptedAfter will be set after the selected tab from each window have loaded.
+ let interruptedAfter = 0;
+ let loadedWindow1 = false;
+ let loadedWindow2 = false;
+ let numTabs = state2.windows[0].tabs.length + state2.windows[1].tabs.length;
+
+ let loadCount = 0;
+ function test_setBrowserStateInterrupted_progressCallback(aBrowser) {
+ loadCount++;
+
+ if (aBrowser.currentURI.spec == state1.windows[0].tabs[2].entries[0].url)
+ loadedWindow1 = true;
+ if (aBrowser.currentURI.spec == state1.windows[1].tabs[0].entries[0].url)
+ loadedWindow2 = true;
+
+ if (!interruptedAfter && loadedWindow1 && loadedWindow2) {
+ interruptedAfter = loadCount;
+ ss.setBrowserState(JSON.stringify(state2));
+ return;
+ }
+
+ if (loadCount < numTabs + interruptedAfter)
+ return;
+
+ // We don't actually care about load order in this test, just that they all
+ // do load.
+ is(loadCount, numTabs + interruptedAfter,
+ "test_setBrowserStateInterrupted: all tabs were restored");
+ let count = countTabs();
+ is(count[0], 0,
+ "test_setBrowserStateInterrupted: there are no tabs left needing restore");
+
+ // Remove the progress listener from this window, it will be removed from
+ // theWin when that window is closed (in setBrowserState).
+ window.getBrowser().removeTabsProgressListener(progressListener);
+ Services.ww.unregisterNotification(windowObserver);
+ runNextTest();
+ }
+
+ // We also want to catch the extra windows (there should be 2), so we need to observe domwindowopened
+ function windowObserver(aSubject, aTopic, aData) {
+ let theWin = aSubject.QueryInterface(Ci.nsIDOMWindow);
+ if (aTopic == "domwindowopened") {
+ theWin.addEventListener("load", function wObserverTheWinLoad() {
+ theWin.removeEventListener("load", wObserverTheWinLoad);
+
+ Services.ww.unregisterNotification(windowObserver);
+ theWin.getBrowser().addTabsProgressListener(progressListener);
+ });
+ }
+ }
+ Services.ww.registerNotification(windowObserver);
+
+ window.getBrowser().addTabsProgressListener(progressListener);
+ ss.setBrowserState(JSON.stringify(state1));
+}
+
+
+function test_reload() {
+ // Set the pref to 0 so we know exactly how many tabs should be restoring at
+ // any given time. This guarantees that a finishing load won't start another.
+ Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 0);
+
+ // We have our own progress listener for this test, which we'll attach before our state is set
+ let progressListener = {
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+ test_reload_progressCallback(aBrowser);
+ }
+ }
+
+ let state = { windows: [{ tabs: [
+ { entries: [{ url: "http://example.org/#1" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#2" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#3" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#4" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#5" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#6" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#7" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#8" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#9" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#10" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#11" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#12" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#13" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#14" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#15" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#16" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#17" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#18" }], extData: { "uniq": r() } }
+ ], selected: 1 }] };
+
+ let loadCount = 0;
+ function test_reload_progressCallback(aBrowser) {
+ loadCount++;
+
+ is(aBrowser.currentURI.spec, state.windows[0].tabs[loadCount - 1].entries[0].url,
+ "test_reload: load " + loadCount + " - browser loaded correct url");
+
+ if (loadCount <= state.windows[0].tabs.length) {
+ // double check that this tab was the right one
+ let expectedData = state.windows[0].tabs[loadCount - 1].extData.uniq;
+ let tab;
+ for (let i = 0; i < window.getBrowser().tabs.length; i++) {
+ if (!tab && window.getBrowser().tabs[i].linkedBrowser == aBrowser)
+ tab = window.getBrowser().tabs[i];
+ }
+ is(ss.getTabValue(tab, "uniq"), expectedData,
+ "test_reload: load " + loadCount + " - correct tab was restored");
+
+ if (loadCount == state.windows[0].tabs.length) {
+ window.getBrowser().removeTabsProgressListener(progressListener);
+ executeSoon(function() {
+ _test_reloadAfter("test_reloadReload", state, runNextTest);
+ });
+ }
+ else {
+ // reload the next tab
+ window.getBrowser().reloadTab(window.getBrowser().tabs[loadCount]);
+ }
+ }
+
+ }
+
+ window.getBrowser().addTabsProgressListener(progressListener);
+ ss.setBrowserState(JSON.stringify(state));
+}
+
+
+// This doesn't actually test anything, just does a cascaded restore with default
+// settings. This really just sets up to test that reloads work.
+function test_reloadCascadeSetup() {
+ // We have our own progress listener for this test, which we'll attach before our state is set
+ let progressListener = {
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+ test_cascadeReloadSetup_progressCallback();
+ }
+ }
+
+ let state = { windows: [{ tabs: [
+ { entries: [{ url: "http://example.com/#1" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com/#2" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com/#3" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com/#4" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com/#5" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com/#6" }], extData: { "uniq": r() } }
+ ] }] };
+
+ let loadCount = 0;
+ function test_cascadeReloadSetup_progressCallback() {
+ loadCount++;
+ if (loadCount < state.windows[0].tabs.length)
+ return;
+
+ window.getBrowser().removeTabsProgressListener(progressListener);
+ executeSoon(function() {
+ _test_reloadAfter("test_reloadCascade", state, runNextTest);
+ });
+ }
+
+ // This progress listener will get attached before the listener in session store.
+ window.getBrowser().addTabsProgressListener(progressListener);
+ ss.setBrowserState(JSON.stringify(state));
+}
+
+
+// This is a generic function that will attempt to reload each test. We do this
+// a couple times, so make it utilitarian.
+// This test expects that aState contains a single window and that each tab has
+// a unique extData value eg. { "uniq": value }.
+function _test_reloadAfter(aTestName, aState, aCallback) {
+ info("starting " + aTestName);
+ let progressListener = {
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+ test_reloadAfter_progressCallback(aBrowser);
+ }
+ }
+
+ // Simulate a left mouse button click with no modifiers, which is what
+ // Command-R, or clicking reload does.
+ let fakeEvent = {
+ button: 0,
+ metaKey: false,
+ altKey: false,
+ ctrlKey: false,
+ shiftKey: false,
+ }
+
+ let loadCount = 0;
+ function test_reloadAfter_progressCallback(aBrowser) {
+ loadCount++;
+
+ if (loadCount <= aState.windows[0].tabs.length) {
+ // double check that this tab was the right one
+ let expectedData = aState.windows[0].tabs[loadCount - 1].extData.uniq;
+ let tab;
+ for (let i = 0; i < window.getBrowser().tabs.length; i++) {
+ if (!tab && window.getBrowser().tabs[i].linkedBrowser == aBrowser)
+ tab = window.getBrowser().tabs[i];
+ }
+ is(ss.getTabValue(tab, "uniq"), expectedData,
+ aTestName + ": load " + loadCount + " - correct tab was reloaded");
+
+ if (loadCount == aState.windows[0].tabs.length) {
+ window.getBrowser().removeTabsProgressListener(progressListener);
+ aCallback();
+ }
+ else {
+ // reload the next tab
+ window.getBrowser().selectTabAtIndex(loadCount);
+ BrowserReload(fakeEvent);
+ }
+ }
+ }
+
+ window.getBrowser().addTabsProgressListener(progressListener);
+ BrowserReload(fakeEvent);
+}
+
+
+function countTabs() {
+ let needsRestore = 0,
+ isRestoring = 0,
+ wasRestored = 0;
+
+ let windowsEnum = Services.wm.getEnumerator("navigator:browser");
+
+ while (windowsEnum.hasMoreElements()) {
+ let window = windowsEnum.getNext();
+ if (window.closed)
+ continue;
+
+ for (let i = 0; i < window.getBrowser().tabs.length; i++) {
+ let browser = window.getBrowser().tabs[i].linkedBrowser;
+ if (browser.__SS_restoreState == TAB_STATE_RESTORING)
+ isRestoring++;
+ else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
+ needsRestore++;
+ else
+ wasRestored++;
+ }
+ }
+ return [needsRestore, isRestoring, wasRestored];
+}
+
+function r() {
+ return "" + Date.now() + Math.random();
+}
+
diff --git a/comm/suite/components/tests/browser/browser_597315.js b/comm/suite/components/tests/browser/browser_597315.js
new file mode 100644
index 0000000000..516ff5ae88
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_597315.js
@@ -0,0 +1,64 @@
+/* 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/. */
+
+var stateBackup = ss.getBrowserState();
+
+function test() {
+ /** Test for Bug 597315 - Frameset history does not work properly when restoring a tab **/
+ waitForExplicitFinish();
+
+ Services.prefs.setIntPref("browser.tabs.max_tabs_undo", 0);
+
+ let testURL = getRootDirectory(gTestPath) + "browser_597315_index.html";
+ let tab = getBrowser().addTab(testURL);
+ getBrowser().selectedTab = tab;
+
+ waitForLoadsInBrowser(tab.linkedBrowser, 4, function() {
+ let browser_b = tab.linkedBrowser.contentDocument.getElementsByTagName("frame")[1];
+ let document_b = browser_b.contentDocument;
+ let links = document_b.getElementsByTagName("a");
+
+ // We're going to click on the first link, so listen for another load event
+ waitForLoadsInBrowser(tab.linkedBrowser, 1, function() {
+ waitForLoadsInBrowser(tab.linkedBrowser, 1, function() {
+
+ getBrowser().removeTab(tab);
+ // wait for 4 loads again...
+ let newTab = ss.undoCloseTab(window, 0);
+
+ waitForLoadsInBrowser(newTab.linkedBrowser, 4, function() {
+ getBrowser().goBack();
+ waitForLoadsInBrowser(newTab.linkedBrowser, 1, function() {
+
+ let expectedURLEnds = ["a.html", "b.html", "c1.html"];
+ let frames = newTab.linkedBrowser.contentDocument.getElementsByTagName("frame");
+ for (let i = 0; i < frames.length; i++) {
+ is(frames[i].contentDocument.location,
+ getRootDirectory(gTestPath) + "browser_597315_" + expectedURLEnds[i],
+ "frame " + i + " has the right url");
+ }
+ Services.prefs.clearUserPref("browser.tabs.max_tabs_undo");
+ getBrowser().removeTab(newTab);
+ ss.setBrowserState(stateBackup);
+ executeSoon(finish);
+ });
+ });
+ });
+ EventUtils.sendMouseEvent({type:"click"}, links[1], browser_b.contentWindow);
+ });
+ EventUtils.sendMouseEvent({type:"click"}, links[0], browser_b.contentWindow);
+ });
+}
+
+// helper function
+function waitForLoadsInBrowser(aBrowser, aLoadCount, aCallback) {
+ let loadCount = 0;
+ aBrowser.addEventListener("load", function aBrowserLoad(aEvent) {
+ if (++loadCount < aLoadCount)
+ return;
+
+ aBrowser.removeEventListener("load", aBrowserLoad, true);
+ aCallback();
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_597315_a.html b/comm/suite/components/tests/browser/browser_597315_a.html
new file mode 100755
index 0000000000..8e7b35d7a1
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_597315_a.html
@@ -0,0 +1,5 @@
+<html>
+ <body>
+ I'm A!
+ </body>
+</html>
diff --git a/comm/suite/components/tests/browser/browser_597315_b.html b/comm/suite/components/tests/browser/browser_597315_b.html
new file mode 100755
index 0000000000..f8dbfb2a27
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_597315_b.html
@@ -0,0 +1,10 @@
+<html>
+ <body>
+ I'm B!<br/>
+ <a target="c" href="browser_597315_c1.html">click me first</a><br/>
+ <a target="c" href="browser_597315_c2.html">then click me</a><br/>
+ Close this tab.<br/>
+ Restore this tab.<br/>
+ Click back.<br/>
+ </body>
+</html>
diff --git a/comm/suite/components/tests/browser/browser_597315_c.html b/comm/suite/components/tests/browser/browser_597315_c.html
new file mode 100755
index 0000000000..0efd7d9026
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_597315_c.html
@@ -0,0 +1,5 @@
+<html>
+ <body>
+ I'm C!
+ </body>
+</html>
diff --git a/comm/suite/components/tests/browser/browser_597315_c1.html b/comm/suite/components/tests/browser/browser_597315_c1.html
new file mode 100755
index 0000000000..b55c1d45a9
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_597315_c1.html
@@ -0,0 +1,5 @@
+<html>
+ <body>
+ I'm C1!
+ </body>
+</html>
diff --git a/comm/suite/components/tests/browser/browser_597315_c2.html b/comm/suite/components/tests/browser/browser_597315_c2.html
new file mode 100755
index 0000000000..aec504141b
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_597315_c2.html
@@ -0,0 +1,5 @@
+<html>
+ <body>
+ I'm C2!
+ </body>
+</html>
diff --git a/comm/suite/components/tests/browser/browser_597315_index.html b/comm/suite/components/tests/browser/browser_597315_index.html
new file mode 100644
index 0000000000..1465ddf044
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_597315_index.html
@@ -0,0 +1,10 @@
+<html>
+ <frameset cols="20%,80%">
+ <frameset rows="30%,70%">
+ <frame src="browser_597315_a.html"/>
+ <frame src="browser_597315_b.html"/>
+ </frameset>
+ <frame src="browser_597315_c.html" name="c"/>
+ </frameset>
+</html>
+
diff --git a/comm/suite/components/tests/browser/browser_607016.js b/comm/suite/components/tests/browser/browser_607016.js
new file mode 100644
index 0000000000..9de13dd05a
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_607016.js
@@ -0,0 +1,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/. */
+
+const TAB_STATE_NEEDS_RESTORE = 1;
+const TAB_STATE_RESTORING = 2;
+
+var stateBackup = ss.getBrowserState();
+
+function cleanup() {
+ // Reset the pref
+ try {
+ Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs");
+ } catch (e) {}
+ ss.setBrowserState(stateBackup);
+ executeSoon(finish);
+}
+
+function test() {
+ /** Bug 607016 - If a tab is never restored, attributes (eg. hidden) aren't updated correctly **/
+ waitForExplicitFinish();
+
+ // Set the pref to 0 so we know exactly how many tabs should be restoring at
+ // any given time. This guarantees that a finishing load won't start another.
+ Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 0);
+ Services.prefs.setIntPref("browser.tabs.max_tabs_undo", 0);
+
+ // We have our own progress listener for this test, which we'll attach before our state is set
+ let progressListener = {
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+ progressCallback(aBrowser);
+ }
+ }
+
+ let state = { windows: [{ tabs: [
+ { entries: [{ url: "http://example.org#1" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org#2" }], extData: { "uniq": r() } }, // overwriting
+ //{ entries: [{ url: "http://example.org#3" }], extData: { "uniq": r() } }, // hiding
+ { entries: [{ url: "http://example.org#4" }], extData: { "uniq": r() } }, // adding
+ { entries: [{ url: "http://example.org#5" }], extData: { "uniq": r() } }, // deleting
+ { entries: [{ url: "http://example.org#6" }] } // creating
+ ], selected: 1 }] };
+
+ function progressCallback(aBrowser) {
+ // We'll remove the progress listener after the first one because we aren't
+ // loading any other tabs
+ window.getBrowser().removeTabsProgressListener(progressListener);
+
+ let curState = JSON.parse(ss.getBrowserState());
+ for (let i = 0; i < curState.windows[0].tabs.length; i++) {
+ if (state.windows[0].tabs[i].extData) {
+ is(curState.windows[0].tabs[i].extData["uniq"],
+ state.windows[0].tabs[i].extData["uniq"],
+ "sanity check that tab has correct extData");
+ }
+ else
+ ok(!("extData" in curState.windows[0].tabs[i]),
+ "sanity check that tab doesn't have extData");
+ }
+
+ // Now we'll set a new unique value on 1 of the tabs
+ let newUniq = r();
+ ss.setTabValue(getBrowser().tabs[1], "uniq", newUniq);
+ getBrowser().removeTab(getBrowser().tabs[1]);
+ let closedTabData = (JSON.parse(ss.getClosedTabData(window)))[0];
+ is(closedTabData.state.extData.uniq, newUniq,
+ "(overwriting) new data is stored in extData");
+
+ // hide the next tab before closing it
+ //getBrowser().hideTab(getBrowser().tabs[1]);
+ //getBrowser().removeTab(getBrowser().tabs[1]);
+ //closedTabData = (JSON.parse(ss.getClosedTabData(window)))[0];
+ //ok(closedTabData.state.hidden, "(hiding) tab data has hidden == true");
+
+ // set data that's not in a conflicting key
+ let stillUniq = r();
+ ss.setTabValue(getBrowser().tabs[1], "stillUniq", stillUniq);
+ getBrowser().removeTab(getBrowser().tabs[1]);
+ closedTabData = (JSON.parse(ss.getClosedTabData(window)))[0];
+ is(closedTabData.state.extData.stillUniq, stillUniq,
+ "(adding) new data is stored in extData");
+
+ // remove the uniq value and make sure it's not there in the closed data
+ ss.deleteTabValue(getBrowser().tabs[1], "uniq");
+ getBrowser().removeTab(getBrowser().tabs[1]);
+ closedTabData = (JSON.parse(ss.getClosedTabData(window)))[0];
+ // Since Panorama might have put data in, first check if there is extData.
+ // If there is explicitly check that "uniq" isn't in it. Otherwise, we're ok
+ if ("extData" in closedTabData.state) {
+ ok(!("uniq" in closedTabData.state.extData),
+ "(deleting) uniq not in existing extData");
+ }
+ else {
+ ok(true, "(deleting) no data is stored in extData");
+ }
+
+ // set unique data on the tab that never had any set, make sure that's saved
+ let newUniq2 = r();
+ ss.setTabValue(getBrowser().tabs[1], "uniq", newUniq2);
+ getBrowser().removeTab(getBrowser().tabs[1]);
+ closedTabData = (JSON.parse(ss.getClosedTabData(window)))[0];
+ is(closedTabData.state.extData.uniq, newUniq2,
+ "(creating) new data is stored in extData where there was none");
+
+ cleanup();
+ }
+
+ window.getBrowser().addTabsProgressListener(progressListener);
+ ss.setBrowserState(JSON.stringify(state));
+}
+
+// Helper function to create a random value
+function r() {
+ return "" + Date.now() + Math.random();
+}
+
diff --git a/comm/suite/components/tests/browser/browser_615394-SSWindowState_events.js b/comm/suite/components/tests/browser/browser_615394-SSWindowState_events.js
new file mode 100644
index 0000000000..e71641d9ca
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_615394-SSWindowState_events.js
@@ -0,0 +1,362 @@
+/* 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/. */
+
+const stateBackup = ss.getBrowserState();
+const testState = {
+ windows: [{
+ tabs: [
+ { entries: [{ url: "about:blank" }] },
+ { entries: [{ url: "about:logo" }] }
+ ]
+ }]
+};
+const lameMultiWindowState = { windows: [
+ {
+ tabs: [
+ { entries: [{ url: "http://example.org#1" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org#2" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org#3" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org#4" }], extData: { "uniq": r() } }
+ ],
+ selected: 1
+ },
+ {
+ tabs: [
+ { entries: [{ url: "http://example.com#1" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#2" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#3" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#4" }], extData: { "uniq": r() } },
+ ],
+ selected: 3
+ }
+ ] };
+
+
+function getOuterWindowID(aWindow) {
+ return aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
+}
+
+function test() {
+ /** Test for Bug 615394 - Session Restore should notify when it is beginning and ending a restore **/
+ waitForExplicitFinish();
+ // Preemptively extend the timeout to prevent [orange]
+ requestLongerTimeout(2);
+ Services.prefs.setIntPref("browser.tabs.max_tabs_undo", 0);
+ runNextTest();
+}
+
+
+var tests = [
+ test_setTabState,
+ test_duplicateTab,
+ test_undoCloseTab,
+ test_setWindowState,
+ test_setBrowserState,
+ test_undoCloseWindow
+];
+function runNextTest() {
+ // set an empty state & run the next test, or finish
+ if (tests.length) {
+ // Enumerate windows and close everything but our primary window. We can't
+ // use waitForFocus() because apparently it's buggy. See bug 599253.
+ var windowsEnum = Services.wm.getEnumerator("navigator:browser");
+ while (windowsEnum.hasMoreElements()) {
+ var currentWindow = windowsEnum.getNext();
+ if (currentWindow != window) {
+ currentWindow.close();
+ }
+ }
+
+ let currentTest = tests.shift();
+ info("prepping for " + currentTest.name);
+ waitForBrowserState(testState, currentTest);
+ }
+ else {
+ Services.prefs.clearUserPref("browser.tabs.max_tabs_undo");
+ ss.setBrowserState(stateBackup);
+ finish();
+ }
+}
+
+/** ACTUAL TESTS **/
+
+function test_setTabState() {
+ let tab = getBrowser().tabs[1];
+ let newTabState = JSON.stringify({ entries: [{ url: "http://example.org" }], extData: { foo: "bar" } });
+ let busyEventCount = 0;
+ let readyEventCount = 0;
+
+ function onSSWindowStateBusy(aEvent) {
+ busyEventCount++;
+ }
+
+ function onSSWindowStateReady(aEvent) {
+ readyEventCount++;
+ is(ss.getTabValue(tab, "foo"), "bar");
+ ss.setTabValue(tab, "baz", "qux");
+ }
+
+ function onSSTabRestored(aEvent) {
+ is(busyEventCount, 1);
+ is(readyEventCount, 1);
+ is(ss.getTabValue(tab, "baz"), "qux");
+ is(tab.linkedBrowser.currentURI.spec, "http://example.org/");
+
+ window.removeEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ window.removeEventListener("SSWindowStateReady", onSSWindowStateReady);
+ getBrowser().tabContainer.removeEventListener("SSTabRestored", onSSTabRestored);
+
+ runNextTest();
+ }
+
+ window.addEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ window.addEventListener("SSWindowStateReady", onSSWindowStateReady);
+ getBrowser().tabContainer.addEventListener("SSTabRestored", onSSTabRestored);
+ ss.setTabState(tab, newTabState);
+}
+
+
+function test_duplicateTab() {
+ let tab = getBrowser().tabs[1];
+ let busyEventCount = 0;
+ let readyEventCount = 0;
+ let newTab;
+
+ // We'll look to make sure this value is on the duplicated tab
+ ss.setTabValue(tab, "foo", "bar");
+
+ function onSSWindowStateBusy(aEvent) {
+ busyEventCount++;
+ }
+
+ // duplicateTab is "synchronous" in tab creation. Since restoreHistory is called
+ // via setTimeout, newTab will be assigned before the SSWindowStateReady event
+ function onSSWindowStateReady(aEvent) {
+ readyEventCount++;
+ is(ss.getTabValue(newTab, "foo"), "bar");
+ ss.setTabValue(newTab, "baz", "qux");
+ }
+
+ function onSSTabRestored(aEvent) {
+ is(busyEventCount, 1);
+ is(readyEventCount, 1);
+ is(ss.getTabValue(newTab, "baz"), "qux");
+ is(newTab.linkedBrowser.currentURI.spec, "about:logo");
+
+ window.removeEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ window.removeEventListener("SSWindowStateReady", onSSWindowStateReady);
+ getBrowser().tabContainer.removeEventListener("SSTabRestored", onSSTabRestored);
+
+ runNextTest();
+ }
+
+ window.addEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ window.addEventListener("SSWindowStateReady", onSSWindowStateReady);
+ getBrowser().tabContainer.addEventListener("SSTabRestored", onSSTabRestored);
+
+ newTab = ss.duplicateTab(window, tab);
+}
+
+
+function test_undoCloseTab() {
+ let tab = getBrowser().tabs[1],
+ busyEventCount = 0,
+ readyEventCount = 0,
+ reopenedTab;
+
+ ss.setTabValue(tab, "foo", "bar");
+
+ function onSSWindowStateBusy(aEvent) {
+ busyEventCount++;
+ }
+
+ // undoCloseTab is "synchronous" in tab creation. Since restoreHistory is called
+ // via setTimeout, reopenedTab will be assigned before the SSWindowStateReady event
+ function onSSWindowStateReady(aEvent) {
+ readyEventCount++;
+ is(ss.getTabValue(reopenedTab, "foo"), "bar");
+ ss.setTabValue(reopenedTab, "baz", "qux");
+ }
+
+ function onSSTabRestored(aEvent) {
+ is(busyEventCount, 1);
+ is(readyEventCount, 1);
+ is(ss.getTabValue(reopenedTab, "baz"), "qux");
+ is(reopenedTab.linkedBrowser.currentURI.spec, "about:logo");
+
+ window.removeEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ window.removeEventListener("SSWindowStateReady", onSSWindowStateReady);
+ getBrowser().tabContainer.removeEventListener("SSTabRestored", onSSTabRestored);
+
+ runNextTest();
+ }
+
+ window.addEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ window.addEventListener("SSWindowStateReady", onSSWindowStateReady);
+ getBrowser().tabContainer.addEventListener("SSTabRestored", onSSTabRestored);
+
+ getBrowser().removeTab(tab);
+ reopenedTab = ss.undoCloseTab(window, 0);
+}
+
+
+function test_setWindowState() {
+ let testState = {
+ windows: [{
+ tabs: [
+ { entries: [{ url: "about:mozilla" }], extData: { "foo": "bar" } },
+ { entries: [{ url: "http://example.org" }], extData: { "baz": "qux" } }
+ ]
+ }]
+ };
+
+ let busyEventCount = 0,
+ readyEventCount = 0,
+ tabRestoredCount = 0;
+
+ function onSSWindowStateBusy(aEvent) {
+ busyEventCount++;
+ }
+
+ function onSSWindowStateReady(aEvent) {
+ readyEventCount++;
+ is(ss.getTabValue(gBrowser.tabs[0], "foo"), "bar");
+ is(ss.getTabValue(gBrowser.tabs[1], "baz"), "qux");
+ }
+
+ function onSSTabRestored(aEvent) {
+ if (++tabRestoredCount < 2)
+ return;
+
+ is(busyEventCount, 1);
+ is(readyEventCount, 1);
+ is(getBrowser().tabs[0].linkedBrowser.currentURI.spec, "about:mozilla");
+ is(getBrowser().tabs[1].linkedBrowser.currentURI.spec, "http://example.org/");
+
+ window.removeEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ window.removeEventListener("SSWindowStateReady", onSSWindowStateReady);
+ getBrowser().tabContainer.removeEventListener("SSTabRestored", onSSTabRestored);
+
+ runNextTest();
+ }
+
+ window.addEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ window.addEventListener("SSWindowStateReady", onSSWindowStateReady);
+ getBrowser().tabContainer.addEventListener("SSTabRestored", onSSTabRestored);
+
+ ss.setWindowState(window, JSON.stringify(testState), true);
+}
+
+
+function test_setBrowserState() {
+ // We'll track events per window so we are sure that they are each happening once
+ // pre window.
+ let windowEvents = {};
+ windowEvents[getOuterWindowID(window)] = { busyEventCount: 0, readyEventCount: 0 };
+
+ // waitForBrowserState does it's own observing for windows, but doesn't attach
+ // the listeners we want here, so do it ourselves.
+ let newWindow;
+ function windowObserver(aSubject, aTopic, aData) {
+ if (aTopic == "domwindowopened") {
+ newWindow = aSubject.QueryInterface(Ci.nsIDOMWindow);
+ newWindow.addEventListener("load", function newWindowLoad() {
+ newWindow.removeEventListener("load", newWindowLoad);
+
+ Services.ww.unregisterNotification(windowObserver);
+
+ windowEvents[getOuterWindowID(newWindow)] = { busyEventCount: 0, readyEventCount: 0 };
+
+ newWindow.addEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ newWindow.addEventListener("SSWindowStateReady", onSSWindowStateReady);
+ });
+ }
+ }
+
+ function onSSWindowStateBusy(aEvent) {
+ windowEvents[getOuterWindowID(aEvent.originalTarget)].busyEventCount++;
+ }
+
+ function onSSWindowStateReady(aEvent) {
+ windowEvents[getOuterWindowID(aEvent.originalTarget)].readyEventCount++;
+ }
+
+ window.addEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ window.addEventListener("SSWindowStateReady", onSSWindowStateReady);
+ Services.ww.registerNotification(windowObserver);
+
+ waitForBrowserState(lameMultiWindowState, function() {
+ let checkedWindows = 0;
+ for (let [id, winEvents] of Object.entries(windowEvents)) {
+ is(winEvents.busyEventCount, 1,
+ "[test_setBrowserState] window" + id + " busy event count correct");
+ is(winEvents.readyEventCount, 1,
+ "[test_setBrowserState] window" + id + " ready event count correct");
+ checkedWindows++;
+ }
+ is(checkedWindows, 2,
+ "[test_setBrowserState] checked 2 windows");
+ window.removeEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ window.removeEventListener("SSWindowStateReady", onSSWindowStateReady);
+ newWindow.removeEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ newWindow.removeEventListener("SSWindowStateReady", onSSWindowStateReady);
+ runNextTest();
+ });
+}
+
+
+function test_undoCloseWindow() {
+ let newWindow, reopenedWindow;
+
+ function firstWindowObserver(aSubject, aTopic, aData) {
+ if (aTopic == "domwindowopened") {
+ newWindow = aSubject.QueryInterface(Ci.nsIDOMWindow);
+ Services.ww.unregisterNotification(firstWindowObserver);
+ }
+ }
+ Services.ww.registerNotification(firstWindowObserver);
+
+ waitForBrowserState(lameMultiWindowState, function() {
+ // Close the window which isn't window
+ newWindow.close();
+ reopenedWindow = ss.undoCloseWindow(0);
+ reopenedWindow.addEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ reopenedWindow.addEventListener("SSWindowStateReady", onSSWindowStateReady);
+
+ reopenedWindow.addEventListener("load", function reopenWindowLoad() {
+ reopenedWindow.removeEventListener("load", reopenWindowLoad);
+
+ reopenedWindow.getBrowser().tabContainer.addEventListener("SSTabRestored", onSSTabRestored);
+ });
+ });
+
+ let busyEventCount = 0,
+ readyEventCount = 0,
+ tabRestoredCount = 0;
+ // These will listen to the reopened closed window...
+ function onSSWindowStateBusy(aEvent) {
+ busyEventCount++;
+ }
+
+ function onSSWindowStateReady(aEvent) {
+ readyEventCount++;
+ }
+
+ function onSSTabRestored(aEvent) {
+ if (++tabRestoredCount < 4)
+ return;
+
+ is(busyEventCount, 1);
+ is(readyEventCount, 1);
+
+ reopenedWindow.removeEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ reopenedWindow.removeEventListener("SSWindowStateReady", onSSWindowStateReady);
+ reopenedWindow.gBrowser.tabContainer.removeEventListener("SSTabRestored", onSSTabRestored);
+
+ reopenedWindow.close();
+
+ runNextTest();
+ }
+}
diff --git a/comm/suite/components/tests/browser/browser_625257.js b/comm/suite/components/tests/browser/browser_625257.js
new file mode 100644
index 0000000000..b8dff1d233
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_625257.js
@@ -0,0 +1,87 @@
+/* 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/. */
+
+// This tests that a tab which is closed while loading is not lost.
+// Specifically, that session store does not rely on an invalid cache when
+// constructing data for a tab which is loading.
+
+// The newly created tab which we load a URL into and try closing/undoing.
+var tab;
+
+// This test steps through the following parts:
+// 1. Tab has been created is loading URI_TO_LOAD.
+// 2. Before URI_TO_LOAD finishes loading, browser.currentURI has changed and
+// tab is scheduled to be removed.
+// 3. After the tab has been closed, undoCloseTab() has been called and the tab
+// should fully load.
+const URI_TO_LOAD = "about:logo";
+
+function test() {
+ waitForExplicitFinish();
+
+ Services.prefs.setIntPref("browser.tabs.max_tabs_undo", 0);
+ getBrowser().addTabsProgressListener(tabsListener);
+
+ tab = getBrowser().addTab();
+
+ tab.linkedBrowser.addEventListener("load", firstOnLoad, true);
+
+ getBrowser().tabContainer.addEventListener("TabClose", onTabClose, true);
+}
+
+function firstOnLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", firstOnLoad, true);
+
+ let uri = aEvent.target.location;
+ is(uri, "about:blank", "first load should be for about:blank");
+
+ // Trigger a save state.
+ ss.getBrowserState();
+
+ is(getBrowser().tabs[1], tab, "newly created tab should exist by now");
+ ok(tab.linkedBrowser.__SS_data, "newly created tab should be in save state");
+
+ tab.linkedBrowser.loadURI(URI_TO_LOAD);
+}
+
+var tabsListener = {
+ onLocationChange: function onLocationChange(aBrowser) {
+ getBrowser().removeTabsProgressListener(tabsListener);
+
+ is(aBrowser.currentURI.spec, URI_TO_LOAD,
+ "should occur after about:blank load and be loading next page");
+
+ // Since we are running in the context of tabs listeners, we do not
+ // want to disrupt other tabs listeners.
+ executeSoon(function() {
+ getBrowser().removeTab(tab);
+ });
+ }
+};
+
+function onTabClose(aEvent) {
+ getBrowser().tabContainer.removeEventListener("TabClose", onTabClose, true);
+
+ is(tab.linkedBrowser.currentURI.spec, URI_TO_LOAD,
+ "should only remove when loading page");
+
+ executeSoon(function() {
+ tab = ss.undoCloseTab(window, 0);
+ tab.linkedBrowser.addEventListener("load", secondOnLoad, true);
+ });
+}
+
+function secondOnLoad(aEvent) {
+ let uri = aEvent.target.location;
+ is(uri, URI_TO_LOAD, "should load page from undoCloseTab");
+ done();
+}
+
+function done() {
+ tab.linkedBrowser.removeEventListener("load", secondOnLoad, true);
+ getBrowser().removeTab(tab);
+ Services.prefs.clearUserPref("browser.tabs.max_tabs_undo");
+
+ executeSoon(finish);
+}
diff --git a/comm/suite/components/tests/browser/browser_636279.js b/comm/suite/components/tests/browser/browser_636279.js
new file mode 100644
index 0000000000..57d976a760
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_636279.js
@@ -0,0 +1,102 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TAB_STATE_NEEDS_RESTORE = 1;
+const TAB_STATE_RESTORING = 2;
+
+var stateBackup = ss.getBrowserState();
+
+var statePinned = {windows:[{tabs:[
+ {entries:[{url:"http://example.com#1"}], pinned: true}
+]}]};
+
+var state = {windows:[{tabs:[
+ {entries:[{url:"http://example.com#1"}]},
+ {entries:[{url:"http://example.com#2"}]},
+ {entries:[{url:"http://example.com#3"}]},
+ {entries:[{url:"http://example.com#4"}]},
+]}]};
+
+function test() {
+ waitForExplicitFinish();
+
+ registerCleanupFunction(function () {
+ TabsProgressListener.uninit();
+ ss.setBrowserState(stateBackup);
+ });
+
+
+ TabsProgressListener.init();
+
+ window.addEventListener("SSWindowStateReady", function onReady() {
+ window.removeEventListener("SSWindowStateReady", onReady);
+
+ let firstProgress = true;
+
+ TabsProgressListener.setCallback(function (needsRestore, isRestoring) {
+ if (firstProgress) {
+ firstProgress = false;
+ is(isRestoring, 3, "restoring 3 tabs concurrently");
+ } else {
+ ok(isRestoring <= 3, "restoring max. 2 tabs concurrently");
+ }
+
+ if (0 == needsRestore) {
+ TabsProgressListener.unsetCallback();
+ waitForFocus(finish);
+ }
+ });
+
+ ss.setBrowserState(JSON.stringify(state));
+ });
+
+ ss.setBrowserState(JSON.stringify(statePinned));
+}
+
+function countTabs() {
+ let needsRestore = 0, isRestoring = 0;
+ let windowsEnum = Services.wm.getEnumerator("navigator:browser");
+
+ while (windowsEnum.hasMoreElements()) {
+ let window = windowsEnum.getNext();
+ if (window.closed)
+ continue;
+
+ for (let i = 0; i < window.getBrowser().tabs.length; i++) {
+ let browser = window.getBrowser().tabs[i].linkedBrowser;
+ if (browser.__SS_restoreState == TAB_STATE_RESTORING)
+ isRestoring++;
+ else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
+ needsRestore++;
+ }
+ }
+
+ return [needsRestore, isRestoring];
+}
+
+var TabsProgressListener = {
+ init: function () {
+ getBrowser().addTabsProgressListener(this);
+ },
+
+ uninit: function () {
+ this.unsetCallback();
+ getBrowser().removeTabsProgressListener(this);
+ },
+
+ setCallback: function (callback) {
+ this.callback = callback;
+ },
+
+ unsetCallback: function () {
+ delete this.callback;
+ },
+
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ if (this.callback && aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+ this.callback.apply(null, countTabs());
+ }
+}
diff --git a/comm/suite/components/tests/browser/browser_637020.js b/comm/suite/components/tests/browser/browser_637020.js
new file mode 100644
index 0000000000..b035a8c1f8
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_637020.js
@@ -0,0 +1,65 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TEST_URL = "http://mochi.test:8888/browser/browser/components/" +
+ "sessionstore/test/browser_637020_slow.sjs";
+
+const TEST_STATE = {
+ windows: [{
+ tabs: [
+ { entries: [{ url: "about:mozilla" }] },
+ { entries: [{ url: "about:robots" }] }
+ ]
+ }, {
+ tabs: [
+ { entries: [{ url: TEST_URL }] },
+ { entries: [{ url: TEST_URL }] }
+ ]
+ }]
+};
+
+function test() {
+ TestRunner.run();
+}
+
+/**
+ * This test ensures that windows that have just been restored will be marked
+ * as dirty, otherwise _getCurrentState() might ignore them when collecting
+ * state for the first time and we'd just save them as empty objects.
+ *
+ * The dirty state acts as a cache to not collect data from all windows all the
+ * time, so at the beginning, each window must be dirty so that we collect
+ * their state at least once.
+ */
+
+async function runTests() {
+ let win;
+
+ // Wait until the new window has been opened.
+ Services.obs.addObserver(function onOpened(subject) {
+ Services.obs.removeObserver(onOpened, "domwindowopened");
+ win = subject;
+ executeSoon(next);
+ }, "domwindowopened");
+
+ // Set the new browser state that will
+ // restore a window with two slowly loading tabs.
+ await SessionStore.setBrowserState(JSON.stringify(TEST_STATE));
+
+ // The window has now been opened. Check the state that is returned,
+ // this should come from the cache while the window isn't restored, yet.
+ info("the window has been opened");
+ checkWindows();
+
+ // The history has now been restored and the tabs are loading. The data must
+ // now come from the window, if it's correctly been marked as dirty before.
+ await whenDelayedStartupFinished(win, next);
+ info("the delayed startup has finished");
+ checkWindows();
+}
+
+function checkWindows() {
+ let state = JSON.parse(SessionStore.getBrowserState());
+ is(state.windows[0].tabs.length, 2, "first window has two tabs");
+ is(state.windows[1].tabs.length, 2, "second window has two tabs");
+}
diff --git a/comm/suite/components/tests/browser/browser_637020_slow.sjs b/comm/suite/components/tests/browser/browser_637020_slow.sjs
new file mode 100644
index 0000000000..63953b762f
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_637020_slow.sjs
@@ -0,0 +1,18 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const DELAY_MS = "2000";
+
+let timer;
+
+function handleRequest(req, resp) {
+ resp.processAsync();
+ resp.setHeader("Cache-Control", "no-cache", false);
+ resp.setHeader("Content-Type", "text/html;charset=utf-8", false);
+
+ timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ timer.init(() => {
+ resp.write("hi");
+ resp.finish();
+ }, DELAY_MS, Ci.nsITimer.TYPE_ONE_SHOT);
+}
diff --git a/comm/suite/components/tests/browser/browser_645428.js b/comm/suite/components/tests/browser/browser_645428.js
new file mode 100644
index 0000000000..bbb3b1b299
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_645428.js
@@ -0,0 +1,22 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const NOTIFICATION = "sessionstore-browser-state-restored";
+
+function test() {
+ waitForExplicitFinish();
+
+ function observe(subject, topic, data) {
+ if (NOTIFICATION == topic) {
+ finish();
+ ok(true, "TOPIC received");
+ }
+ }
+
+ Services.obs.addObserver(observe, NOTIFICATION);
+ registerCleanupFunction(function () {
+ Services.obs.removeObserver(observe, NOTIFICATION);
+ });
+
+ ss.setBrowserState(JSON.stringify({ windows: [] }));
+}
diff --git a/comm/suite/components/tests/browser/browser_665702-state_session.js b/comm/suite/components/tests/browser/browser_665702-state_session.js
new file mode 100644
index 0000000000..e467337313
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_665702-state_session.js
@@ -0,0 +1,25 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function compareArray(a, b) {
+ if (a.length !== b.length) {
+ return false;
+ }
+ for (let i = 0; i < a.length; i++) {
+ if (a[i] !== b[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+function test() {
+ let currentState = JSON.parse(ss.getBrowserState());
+ ok(currentState.session, "session data returned by getBrowserState");
+
+ let keys = Object.keys(currentState.session);
+ let expectedKeys = ["state", "lastUpdate", "startTime", "recentCrashes"];
+ info("keys "+JSON.stringify(keys.sort())+" expectedKeys "+JSON.stringify(expectedKeys.sort()));
+ ok(compareArray(keys.sort(), expectedKeys.sort()),
+ "session object from getBrowserState has correct keys");
+}
diff --git a/comm/suite/components/tests/browser/browser_687710.js b/comm/suite/components/tests/browser/browser_687710.js
new file mode 100644
index 0000000000..372ecf7ae5
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_687710.js
@@ -0,0 +1,44 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that sessionrestore handles cycles in the shentry graph properly.
+//
+// These cycles shouldn't be there in the first place, but they cause hangs
+// when they mysteriously appear (bug 687710). Docshell code assumes this
+// graph is a tree and tires to walk to the root. But if there's a cycle,
+// there is no root, and we loop forever.
+
+var stateBackup = ss.getBrowserState();
+
+var state = {windows:[{tabs:[{entries:[
+ {
+ docIdentifier: 1,
+ url: "http://example.com",
+ children: [
+ {
+ docIdentifier: 2,
+ url: "http://example.com"
+ }
+ ]
+ },
+ {
+ docIdentifier: 2,
+ url: "http://example.com",
+ children: [
+ {
+ docIdentifier: 1,
+ url: "http://example.com"
+ }
+ ]
+ }
+]}]}]}
+
+function test() {
+ registerCleanupFunction(function () {
+ ss.setBrowserState(stateBackup);
+ });
+
+ /* This test fails by hanging. */
+ ss.setBrowserState(JSON.stringify(state));
+ ok(true, "Didn't hang!");
+}
diff --git a/comm/suite/components/tests/browser/browser_687710_2.js b/comm/suite/components/tests/browser/browser_687710_2.js
new file mode 100644
index 0000000000..5d46bd94d6
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_687710_2.js
@@ -0,0 +1,64 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that the fix for bug 687710 isn't too aggressive -- shentries which are
+// cousins should be able to share bfcache entries.
+
+var stateBackup = ss.getBrowserState();
+
+var state = {entries:[
+ {
+ docIdentifier: 1,
+ url: "http://example.com?1",
+ children: [{ docIdentifier: 10,
+ url: "http://example.com?10" }]
+ },
+ {
+ docIdentifier: 1,
+ url: "http://example.com?1#a",
+ children: [{ docIdentifier: 10,
+ url: "http://example.com?10#aa" }]
+ }
+]};
+
+function test()
+{
+ registerCleanupFunction(function () {
+ ss.setBrowserState(stateBackup);
+ });
+
+ let tab = getBrowser().addTab("about:blank");
+ ss.setTabState(tab, JSON.stringify(state));
+ let history = tab.linkedBrowser.webNavigation.sessionHistory;
+
+ is(history.count, 2, "history.count");
+ for (let i = 0; i < history.count; i++) {
+ for (let j = 0; j < history.count; j++) {
+ compareEntries(i, j, history);
+ }
+ }
+}
+
+function compareEntries(i, j, history)
+{
+ let e1 = history.getEntryAtIndex(i)
+ .QueryInterface(Ci.nsISHEntry)
+ .QueryInterface(Ci.nsISHContainer);
+
+ let e2 = history.getEntryAtIndex(j)
+ .QueryInterface(Ci.nsISHEntry)
+ .QueryInterface(Ci.nsISHContainer);
+
+ ok(e1.sharesDocumentWith(e2),
+ i + ' should share doc with ' + j);
+ is(e1.childCount, e2.childCount,
+ 'Child count mismatch (' + i + ', ' + j + ')');
+
+ for (let c = 0; c < e1.childCount; c++) {
+ let c1 = e1.GetChildAt(c);
+ let c2 = e2.GetChildAt(c);
+
+ ok(c1.sharesDocumentWith(c2),
+ 'Cousins should share documents. (' + i + ', ' + j + ', ' + c + ')');
+ }
+}
diff --git a/comm/suite/components/tests/browser/browser_694378.js b/comm/suite/components/tests/browser/browser_694378.js
new file mode 100644
index 0000000000..8578428d8f
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_694378.js
@@ -0,0 +1,33 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test Summary:
+// 1. call ss.setWindowState with a broken state
+// 1a. ensure that it doesn't throw.
+
+function test() {
+ waitForExplicitFinish();
+
+ let brokenState = {
+ windows: [
+ { tabs: [{ entries: [{ url: "about:mozilla" }] }] }
+ ],
+ selectedWindow: 2
+ };
+ let brokenStateString = JSON.stringify(brokenState);
+
+ let gotError = false;
+ try {
+ ss.setWindowState(window, brokenStateString, true);
+ }
+ catch (ex) {
+ gotError = true;
+ info(ex);
+ }
+
+ ok(!gotError, "ss.setWindowState did not throw an error");
+
+ // Make sure that we reset the state. Use a full state just in case things get crazy.
+ let blankState = { windows: [{ tabs: [{ entries: [{ url: "about:blank" }] }]}]};
+ waitForBrowserState(blankState, finish);
+}
diff --git a/comm/suite/components/tests/browser/browser_bug431826.js b/comm/suite/components/tests/browser/browser_bug431826.js
new file mode 100644
index 0000000000..f4430fc95d
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_bug431826.js
@@ -0,0 +1,42 @@
+function test() {
+ waitForExplicitFinish();
+
+ getBrowser().selectedTab = gBrowser.addTab();
+
+ // Navigate to a site with a broken cert
+ window.addEventListener("DOMContentLoaded", testBrokenCert, true);
+ content.location = "https://nocert.example.com/";
+}
+
+function testBrokenCert() {
+ window.removeEventListener("DOMContentLoaded", testBrokenCert, true);
+
+ // Confirm that we are displaying the contributed error page, not the default
+ ok(/^about:certerror/.test(gBrowser.contentDocument.documentURI), "Broken page should go to about:certerror, not about:neterror");
+
+ // Confirm that the expert section is collapsed
+ var expertDiv = gBrowser.contentDocument.getElementById("expertContent");
+ ok(expertDiv, "Expert content div should exist");
+ ok(expertDiv.hasAttribute("collapsed"), "Expert content should be collapsed by default");
+
+ // Tweak the expert mode pref
+ Services.prefs.setBoolPref("browser.xul.error_pages.expert_bad_cert", true);
+
+ window.addEventListener("DOMContentLoaded", testExpertPref, true);
+ getBrowser().reload();
+}
+
+function testExpertPref() {
+ window.removeEventListener("DOMContentLoaded", testExpertPref, true);
+
+ var expertDiv = gBrowser.contentDocument.getElementById("expertContent");
+ var technicalDiv = gBrowser.contentDocument.getElementById("technicalContent");
+ ok(!expertDiv.hasAttribute("collapsed"), "Expert content should not be collapsed with the expert mode pref set");
+ ok(!technicalDiv.hasAttribute("collapsed"), "Technical content should not be collapsed with the expert mode pref set");
+
+ // Clean up
+ getBrowser().removeCurrentTab();
+ if (Services.prefs.prefHasUserValue("browser.xul.error_pages.expert_bad_cert"))
+ Services.prefs.clearUserPref("browser.xul.error_pages.expert_bad_cert");
+ finish();
+}
diff --git a/comm/suite/components/tests/browser/browser_isempty.js b/comm/suite/components/tests/browser/browser_isempty.js
new file mode 100644
index 0000000000..84ae62b4cb
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_isempty.js
@@ -0,0 +1,28 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Bug 589659 - Lots of mozapps/extensions/test/ failures
+// This introduced isTabEmpty() and isBrowserEmpty() functions, the latter
+// being used in openUILinkIn() which in turn is used by switchToTabHavingURI()
+
+var gWindowObject;
+var gTabCount;
+
+function test() {
+ waitForExplicitFinish();
+ gTabCount = gBrowser.tabs.length;
+
+ gBrowser.selectedTab = gBrowser.addTab();
+ is(isTabEmpty(gBrowser.selectedTab), true, "Added tab is empty");
+ switchToTabHavingURI("about:", true, function(aBrowser) {
+ gWindowObject = aBrowser.contentWindow.wrappedJSObject;
+ end_test();
+ });
+}
+
+function end_test() {
+ gWindowObject.close();
+ is(gBrowser.tabs.length, gTabCount, "We're still at the same number of tabs");
+ finish();
+}
diff --git a/comm/suite/components/tests/browser/browser_markPageAsFollowedLink.js b/comm/suite/components/tests/browser/browser_markPageAsFollowedLink.js
new file mode 100644
index 0000000000..d8ab033cb0
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_markPageAsFollowedLink.js
@@ -0,0 +1,87 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Tests that visits across frames are correctly represented in the database.
+ */
+
+const BASE_URL = "http://mochi.test:8888/browser/suite/common/tests/browser";
+const PAGE_URL = BASE_URL + "/framedPage.html";
+const LEFT_URL = BASE_URL + "/frameLeft.html";
+const RIGHT_URL = BASE_URL + "/frameRight.html";
+
+var gTabLoaded = false;
+var gLeftFrameVisited = false;
+
+var observer = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ let url = aSubject.QueryInterface(Ci.nsIURI).spec;
+ if (url == LEFT_URL ) {
+ is(getTransitionForUrl(url), null,
+ "Embed visits should not get a database entry.");
+ gLeftFrameVisited = true;
+ maybeClickLink();
+ }
+ else if (url == RIGHT_URL ) {
+ is(getTransitionForUrl(url), PlacesUtils.history.TRANSITION_FRAMED_LINK,
+ "User activated visits should get a FRAMED_LINK transition.");
+ finish();
+ }
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver])
+};
+Services.obs.addObserver(observer, "uri-visit-saved");
+
+function test()
+{
+ waitForExplicitFinish();
+ gBrowser.selectedTab = gBrowser.addTab(PAGE_URL);
+ let frameCount = 0;
+ gBrowser.selectedTab.linkedBrowser.addEventListener("DOMContentLoaded",
+ function gBrowserDOMContentLoaded(event)
+ {
+ // Wait for all the frames.
+ if (frameCount++ < 2)
+ return;
+ gBrowser.selectedTab.linkedBrowser.removeEventListener("DOMContentLoaded",
+ gBrowserDOMContentLoaded)
+ gTabLoaded = true;
+ maybeClickLink();
+ }
+ );
+}
+
+function maybeClickLink() {
+ if (gTabLoaded && gLeftFrameVisited) {
+ // Click on the link in the left frame to cause a page load in the
+ // right frame.
+ EventUtils.sendMouseEvent({type: "click"}, "clickme", content.frames[0]);
+ }
+}
+
+function getTransitionForUrl(aUrl)
+{
+ let dbConn = PlacesUtils.history
+ .QueryInterface(Ci.nsPIPlacesDatabase).DBConnection;
+ let stmt = dbConn.createStatement(
+ "SELECT visit_type FROM moz_historyvisits WHERE place_id = " +
+ "(SELECT id FROM moz_places WHERE url = :page_url)");
+ stmt.params.page_url = aUrl;
+ try {
+ if (!stmt.executeStep()) {
+ return null;
+ }
+ return stmt.row.visit_type;
+ }
+ finally {
+ stmt.finalize();
+ }
+}
+
+registerCleanupFunction(function ()
+{
+ gBrowser.removeTab(gBrowser.selectedTab);
+ Services.obs.removeObserver(observer, "uri-visit-saved");
+})
diff --git a/comm/suite/components/tests/browser/frameLeft.html b/comm/suite/components/tests/browser/frameLeft.html
new file mode 100644
index 0000000000..5a54fe353b
--- /dev/null
+++ b/comm/suite/components/tests/browser/frameLeft.html
@@ -0,0 +1,8 @@
+<html>
+ <head>
+ <title>Left frame</title>
+ </head>
+ <body>
+ <a id="clickme" href="frameRight.html" target="right">Open page in the right frame.</a>
+ </body>
+</html>
diff --git a/comm/suite/components/tests/browser/frameRight.html b/comm/suite/components/tests/browser/frameRight.html
new file mode 100644
index 0000000000..226accc349
--- /dev/null
+++ b/comm/suite/components/tests/browser/frameRight.html
@@ -0,0 +1,8 @@
+<html>
+ <head>
+ <title>Right Frame</title>
+ </head>
+ <body>
+ This is the right frame.
+ </body>
+</html>
diff --git a/comm/suite/components/tests/browser/framedPage.html b/comm/suite/components/tests/browser/framedPage.html
new file mode 100644
index 0000000000..d388562e6e
--- /dev/null
+++ b/comm/suite/components/tests/browser/framedPage.html
@@ -0,0 +1,9 @@
+<html>
+ <head>
+ <title>Framed page</title>
+ </head>
+ <frameset cols="*,*">
+ <frame name="left" src="frameLeft.html">
+ <frame name="right" src="about:mozilla">
+ </frameset>
+</html>
diff --git a/comm/suite/components/tests/browser/head.js b/comm/suite/components/tests/browser/head.js
new file mode 100644
index 0000000000..531319d3ff
--- /dev/null
+++ b/comm/suite/components/tests/browser/head.js
@@ -0,0 +1,151 @@
+/* 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/. */
+
+var ss = Cc["@mozilla.org/suite/sessionstore;1"]
+ .getService(Ci.nsISessionStore);
+
+function provideWindow(aCallback, aURL, aFeatures) {
+ function callbackSoon(aWindow) {
+ executeSoon(function executeCallbackSoon() {
+ aCallback(aWindow);
+ });
+ }
+
+ let win = openDialog(getBrowserURL(), "", aFeatures || "chrome,all,dialog=no", aURL);
+ whenWindowLoaded(win, function onWindowLoaded(aWin) {
+ if (!aURL) {
+ info("Loaded a blank window.");
+ callbackSoon(aWin);
+ return;
+ }
+
+ aWin.gBrowser.selectedBrowser.addEventListener("load", function selectedBrowserLoadListener() {
+ aWin.gBrowser.selectedBrowser.removeEventListener("load", selectedBrowserLoadListener, true);
+ callbackSoon(aWin);
+ }, true);
+ });
+}
+
+// This assumes that tests will at least have some state/entries
+function waitForBrowserState(aState, aSetStateCallback) {
+ let windows = [window];
+ let tabsRestored = 0;
+ let expectedTabsRestored = 0;
+ let expectedWindows = aState.windows.length;
+ let windowsOpen = 1;
+ let listening = false;
+ let windowObserving = false;
+
+ aState.windows.forEach(winState => expectedTabsRestored += winState.tabs.length);
+
+ function onSSTabRestored(aEvent) {
+ if (++tabsRestored == expectedTabsRestored) {
+ // Remove the event listener from each window
+ windows.forEach(function(win) {
+ win.getBrowser().tabContainer.removeEventListener("SSTabRestored", onSSTabRestored, true);
+ });
+ listening = false;
+ info("running " + aSetStateCallback.name);
+ executeSoon(aSetStateCallback);
+ }
+ }
+
+ // Used to add our listener to further windows so we can catch SSTabRestored
+ // coming from them when creating a multi-window state.
+ function windowObserver(aSubject, aTopic, aData) {
+ if (aTopic == "domwindowopened") {
+ let newWindow = aSubject.QueryInterface(Ci.nsIDOMWindow);
+ newWindow.addEventListener("load", function newWindowLoad() {
+ newWindow.removeEventListener("load", newWindowLoad);
+
+ if (++windowsOpen == expectedWindows) {
+ Services.ww.unregisterNotification(windowObserver);
+ windowObserving = false;
+ }
+
+ // Track this window so we can remove the progress listener later
+ windows.push(newWindow);
+ // Add the progress listener
+ newWindow.getBrowser().tabContainer.addEventListener("SSTabRestored", onSSTabRestored, true);
+ });
+ }
+ }
+
+ // We only want to register the notification if we expect more than 1 window
+ if (expectedWindows > 1) {
+ registerCleanupFunction(function() {
+ if (windowObserving) {
+ Services.ww.unregisterNotification(windowObserver);
+ }
+ });
+ windowObserving = true;
+ Services.ww.registerNotification(windowObserver);
+ }
+
+ registerCleanupFunction(function() {
+ if (listening) {
+ windows.forEach(function(win) {
+ win.getBrowser().tabContainer.removeEventListener("SSTabRestored", onSSTabRestored, true);
+ });
+ }
+ });
+ // Add the event listener for this window as well.
+ listening = true;
+ getBrowser().tabContainer.addEventListener("SSTabRestored", onSSTabRestored, true);
+
+ // Finally, call setBrowserState
+ ss.setBrowserState(JSON.stringify(aState));
+}
+
+// waitForSaveState waits for a state write but not necessarily for the state to
+// turn dirty.
+function waitForSaveState(aSaveStateCallback) {
+ let observing = false;
+ let topic = "sessionstore-state-write";
+
+ let sessionSaveTimeout = 1000 +
+ Services.prefs.getIntPref("browser.sessionstore.interval");
+
+ function removeObserver() {
+ if (!observing)
+ return;
+ Services.obs.removeObserver(observer, topic, false);
+ observing = false;
+ }
+
+ let timeout = setTimeout(function () {
+ removeObserver();
+ aSaveStateCallback();
+ }, sessionSaveTimeout);
+
+ function observer(aSubject, aTopic, aData) {
+ removeObserver();
+ timeout = clearTimeout(timeout);
+ executeSoon(aSaveStateCallback);
+ }
+
+ registerCleanupFunction(function() {
+ removeObserver();
+ if (timeout) {
+ clearTimeout(timeout);
+ }
+ });
+
+ observing = true;
+ Services.obs.addObserver(observer, topic);
+};
+
+function whenWindowLoaded(aWindow, aCallback) {
+ aWindow.addEventListener("load", function windowLoadListener() {
+ aWindow.removeEventListener("load", windowLoadListener);
+ executeSoon(function executeWhenWindowLoaded() {
+ aCallback(aWindow);
+ });
+ });
+}
+
+var gUniqueCounter = 0;
+function r() {
+ return Date.now() + "-" + (++gUniqueCounter);
+}
diff --git a/comm/suite/components/tests/chrome/chrome.ini b/comm/suite/components/tests/chrome/chrome.ini
new file mode 100644
index 0000000000..cf58b1085c
--- /dev/null
+++ b/comm/suite/components/tests/chrome/chrome.ini
@@ -0,0 +1,4 @@
+[DEFAULT]
+
+[test_idcheck.xul]
+support-files = ../../../../mailnews/test/resources/mailTestUtils.js
diff --git a/comm/suite/components/tests/chrome/test_idcheck.xul b/comm/suite/components/tests/chrome/test_idcheck.xul
new file mode 100644
index 0000000000..213fda008a
--- /dev/null
+++ b/comm/suite/components/tests/chrome/test_idcheck.xul
@@ -0,0 +1,302 @@
+<?xml version="1.0"?>
+
+<!-- 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/. -->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Chrome Window ID Checking Tests"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="RunTest();">
+ <description>Chrome Window ID Checking Tests</description>
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/chrome-harness.js"/>
+
+ <script>
+ <![CDATA[
+ let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
+ getService(Ci.mozIJSSubScriptLoader);
+
+ let rootDir = getRootDirectory(window.location.href);
+ scriptLoader.loadSubScript(rootDir + "mailTestUtils.js", this);
+
+ var gLoadedWindows = {};
+
+ // Have all loaded windows been closed again?
+ function AllWindowsClosed()
+ {
+ return Object.keys(window.gLoadedWindows).length == 0;
+ }
+
+ function DumpElementTree(aElement)
+ {
+ // walk upwards from element to DOM root
+ while (aElement)
+ {
+ // print nodeName, id and index in parent
+ let s = " " + aElement.nodeName + " " + aElement.id + " ";
+ let ix = 0;
+ while (aElement.previousSibling)
+ {
+ aElement = aElement.previousSibling;
+ ++ix;
+ }
+ dump(s + "[" + ix + "]\n");
+ aElement = aElement.parentNode;
+ }
+ }
+
+ function CheckIDs(aDocument, aIgnorableIDs)
+ {
+ var filename = aDocument.location.href.match(/[^/]+$/);
+ // all panes are loaded, now check the ids
+ // first, get the list of all used ids
+ var idList = aDocument.getElementsByAttribute("id", "*");
+ // then store them in another object, checking if it's already there
+ var checkedList = {};
+ var ignoredList = {};
+ for (let i = 0; i < idList.length; ++i)
+ {
+ let id = idList[i].id;
+ let duplicate = (id in checkedList);
+ if (!duplicate)
+ {
+ checkedList[id] = idList[i];
+ }
+ else
+ {
+ // always dump DOM trees of the conflicting elements
+ dump("Double id='" + id + "' detected in " + aDocument.location.href + ":\n");
+ dump(" Tree 0:\n");
+ DumpElementTree(checkedList[id]);
+ dump(" Tree 1:\n");
+ DumpElementTree(idList[i]);
+ }
+ if (!aIgnorableIDs.includes(id))
+ {
+ // if the id is not in our ignore list, show its status
+ ok(!duplicate, "check id: " + filename + "#" + id);
+ }
+ else if (!(id in ignoredList))
+ {
+ // mark ignored id tests as todo,
+ // even though we may never (be able to) fix them
+ ignoredList[id] = idList[i];
+ todo(false, "disabled id checks: " + filename + "#" + id);
+ }
+ }
+
+ // finally, close the loaded window
+ aDocument.defaultView.close();
+ }
+
+ function DisambiguateCharsetMenulist(aDocument, aListID, aPrefix)
+ {
+ let menulist = aDocument.getElementById(aListID)
+ .getElementsByTagName("menuitem");
+ for (let menuitem of menulist)
+ {
+ menuitem.id = aPrefix + menuitem.id;
+ menuitem = menuitem.nextSibling;
+ }
+ }
+
+ function LoadPaneLoop(aDocument, aPanes, aPaneIndex, aForceLoad)
+ {
+ if (aPaneIndex < aPanes.length)
+ {
+ const WAIT_CYCLE = 10;
+ // may need to load this pane
+ let pane = aPanes[aPaneIndex];
+ if (pane.loaded)
+ {
+ // okay, check/load next one
+ setTimeout(LoadPaneLoop, WAIT_CYCLE, aDocument, aPanes, aPaneIndex + 1, true);
+ }
+ else
+ {
+ // force load once and wait until done
+ if (aForceLoad)
+ {
+ try
+ {
+ aDocument.documentElement.showPane.call(aDocument.documentElement, pane);
+ }
+ catch (ignored) {}
+ }
+ setTimeout(LoadPaneLoop, WAIT_CYCLE, aDocument, aPanes, aPaneIndex, false);
+ }
+ }
+ else
+ {
+ // All preference panes are loaded now!
+
+ // The character_encoding_pane contains two template driven menulists
+ // (viewDefaultCharsetList and sendDefaultCharsetList),
+ // both of which autogenerate the *same* ids for their menuitems.
+ // The same ids are generated by the charset list (defaultCharsetList)
+ // on the languages_pane, too.
+ // We alter two of these sets here to avoid unnecessary test failure.
+ // (We probably should remove those RDF templates?)
+ DisambiguateCharsetMenulist(aDocument, "viewDefaultCharsetList", "test_idcheck.1.");
+ DisambiguateCharsetMenulist(aDocument, "sendDefaultCharsetList", "test_idcheck.2.");
+
+ // now check the ids
+ CheckIDs(aDocument, window.gLoadedWindows[aDocument.location.href]);
+ }
+ }
+
+ function CheckPreferences()
+ {
+ this.removeEventListener("load", window.CheckPreferences, false);
+
+ // Prefpanes are loaded lazily, thus we need to trigger each panel manually
+ // before we can check for doubled ids...
+ var panes = this.document.getElementsByTagName("prefpane");
+ setTimeout(LoadPaneLoop, 0, this.document, panes, 0, true);
+ }
+
+ function CheckGenerics()
+ {
+ this.removeEventListener("load", window.CheckGenerics, false);
+ CheckIDs(this.document, window.gLoadedWindows[this.location.href]);
+ }
+
+ function UncountWindow()
+ {
+ if (this.location.href in window.gLoadedWindows)
+ {
+ this.removeEventListener("unload", window.UncountWindow, false);
+ delete window.gLoadedWindows[this.location.href];
+ }
+ }
+
+ function InitTest()
+ {
+ // fake a mail account to avoid the account creation wizard
+ loadLocalMailAccount();
+ }
+
+ function ExitTest()
+ {
+ // remove the mailnews data from the test profile
+ Services.prefs.resetPrefs();
+ }
+
+ function FinishTest()
+ {
+ if (AllWindowsClosed())
+ {
+ // commented out to fix test failures after this due to missing prefs
+ //ExitTest();
+ SimpleTest.finish();
+ }
+ else
+ {
+ setTimeout(FinishTest, 1000);
+ }
+ }
+
+ function RunTest()
+ {
+ SimpleTest.waitForExplicitFinish();
+ InitTest();
+
+ // Basically, this test framework is generic enough to check arbitrary
+ // chrome windows for doubled ids. But certain stuff like preferences
+ // needs some extra processing.
+ // The uriList members have the following format:
+ // "chrome://uri/of/xul.window":
+ // [
+ // check function,
+ // array of IDs to be ignored during in the test
+ // ],
+ var uriList =
+ {
+ // Preferences
+ "chrome://communicator/content/pref/preferences.xul":
+ [
+ window.CheckPreferences,
+ []
+ ],
+
+ // Browser
+ "chrome://navigator/content/navigator.xul":
+ [
+ window.CheckGenerics,
+ ["contentAreaContextSet"]
+ ],
+
+ // MailNews (needs at least one mail account)
+ "chrome://messenger/content/messenger.xul":
+ [
+ window.CheckGenerics,
+ []
+ ],
+ "chrome://messenger/content/messageWindow.xul":
+ [
+ window.CheckGenerics,
+ []
+ ],
+ "chrome://messenger/content/messengercompose/messengercompose.xul":
+ [
+ window.CheckGenerics,
+ []
+ ],
+
+ // Addressbook (needs at least one mail account)
+ "chrome://messenger/content/addressbook/addressbook.xul":
+ [
+ window.CheckGenerics,
+ []
+ ],
+
+ // Composer
+ "chrome://editor/content/editor.xul":
+ [
+ window.CheckGenerics,
+ []
+ ],
+
+ // Error Console
+ "chrome://communicator/content/console/console.xul":
+ [
+ window.CheckGenerics,
+ []
+ ],
+
+ // Chatzilla
+ "chrome://chatzilla/content/chatzilla.xul":
+ [
+ window.CheckGenerics,
+ []
+ ],
+ };
+
+ // run test
+ for (var uri in uriList)
+ {
+ // load the window, but postpone the id check until it's fully loaded
+ window.gLoadedWindows[uri] = uriList[uri][1]; // ignore these ids
+ var win = openDialog(uri, "", "chrome,titlebar,dialog=no,resizable");
+ win.addEventListener("load", uriList[uri][0], false);
+ win.addEventListener("unload", window.UncountWindow, false);
+ }
+
+ // wait for all tests to finish
+ SimpleTest.executeSoon(FinishTest);
+ }
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ </body>
+
+</window>