From 12d5089ebd6a11615888a977bdf9e5a27d0bf90b Mon Sep 17 00:00:00 2001 From: Mike de Boer Date: Tue, 15 Mar 2016 11:09:34 +0100 Subject: [PATCH] Bug 1093153 - enabled and re-factored browser_aboutHome.js to work correctly in e10s mode. r=mak, a=ritu --- browser/base/content/abouthome/aboutHome.js | 12 +- browser/base/content/test/general/browser.ini | 1 - .../content/test/general/browser_aboutHome.js | 1023 +++++++++-------- 3 files changed, 528 insertions(+), 508 deletions(-) diff --git a/browser/base/content/abouthome/aboutHome.js b/browser/base/content/abouthome/aboutHome.js index 7ad7d68c978..71fa3c9d108 100644 --- a/browser/base/content/abouthome/aboutHome.js +++ b/browser/base/content/abouthome/aboutHome.js @@ -152,8 +152,16 @@ function ensureSnippetsMapThen(aCallback) } let cache = new Map(); - let cursorRequest = db.transaction(SNIPPETS_OBJECTSTORE_NAME) - .objectStore(SNIPPETS_OBJECTSTORE_NAME).openCursor(); + let cursorRequest; + try { + cursorRequest = db.transaction(SNIPPETS_OBJECTSTORE_NAME) + .objectStore(SNIPPETS_OBJECTSTORE_NAME).openCursor(); + } catch(ex) { + console.error(ex); + invokeCallbacks(); + return; + } + cursorRequest.onerror = function (event) { invokeCallbacks(); } diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini index a8d7d695a03..a6fd00abe3c 100644 --- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -132,7 +132,6 @@ support-files = [browser_aboutHealthReport.js] skip-if = os == "linux" # Bug 924307 [browser_aboutHome.js] -skip-if = e10s # Bug 1093153 - no about:home support yet [browser_aboutHome_wrapsCorrectly.js] [browser_action_keyword.js] [browser_action_keyword_override.js] diff --git a/browser/base/content/test/general/browser_aboutHome.js b/browser/base/content/test/general/browser_aboutHome.js index 74696e0cdc3..a48248dcbf4 100644 --- a/browser/base/content/test/general/browser_aboutHome.js +++ b/browser/base/content/test/general/browser_aboutHome.js @@ -2,22 +2,16 @@ * http://creativecommons.org/publicdomain/zero/1.0/ */ -/////////////////// -// -// Whitelisting this test. -// As part of bug 1077403, the leaking uncaught rejection should be fixed. -// -thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: Assert is null"); +requestLongerTimeout(2); +ignoreAllUncaughtExceptions(); - -XPCOMUtils.defineLazyModuleGetter(this, "Promise", - "resource://gre/modules/Promise.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Task", - "resource://gre/modules/Task.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "AboutHomeUtils", "resource:///modules/AboutHome.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "AppConstants", + "resource://gre/modules/AppConstants.jsm"); -const TEST_CONTENT_HELPER = "chrome://mochitests/content/browser/browser/base/content/test/general/aboutHome_content_script.js"; +const TEST_CONTENT_HELPER = "chrome://mochitests/content/browser/browser/base/" + + "content/test/general/aboutHome_content_script.js"; var gRightsVersion = Services.prefs.getIntPref("browser.rights.version"); registerCleanupFunction(function() { @@ -28,77 +22,69 @@ registerCleanupFunction(function() { Services.prefs.clearUserPref("browser.rights." + gRightsVersion + ".shown"); }); -var gTests = [ +add_task(function* () { + info("Check that clearing cookies does not clear storage"); -{ - desc: "Check that clearing cookies does not clear storage", - setup: function () - { - Cc["@mozilla.org/observer-service;1"] - .getService(Ci.nsIObserverService) - .notifyObservers(null, "cookie-changed", "cleared"); - }, - run: function (aSnippetsMap) - { - isnot(aSnippetsMap.get("snippets-last-update"), null, - "snippets-last-update should have a value"); - } -}, + yield withSnippetsMap( + () => { + Cc["@mozilla.org/observer-service;1"] + .getService(Ci.nsIObserverService) + .notifyObservers(null, "cookie-changed", "cleared"); + }, + function* () { + isnot(content.gSnippetsMap.get("snippets-last-update"), null, + "snippets-last-update should have a value"); + }); +}); -{ - desc: "Check default snippets are shown", - setup: function () { }, - run: function () - { - let doc = gBrowser.selectedBrowser.contentDocument; +add_task(function* () { + info("Check default snippets are shown"); + + yield withSnippetsMap(null, function* () { + let doc = content.document; let snippetsElt = doc.getElementById("snippets"); ok(snippetsElt, "Found snippets element") is(snippetsElt.getElementsByTagName("span").length, 1, "A default snippet is present."); - } -}, + }); +}); -{ - desc: "Check default snippets are shown if snippets are invalid xml", - setup: function (aSnippetsMap) - { - // This must be some incorrect xhtml code. - aSnippetsMap.set("snippets", "

