diff options
Diffstat (limited to 'comm/suite/components/tests/browser')
72 files changed, 5250 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 <input>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); +} |