"); - }, - run: function (aSnippetsMap) - { - let doc = gBrowser.selectedBrowser.contentDocument; +add_task(function* () { + info("Check default snippets are shown if snippets are invalid xml"); - let snippetsElt = doc.getElementById("snippets"); - ok(snippetsElt, "Found snippets element"); - is(snippetsElt.getElementsByTagName("span").length, 1, - "A default snippet is present."); + yield withSnippetsMap( + // This must set some incorrect xhtml code. + snippetsMap => snippetsMap.set("snippets", "

"), + function* () { + let doc = content.document; + let snippetsElt = doc.getElementById("snippets"); + ok(snippetsElt, "Found snippets element"); + is(snippetsElt.getElementsByTagName("span").length, 1, + "A default snippet is present."); - aSnippetsMap.delete("snippets"); - } -}, + content.gSnippetsMap.delete("snippets"); + }); +}); -{ - desc: "Check that performing a search fires a search event and records to " + - "Telemetry.", - setup: function () { }, - run: function* () { +add_task(function* () { + info("Check that performing a search fires a search event and records to Telemetry."); + yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:home" }, function* (browser) { let engine = yield promiseNewEngine("searchSuggestionEngine.xml"); // Make this actually work in healthreport by giving it an ID: Object.defineProperty(engine.wrappedJSObject, "identifier", - {value: "org.mozilla.testsearchsuggestions"}); + { value: "org.mozilla.testsearchsuggestions" }); - let p = promiseContentSearchChange(engine.name); + let p = promiseContentSearchChange(browser, engine.name); Services.search.currentEngine = engine; yield p; - let numSearchesBefore = 0; - let searchEventDeferred = Promise.defer(); - let doc = gBrowser.contentDocument; - let engineName = gBrowser.contentWindow.wrappedJSObject.gContentSearchController.defaultEngine.name; - is(engine.name, engineName, "Engine name in DOM should match engine we just added"); + yield ContentTask.spawn(browser, { expectedName: engine.name }, function* (args) { + let engineName = content.wrappedJSObject.gContentSearchController.defaultEngine.name; + is(engineName, args.expectedName, "Engine name in DOM should match engine we just added"); + }); + let numSearchesBefore = 0; // Get the current number of recorded searches. let histogramKey = engine.identifier + ".abouthome"; try { @@ -110,186 +96,183 @@ var gTests = [ // No searches performed yet, not a problem, |numSearchesBefore| is 0. } - // Perform a search to increase the SEARCH_COUNT histogram. let searchStr = "a search"; - info("Perform a search."); - doc.getElementById("searchText").value = searchStr; - doc.getElementById("searchSubmit").click(); - let expectedURL = Services.search.currentEngine. - getSubmission(searchStr, null, "homepage"). - uri.spec; - let loadPromise = waitForDocLoadAndStopIt(expectedURL).then(() => { - // Make sure the SEARCH_COUNTS histogram has the right key and count. - let hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); - Assert.ok(histogramKey in hs, "histogram with key should be recorded"); - Assert.equal(hs[histogramKey].sum, numSearchesBefore + 1, - "histogram sum should be incremented"); - searchEventDeferred.resolve(); + let expectedURL = Services.search.currentEngine + .getSubmission(searchStr, null, "homepage").uri.spec; + let promise = waitForDocLoadAndStopIt(expectedURL, browser); + + // Perform a search to increase the SEARCH_COUNT histogram. + yield ContentTask.spawn(browser, { searchStr }, function* (args) { + let doc = content.document; + info("Perform a search."); + doc.getElementById("searchText").value = args.searchStr; + doc.getElementById("searchSubmit").click(); }); + yield promise; + + // Make sure the SEARCH_COUNTS histogram has the right key and count. + let hs = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").snapshot(); + Assert.ok(histogramKey in hs, "histogram with key should be recorded"); + Assert.equal(hs[histogramKey].sum, numSearchesBefore + 1, + "histogram sum should be incremented"); + try { - yield Promise.all([searchEventDeferred.promise, loadPromise]); - } catch (ex) { - Cu.reportError(ex); - ok(false, "An error occurred waiting for the search to be performed: " + ex); - } finally { - try { - Services.search.removeEngine(engine); - } catch (ex) {} - } - } -}, + Services.search.removeEngine(engine); + } catch (ex) {} + }); +}); -{ - desc: "Check snippets map is cleared if cached version is old", - setup: function (aSnippetsMap) - { - aSnippetsMap.set("snippets", "test"); - aSnippetsMap.set("snippets-cached-version", 0); - }, - run: function (aSnippetsMap) - { - ok(!aSnippetsMap.has("snippets"), "snippets have been properly cleared"); - ok(!aSnippetsMap.has("snippets-cached-version"), - "cached-version has been properly cleared"); - } -}, +add_task(function* () { + info("Check snippets map is cleared if cached version is old"); -{ - desc: "Check cached snippets are shown if cached version is current", - setup: function (aSnippetsMap) - { - aSnippetsMap.set("snippets", "test"); - }, - run: function (aSnippetsMap) - { - let doc = gBrowser.selectedBrowser.contentDocument; - - let snippetsElt = doc.getElementById("snippets"); - ok(snippetsElt, "Found snippets element"); - is(snippetsElt.innerHTML, "test", "Cached snippet is present."); - - is(aSnippetsMap.get("snippets"), "test", "snippets still cached"); - is(aSnippetsMap.get("snippets-cached-version"), - AboutHomeUtils.snippetsVersion, - "cached-version is correct"); - ok(aSnippetsMap.has("snippets-last-update"), "last-update still exists"); - } -}, - -{ - desc: "Check if the 'Know Your Rights default snippet is shown when 'browser.rights.override' pref is set", - beforeRun: function () - { - Services.prefs.setBoolPref("browser.rights.override", false); - }, - setup: function () { }, - run: function (aSnippetsMap) - { - let doc = gBrowser.selectedBrowser.contentDocument; - let showRights = AboutHomeUtils.showKnowYourRights; - - ok(showRights, "AboutHomeUtils.showKnowYourRights should be TRUE"); - - let snippetsElt = doc.getElementById("snippets"); - ok(snippetsElt, "Found snippets element"); - is(snippetsElt.getElementsByTagName("a")[0].href, "about:rights", "Snippet link is present."); - - Services.prefs.clearUserPref("browser.rights.override"); - } -}, - -{ - desc: "Check if the 'Know Your Rights default snippet is NOT shown when 'browser.rights.override' pref is NOT set", - beforeRun: function () - { - Services.prefs.setBoolPref("browser.rights.override", true); - }, - setup: function () { }, - run: function (aSnippetsMap) - { - let doc = gBrowser.selectedBrowser.contentDocument; - let rightsData = AboutHomeUtils.knowYourRightsData; - - ok(!rightsData, "AboutHomeUtils.knowYourRightsData should be FALSE"); - - let snippetsElt = doc.getElementById("snippets"); - ok(snippetsElt, "Found snippets element"); - ok(snippetsElt.getElementsByTagName("a")[0].href != "about:rights", "Snippet link should not point to about:rights."); - - Services.prefs.clearUserPref("browser.rights.override"); - } -}, - -{ - desc: "Check POST search engine support", - setup: function() {}, - run: function* () - { - let deferred = Promise.defer(); - let currEngine = Services.search.defaultEngine; - let searchObserver = Task.async(function* search_observer(aSubject, aTopic, aData) { - let engine = aSubject.QueryInterface(Ci.nsISearchEngine); - info("Observer: " + aData + " for " + engine.name); - - if (aData != "engine-added") - return; - - if (engine.name != "POST Search") - return; - - // Ready to execute the tests! - let needle = "Search for something awesome."; - let document = gBrowser.selectedBrowser.contentDocument; - let searchText = document.getElementById("searchText"); - - let p = promiseContentSearchChange(engine.name); - Services.search.defaultEngine = engine; - yield p; - - searchText.value = needle; - searchText.focus(); - EventUtils.synthesizeKey("VK_RETURN", {}); - - registerCleanupFunction(function() { - Services.search.removeEngine(engine); - Services.search.defaultEngine = currEngine; - }); - - - // When the search results load, check them for correctness. - waitForLoad(function() { - let loadedText = gBrowser.contentDocument.body.textContent; - ok(loadedText, "search page loaded"); - is(loadedText, "searchterms=" + escape(needle.replace(/\s/g, "+")), - "Search text should arrive correctly"); - deferred.resolve(); - }); + yield withSnippetsMap( + snippetsMap => { + snippetsMap.set("snippets", "test"); + snippetsMap.set("snippets-cached-version", 0); + }, + function* () { + let snippetsMap = content.gSnippetsMap; + ok(!snippetsMap.has("snippets"), "snippets have been properly cleared"); + ok(!snippetsMap.has("snippets-cached-version"), + "cached-version has been properly cleared"); }); - Services.obs.addObserver(searchObserver, "browser-search-engine-modified", false); - registerCleanupFunction(function () { - Services.obs.removeObserver(searchObserver, "browser-search-engine-modified"); +}); + +add_task(function* () { + info("Check cached snippets are shown if cached version is current"); + + yield withSnippetsMap( + snippetsMap => snippetsMap.set("snippets", "test"), + function* (args) { + let doc = content.document; + let snippetsMap = content.gSnippetsMap + + let snippetsElt = doc.getElementById("snippets"); + ok(snippetsElt, "Found snippets element"); + is(snippetsElt.innerHTML, "test", "Cached snippet is present."); + + is(snippetsMap.get("snippets"), "test", "snippets still cached"); + is(snippetsMap.get("snippets-cached-version"), + args.expectedVersion, + "cached-version is correct"); + ok(snippetsMap.has("snippets-last-update"), "last-update still exists"); + }, { expectedVersion: AboutHomeUtils.snippetsVersion }); +}); + +add_task(function* () { + info("Check if the 'Know Your Rights default snippet is shown when " + + "'browser.rights.override' pref is set"); + + Services.prefs.setBoolPref("browser.rights.override", false); + + ok(AboutHomeUtils.showKnowYourRights, "AboutHomeUtils.showKnowYourRights should be TRUE"); + + yield withSnippetsMap(null, function* () { + let doc = content.document; + let snippetsElt = doc.getElementById("snippets"); + ok(snippetsElt, "Found snippets element"); + is(snippetsElt.getElementsByTagName("a")[0].href, "about:rights", + "Snippet link is present."); + }); + + Services.prefs.clearUserPref("browser.rights.override"); +}); + +add_task(function* () { + info("Check if the 'Know Your Rights default snippet is NOT shown when " + + "'browser.rights.override' pref is NOT set"); + + Services.prefs.setBoolPref("browser.rights.override", true); + + let rightsData = AboutHomeUtils.knowYourRightsData; + ok(!rightsData, "AboutHomeUtils.knowYourRightsData should be FALSE"); + + yield withSnippetsMap(null, function*() { + let doc = content.document; + let snippetsElt = doc.getElementById("snippets"); + ok(snippetsElt, "Found snippets element"); + ok(snippetsElt.getElementsByTagName("a")[0].href != "about:rights", + "Snippet link should not point to about:rights."); + }); + + Services.prefs.clearUserPref("browser.rights.override"); +}); + +add_task(function* () { + info("Check POST search engine support"); + + yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:home" }, function* (browser) { + return new Promise(resolve => { + let currEngine = Services.search.defaultEngine; + let searchObserver = Task.async(function* search_observer(subject, topic, data) { + let engine = subject.QueryInterface(Ci.nsISearchEngine); + info("Observer: " + data + " for " + engine.name); + + if (data != "engine-added") + return; + + if (engine.name != "POST Search") + return; + + // Ready to execute the tests! + let needle = "Search for something awesome."; + + let p = promiseContentSearchChange(browser, engine.name); + Services.search.defaultEngine = engine; + yield p; + + registerCleanupFunction(function() { + Services.search.removeEngine(engine); + Services.search.defaultEngine = currEngine; + }); + + let promise = BrowserTestUtils.browserLoaded(browser); + + yield ContentTask.spawn(browser, { needle }, function* (args) { + let doc = content.document; + doc.getElementById("searchText").value = args.needle; + doc.getElementById("searchSubmit").click(); + }); + + yield promise; + + // When the search results load, check them for correctness. + yield ContentTask.spawn(browser, { needle }, function* (args) { + let loadedText = content.document.body.textContent; + ok(loadedText, "search page loaded"); + is(loadedText, "searchterms=" + escape(args.needle.replace(/\s/g, "+")), + "Search text should arrive correctly"); + }); + + resolve(); + }); + Services.obs.addObserver(searchObserver, "browser-search-engine-modified", false); + registerCleanupFunction(function () { + Services.obs.removeObserver(searchObserver, "browser-search-engine-modified"); + }); + Services.search.addEngine("http://test:80/browser/browser/base/content/test/general/POSTSearchEngine.xml", + null, null, false); }); - Services.search.addEngine("http://test:80/browser/browser/base/content/test/general/POSTSearchEngine.xml", - null, null, false); - return deferred.promise; - } -}, + }); +}); -{ - desc: "Make sure that a page can't imitate about:home", - setup: function () { }, - run: function (aSnippetsMap) - { - let deferred = Promise.defer(); +add_task(function* () { + info("Make sure that a page can't imitate about:home"); - let browser = gBrowser.selectedBrowser; - waitForLoad(() => { - let button = browser.contentDocument.getElementById("settings"); + yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:home" }, function* (browser) { + let promise = BrowserTestUtils.browserLoaded(browser); + browser.loadURI("https://example.com/browser/browser/base/content/test/general/test_bug959531.html"); + yield promise; + + yield ContentTask.spawn(browser, null, function* () { + let button = content.document.getElementById("settings"); ok(button, "Found settings button in test page"); button.click(); + }); + yield new Promise(resolve => { // It may take a few turns of the event loop before the window // is displayed, so we wait. function check(n) { @@ -302,188 +285,227 @@ var gTests = [ if (n > 0) { executeSoon(() => check(n-1)); } else { - deferred.resolve(); + resolve(); } } check(5); }); + }); +}); - browser.loadURI("https://example.com/browser/browser/base/content/test/general/test_bug959531.html"); - return deferred.promise; - } -}, - -{ +add_task(function* () { // See browser_contentSearchUI.js for comprehensive content search UI tests. - desc: "Search suggestion smoke test", - setup: function() {}, - run: function() - { - return Task.spawn(function* () { - // Add a test engine that provides suggestions and switch to it. - let engine = yield promiseNewEngine("searchSuggestionEngine.xml"); - let p = promiseContentSearchChange(engine.name); - Services.search.currentEngine = engine; - yield p; + info("Search suggestion smoke test"); + yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:home" }, function* (browser) { + // Add a test engine that provides suggestions and switch to it. + let engine = yield promiseNewEngine("searchSuggestionEngine.xml"); + let p = promiseContentSearchChange(browser, engine.name); + Services.search.currentEngine = engine; + yield p; + + yield ContentTask.spawn(browser, null, function* () { // Avoid intermittent failures. - gBrowser.contentWindow.wrappedJSObject.gContentSearchController.remoteTimeout = 5000; + content.wrappedJSObject.gContentSearchController.remoteTimeout = 5000; // Type an X in the search input. - let input = gBrowser.contentDocument.getElementById("searchText"); + let input = content.document.getElementById("searchText"); input.focus(); - EventUtils.synthesizeKey("x", {}); - - // Wait for the search suggestions to become visible. - let table = - gBrowser.contentDocument.getElementById("searchSuggestionTable"); - let deferred = Promise.defer(); - let observer = new MutationObserver(() => { - if (input.getAttribute("aria-expanded") == "true") { - observer.disconnect(); - ok(!table.hidden, "Search suggestion table unhidden"); - deferred.resolve(); - } - }); - observer.observe(input, { - attributes: true, - attributeFilter: ["aria-expanded"], - }); - yield deferred.promise; - - // Empty the search input, causing the suggestions to be hidden. - EventUtils.synthesizeKey("a", { accelKey: true }); - EventUtils.synthesizeKey("VK_DELETE", {}); - ok(table.hidden, "Search suggestion table hidden"); - - try { - Services.search.removeEngine(engine); - } catch (ex) { } }); - } -}, -{ - desc: "Clicking suggestion list while composing", - setup: function() {}, - run: function() - { - return Task.spawn(function* () { - // Add a test engine that provides suggestions and switch to it. - let engine = yield promiseNewEngine("searchSuggestionEngine.xml"); - let p = promiseContentSearchChange(engine.name); - Services.search.currentEngine = engine; - yield p; + yield BrowserTestUtils.synthesizeKey("x", {}, browser); + + yield ContentTask.spawn(browser, null, function* () { + // Wait for the search suggestions to become visible. + let table = content.document.getElementById("searchSuggestionTable"); + let input = content.document.getElementById("searchText"); + + yield new Promise(resolve => { + let observer = new content.MutationObserver(() => { + if (input.getAttribute("aria-expanded") == "true") { + observer.disconnect(); + ok(!table.hidden, "Search suggestion table unhidden"); + resolve(); + } + }); + observer.observe(input, { + attributes: true, + attributeFilter: ["aria-expanded"], + }); + }); + }); + + // Empty the search input, causing the suggestions to be hidden. + yield BrowserTestUtils.synthesizeKey("a", { accelKey: true }, browser); + yield BrowserTestUtils.synthesizeKey("VK_DELETE", {}, browser); + + yield ContentTask.spawn(browser, null, function* () { + let table = content.document.getElementById("searchSuggestionTable"); + yield ContentTaskUtils.waitForCondition(() => table.hidden, + "Search suggestion table hidden"); + }); + + try { + Services.search.removeEngine(engine); + } catch (ex) { } + }); +}); + +add_task(function* () { + info("Clicking suggestion list while composing"); + + yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:home" }, function* (browser) { + // Add a test engine that provides suggestions and switch to it. + let engine = yield promiseNewEngine("searchSuggestionEngine.xml"); + let p = promiseContentSearchChange(browser, engine.name); + Services.search.currentEngine = engine; + yield p; + + yield ContentTask.spawn(browser, null, function* () { // Start composition and type "x" - let input = gBrowser.contentDocument.getElementById("searchText"); + let input = content.document.getElementById("searchText"); input.focus(); - EventUtils.synthesizeComposition({ type: "compositionstart", data: "" }, - gBrowser.contentWindow); - EventUtils.synthesizeCompositionChange({ - composition: { - string: "x", - clauses: [ - { length: 1, attr: EventUtils.COMPOSITION_ATTR_RAW_CLAUSE } - ] - }, - caret: { start: 1, length: 0 } - }, gBrowser.contentWindow); + }); - let searchController = - gBrowser.contentWindow.wrappedJSObject.gContentSearchController; + yield BrowserTestUtils.synthesizeComposition({ + type: "compositionstart", + data: "" + }, browser); + yield BrowserTestUtils.synthesizeCompositionChange({ + composition: { + string: "x", + clauses: [ + { length: 1, attr: Ci.nsITextInputProcessor.ATTR_RAW_CLAUSE } + ] + }, + caret: { start: 1, length: 0 } + }, browser); + + yield ContentTask.spawn(browser, null, function* () { + let searchController = content.wrappedJSObject.gContentSearchController; // Wait for the search suggestions to become visible. let table = searchController._suggestionsList; - let deferred = Promise.defer(); - let observer = new MutationObserver(() => { - if (input.getAttribute("aria-expanded") == "true") { - observer.disconnect(); - ok(!table.hidden, "Search suggestion table unhidden"); - deferred.resolve(); - } - }); - observer.observe(input, { - attributes: true, - attributeFilter: ["aria-expanded"], - }); - yield deferred.promise; + let input = content.document.getElementById("searchText"); + + yield new Promise(resolve => { + let observer = new content.MutationObserver(() => { + if (input.getAttribute("aria-expanded") == "true") { + observer.disconnect(); + ok(!table.hidden, "Search suggestion table unhidden"); + resolve(); + } + }); + observer.observe(input, { + attributes: true, + attributeFilter: ["aria-expanded"], + }); + }); - // Click the second suggestion. - let expectedURL = Services.search.currentEngine. - getSubmission("xbar", null, "homepage"). - uri.spec; - let loadPromise = waitForDocLoadAndStopIt(expectedURL); let row = table.children[1]; + row.setAttribute("id", "TEMPID"); + // ContentSearchUIController looks at the current selectedIndex when // performing a search. Synthesizing the mouse event on the suggestion // doesn't actually mouseover the suggestion and trigger it to be flagged // as selected, so we manually select it first. searchController.selectedIndex = 1; - EventUtils.synthesizeMouseAtCenter(row, {button: 0}, gBrowser.contentWindow); - yield loadPromise; - ok(input.value == "x", "Input value did not change"); }); - } -}, -{ - desc: "Pressing any key should focus the search box in the page, and send the key to it", - setup: function () {}, - run: Task.async(function* () { - let doc = gBrowser.selectedBrowser.contentDocument; - let logo = doc.getElementById("brandLogo"); - let searchInput = doc.getElementById("searchText"); - EventUtils.synthesizeMouseAtCenter(logo, {}); - isnot(searchInput, doc.activeElement, "Search input should not be the active element."); + // Click the second suggestion. + let expectedURL = Services.search.currentEngine + .getSubmission("xbar", null, "homepage").uri.spec; + let loadPromise = waitForDocLoadAndStopIt(expectedURL); + yield BrowserTestUtils.synthesizeMouseAtCenter("#TEMPID", { + button: 0 + }, browser); + yield loadPromise; - EventUtils.synthesizeKey("a", {}); - yield promiseWaitForCondition(() => doc.activeElement === searchInput); - is(searchInput, doc.activeElement, "Search input should be the active element."); - is(searchInput.value, "a", "Search input should be 'a'."); - }) -}, -{ - desc: "Cmd+k should focus the search box in the page when the search box in the toolbar is absent", - setup: function () { - // Remove the search bar from toolbar - CustomizableUI.removeWidgetFromArea("search-container"); - }, - run: Task.async(function* () { - let doc = gBrowser.selectedBrowser.contentDocument; - let logo = doc.getElementById("brandLogo"); - let searchInput = doc.getElementById("searchText"); + yield ContentTask.spawn(browser, null, function* () { + let input = content.document.getElementById("searchText"); + ok(input.value == "x", "Input value did not change"); - EventUtils.synthesizeMouseAtCenter(logo, {}); - isnot(searchInput, doc.activeElement, "Search input should not be the active element."); + let row = content.document.getElementById("TEMPID"); + if (row) { + row.removeAttribute("id"); + } + }); + }); +}); + +add_task(function* () { + info("Pressing any key should focus the search box in the page, and send the key to it"); + + yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:home" }, function* (browser) { + yield BrowserTestUtils.synthesizeMouseAtCenter("#brandLogo", {}, browser); + + yield ContentTask.spawn(browser, null, function* () { + let doc = content.document; + isnot(doc.getElementById("searchText"), doc.activeElement, + "Search input should not be the active element."); + }); + + yield BrowserTestUtils.synthesizeKey("a", {}, browser); + + yield ContentTask.spawn(browser, null, function* () { + let doc = content.document; + let searchInput = doc.getElementById("searchText"); + yield ContentTaskUtils.waitForCondition(() => doc.activeElement === searchInput, + "Search input should be the active element."); + is(searchInput.value, "a", "Search input should be 'a'."); + }); + }); +}); + +add_task(function* () { + info("Cmd+k should focus the search box in the page when the search box in the toolbar is absent"); + + // Remove the search bar from toolbar + CustomizableUI.removeWidgetFromArea("search-container"); + + yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:home" }, function* (browser) { + yield BrowserTestUtils.synthesizeMouseAtCenter("#brandLogo", {}, browser); + yield ContentTask.spawn(browser, null, function* () { + let doc = content.document; + isnot(doc.getElementById("searchText"), doc.activeElement, + "Search input should not be the active element."); + }); EventUtils.synthesizeKey("k", { accelKey: true }); - yield promiseWaitForCondition(() => doc.activeElement === searchInput); - is(searchInput, doc.activeElement, "Search input should be the active element."); - CustomizableUI.reset(); - }) -}, -{ - desc: "Cmd+k should focus the search box in the toolbar when it's present", - setup: function () {}, - run: Task.async(function* () { - let logo = gBrowser.selectedBrowser.contentDocument.getElementById("brandLogo"); + + yield ContentTask.spawn(browser, null, function* () { + let doc = content.document; + let searchInput = doc.getElementById("searchText"); + + yield ContentTaskUtils.waitForCondition(() => doc.activeElement === searchInput, + "Search input should be the active element."); + }); + }); + + CustomizableUI.reset(); +}); + +add_task(function* () { + info("Cmd+k should focus the search box in the toolbar when it's present"); + + yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:home" }, function* (browser) { + yield BrowserTestUtils.synthesizeMouseAtCenter("#brandLogo", {}, browser); + let doc = window.document; let searchInput = doc.getElementById("searchbar").textbox.inputField; - - EventUtils.synthesizeMouseAtCenter(logo, {}); isnot(searchInput, doc.activeElement, "Search bar should not be the active element."); EventUtils.synthesizeKey("k", { accelKey: true }); yield promiseWaitForCondition(() => doc.activeElement === searchInput); is(searchInput, doc.activeElement, "Search bar should be the active element."); - }) -}, -{ - desc: "Sync button should open about:preferences#sync", - setup: function () {}, - run: Task.async(function* () { - let syncButton = gBrowser.selectedBrowser.contentDocument.getElementById("sync"); + }); +}); + +add_task(function* () { + info("Sync button should open about:preferences#sync"); + + yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:home" }, function* (browser) { let oldOpenPrefs = window.openPreferences; let openPrefsPromise = new Promise(resolve => { window.openPreferences = function (pane, params) { @@ -491,172 +513,163 @@ var gTests = [ }; }); - yield EventUtils.synthesizeMouseAtCenter(syncButton, {}, gBrowser.contentWindow); + yield BrowserTestUtils.synthesizeMouseAtCenter("#sync", {}, browser); let result = yield openPrefsPromise; window.openPreferences = oldOpenPrefs; is(result.pane, "paneSync", "openPreferences should be called with paneSync"); - is(result.params.urlParams.entrypoint, "abouthome", "openPreferences should be called with abouthome entrypoint"); - }) -}, -{ - desc: "Pressing Space while the Addons button is focussed should activate it", - setup: function () {}, - run: Task.async(function* () { - // Skip this test on Mac, because Space doesn't activate the button there. - if (navigator.platform.indexOf("Mac") == 0) { - return Promise.resolve(); - } + is(result.params.urlParams.entrypoint, "abouthome", + "openPreferences should be called with abouthome entrypoint"); + }); +}); +add_task(function* () { + info("Pressing Space while the Addons button is focused should activate it"); + + // Skip this test on Mac, because Space doesn't activate the button there. + if (AppConstants.platform == "macosx") { + return; + } + + yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:home" }, function* (browser) { info("Waiting for about:addons tab to open..."); let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, "about:addons"); - let addOnsButton = gBrowser.selectedBrowser.contentDocument.getElementById("addons"); - addOnsButton.focus(); - EventUtils.synthesizeKey(" ", {}); + + yield ContentTask.spawn(browser, null, function* () { + let addOnsButton = content.document.getElementById("addons"); + addOnsButton.focus(); + }); + yield BrowserTestUtils.synthesizeKey(" ", {}, browser); + let tab = yield promiseTabOpened; - is(tab.linkedBrowser.currentURI.spec, "about:addons", "Should have seen the about:addons tab"); + is(tab.linkedBrowser.currentURI.spec, "about:addons", + "Should have seen the about:addons tab"); yield BrowserTestUtils.removeTab(tab); - }) -} - -]; - -function test() -{ - waitForExplicitFinish(); - requestLongerTimeout(2); - ignoreAllUncaughtExceptions(); - - Task.spawn(function () { - for (let test of gTests) { - info(test.desc); - - if (test.beforeRun) - yield test.beforeRun(); - - // Create a tab to run the test. - let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank"); - - // Add an event handler to modify the snippets map once it's ready. - let snippetsPromise = promiseSetupSnippetsMap(tab, test.setup); - - // Start loading about:home and wait for it to complete. - yield promiseTabLoadEvent(tab, "about:home", "AboutHomeLoadSnippetsCompleted"); - - // This promise should already be resolved since the page is done, - // but we still want to get the snippets map out of it. - let snippetsMap = yield snippetsPromise; - - info("Running test"); - yield test.run(snippetsMap); - info("Cleanup"); - gBrowser.removeCurrentTab(); - } - }).then(finish, ex => { - ok(false, "Unexpected Exception: " + ex); - finish(); }); -} +}); /** * Cleans up snippets and ensures that by default we don't try to check for * remote snippets since that may cause network bustage or slowness. * - * @param aTab - * The tab containing about:home. * @param aSetupFn * The setup function to be run. * @return {Promise} resolved when the snippets are ready. Gets the snippets map. */ -function promiseSetupSnippetsMap(aTab, aSetupFn) -{ - let deferred = Promise.defer(); - info("Waiting for snippets map"); - aTab.linkedBrowser.addEventListener("AboutHomeLoadSnippets", function load(event) { - aTab.linkedBrowser.removeEventListener("AboutHomeLoadSnippets", load, true); +function* withSnippetsMap(setupFn, testFn, testArgs = null) { + let setupFnSource; + if (setupFn) { + setupFnSource = setupFn.toSource(); + } - let cw = aTab.linkedBrowser.contentWindow.wrappedJSObject; + yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank" }, function* (browser) { // The snippets should already be ready by this point. Here we're // just obtaining a reference to the snippets map. - cw.ensureSnippetsMapThen(function (aSnippetsMap) { - aSnippetsMap = Cu.waiveXrays(aSnippetsMap); - info("Got snippets map: " + - "{ last-update: " + aSnippetsMap.get("snippets-last-update") + - ", cached-version: " + aSnippetsMap.get("snippets-cached-version") + - " }"); - // Don't try to update. - aSnippetsMap.set("snippets-last-update", Date.now()); - aSnippetsMap.set("snippets-cached-version", AboutHomeUtils.snippetsVersion); - // Clear snippets. - aSnippetsMap.delete("snippets"); - aSetupFn(aSnippetsMap); - deferred.resolve(aSnippetsMap); + yield ContentTask.spawn(browser, { + setupFnSource, + version: AboutHomeUtils.snippetsVersion, + }, function* (args) { + yield new Promise(resolve => { + let onAfterLocationChange = () => { + let document = content.document; + // We're not using Promise-based listeners, because they resolve asynchronously. + // The snippets test setup code relies on synchronous behaviour here. + document.addEventListener("AboutHomeLoadSnippets", function loadSnippets() { + document.removeEventListener("AboutHomeLoadSnippets", loadSnippets); + + let updateSnippets; + if (args.setupFnSource) { + updateSnippets = eval(`(() => (${args.setupFnSource}))()`); + } + + content.wrappedJSObject.ensureSnippetsMapThen(snippetsMap => { + snippetsMap = Cu.waiveXrays(snippetsMap); + info("Got snippets map: " + + "{ last-update: " + snippetsMap.get("snippets-last-update") + + ", cached-version: " + snippetsMap.get("snippets-cached-version") + + " }"); + // Don't try to update. + snippetsMap.set("snippets-last-update", Date.now()); + snippetsMap.set("snippets-cached-version", args.version); + // Clear snippets. + snippetsMap.delete("snippets"); + + if (updateSnippets) { + updateSnippets(snippetsMap); + } + + // Tack it to the global object + content.gSnippetsMap = snippetsMap; + + resolve(); + }); + }); + }; + + // We'd like to listen to the 'AboutHomeLoadSnippets' event on a fresh + // document as soon as technically possible, so we use webProgress. + let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebProgress); + let wpl = { + onLocationChange: () => { + onAfterLocationChange(); + webProgress.removeProgressListener(wpl); + }, + onProgressChange: () => {}, + onStatusChange: () => {}, + onSecurityChange: () => {}, + QueryInterface: XPCOMUtils.generateQI([ + Ci.nsIWebProgressListener, + Ci.nsISupportsWeakReference, + Ci.nsISupports + ]) + }; + webProgress.addProgressListener(wpl, Ci.nsIWebProgress.NOTIFY_STATE_REQUEST | + Ci.nsIWebProgress.NOTIFY_LOCATION); + + // Set the URL to 'about:home' here to allow capturing the 'AboutHomeLoadSnippets' + // event. + content.document.location.href = "about:home"; + }); }); - }, true, true); - return deferred.promise; -} -function waitForLoad(cb) { - let browser = gBrowser.selectedBrowser; - browser.addEventListener("load", function listener() { - if (browser.currentURI.spec == "about:blank") - return; - info("Page loaded: " + browser.currentURI.spec); - browser.removeEventListener("load", listener, true); - - cb(); - }, true); -} - -function promiseWaitForEvent(node, type, capturing) { - return new Promise((resolve) => { - node.addEventListener(type, function listener(event) { - node.removeEventListener(type, listener, capturing); - resolve(event); - }, capturing); + yield ContentTask.spawn(browser, testArgs, testFn); }); } -var promisePrefsOpen = Task.async(function*() { - info("Waiting for the preferences tab to open..."); - let event = yield promiseWaitForEvent(gBrowser.tabContainer, "TabOpen", true); - let tab = event.target; - yield promiseTabLoadEvent(tab); - is(tab.linkedBrowser.currentURI.spec, "about:preferences#search", "Should have seen the prefs tab"); - gBrowser.removeTab(tab); -}); - -function promiseContentSearchChange(newEngineName) { - return new Promise(resolve => { - content.addEventListener("ContentSearchService", function listener(aEvent) { - if (aEvent.detail.type == "CurrentState" && - gBrowser.contentWindow.wrappedJSObject.gContentSearchController.defaultEngine.name == newEngineName) { - content.removeEventListener("ContentSearchService", listener); - resolve(); - } +function promiseContentSearchChange(browser, newEngineName) { + return ContentTask.spawn(browser, { newEngineName }, function* (args) { + return new Promise(resolve => { + content.addEventListener("ContentSearchService", function listener(aEvent) { + if (aEvent.detail.type == "CurrentState" && + content.wrappedJSObject.gContentSearchController.defaultEngine.name == args.newEngineName) { + content.removeEventListener("ContentSearchService", listener); + resolve(); + } + }); }); }); } function promiseNewEngine(basename) { info("Waiting for engine to be added: " + basename); - let addDeferred = Promise.defer(); - let url = getRootDirectory(gTestPath) + basename; - Services.search.addEngine(url, null, "", false, { - onSuccess: function (engine) { - info("Search engine added: " + basename); - registerCleanupFunction(() => { - try { - Services.search.removeEngine(engine); - } catch (ex) { /* Can't remove the engine more than once */ } - }); - addDeferred.resolve(engine); - }, - onError: function (errCode) { - ok(false, "addEngine failed with error code " + errCode); - addDeferred.reject(); - }, + return new Promise((resolve, reject) => { + let url = getRootDirectory(gTestPath) + basename; + Services.search.addEngine(url, null, "", false, { + onSuccess: function (engine) { + info("Search engine added: " + basename); + registerCleanupFunction(() => { + try { + Services.search.removeEngine(engine); + } catch (ex) { /* Can't remove the engine more than once */ } + }); + resolve(engine); + }, + onError: function (errCode) { + ok(false, "addEngine failed with error code " + errCode); + reject(); + }, + }); }); - return addDeferred.promise; }