From 1d863b1c0953185efba351d1d342cfdeed2e0f1c Mon Sep 17 00:00:00 2001 From: Gijs Kruitbosch Date: Mon, 25 Apr 2016 17:27:35 +0100 Subject: [PATCH] Bug 1267289 - add more URL bar tests and fix issue with error pages, r=mikedeboer,mconley This adds tests for issues brought up in bug 231393, bug 264610, bug 302575 and bug 1129564, all of which fed into the current implementation of userTypedClear/userTypedValue. I intend to move us away from userTypedClear, but I'm keen not to regress any of these issues, so I'm adding automated tests to ensure that doesn't happen. MozReview-Commit-ID: 1up2MIXzkzG --HG-- extra : rebase_source : 4d37f13895b8c7e7aba5331664718582c6b2136c --- browser/base/content/test/urlbar/browser.ini | 3 + .../browser_urlbarHashChangeProxyState.js | 111 ++++++++++++++++++ ...rowser_urlbarKeepStateAcrossTabSwitches.js | 49 ++++++++ ...browser_urlbarUpdateForDomainCompletion.js | 17 +++ .../components/sessionstore/SessionStore.jsm | 6 +- .../content/content-sessionStore.js | 2 +- .../BrowserTestUtils/BrowserTestUtils.jsm | 50 +++++++- 7 files changed, 229 insertions(+), 9 deletions(-) create mode 100644 browser/base/content/test/urlbar/browser_urlbarHashChangeProxyState.js create mode 100644 browser/base/content/test/urlbar/browser_urlbarKeepStateAcrossTabSwitches.js create mode 100644 browser/base/content/test/urlbar/browser_urlbarUpdateForDomainCompletion.js diff --git a/browser/base/content/test/urlbar/browser.ini b/browser/base/content/test/urlbar/browser.ini index acde12a2083a..b26e0127f08f 100644 --- a/browser/base/content/test/urlbar/browser.ini +++ b/browser/base/content/test/urlbar/browser.ini @@ -51,6 +51,8 @@ support-files = [browser_urlbarEnter.js] [browser_urlbarEnterAfterMouseOver.js] skip-if = os == "linux" # Bug 1073339 - Investigate autocomplete test unreliability on Linux/e10s +[browser_urlbarHashChangeProxyState.js] +[browser_urlbarKeepStateAcrossTabSwitches.js] [browser_urlbarRevert.js] [browser_urlbarSearchSingleWordNotification.js] [browser_urlbarSearchSuggestions.js] @@ -67,6 +69,7 @@ support-files = searchSuggestionEngine.sjs [browser_urlbarStop.js] [browser_urlbarTrimURLs.js] +[browser_urlbarUpdateForDomainCompletion.js] [browser_urlbar_autoFill_backspaced.js] [browser_urlbar_blanking.js] support-files = diff --git a/browser/base/content/test/urlbar/browser_urlbarHashChangeProxyState.js b/browser/base/content/test/urlbar/browser_urlbarHashChangeProxyState.js new file mode 100644 index 000000000000..d4e0a0ca244b --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbarHashChangeProxyState.js @@ -0,0 +1,111 @@ +"use strict"; + +/** + * Check that navigating through both the URL bar and using in-page hash- or ref- + * based links and back or forward navigation updates the URL bar and identity block correctly. + */ +add_task(function* () { + let baseURL = "https://example.org/browser/browser/base/content/test/urlbar/dummy_page.html"; + let url = baseURL + "#foo"; + yield BrowserTestUtils.withNewTab({ gBrowser, url }, function(browser) { + let identityBox = document.getElementById("identity-box"); + let expectedURL = url; + + let verifyURLBarState = testType => { + is(gURLBar.textValue, expectedURL, "URL bar visible value should be correct " + testType); + is(gURLBar.value, expectedURL, "URL bar value should be correct " + testType); + ok(identityBox.classList.contains("verifiedDomain"), "Identity box should know we're doing SSL " + testType); + is(gURLBar.getAttribute("pageproxystate"), "valid", "URL bar is in valid page proxy state"); + }; + + verifyURLBarState("at the beginning"); + + let locationChangePromise; + let resolveLocationChangePromise; + let expectURL = url => { + expectedURL = url; + locationChangePromise = new Promise(r => resolveLocationChangePromise = r); + }; + let wpl = { + onLocationChange(wpl, request, location, flags) { + is(location.spec, expectedURL, "Got the expected URL"); + resolveLocationChangePromise(); + }, + }; + gBrowser.addProgressListener(wpl); + + expectURL(baseURL + "#foo"); + gURLBar.select(); + EventUtils.sendKey("return"); + + yield locationChangePromise; + verifyURLBarState("after hitting enter on the same URL a second time"); + + expectURL(baseURL + "#bar"); + gURLBar.value = expectedURL; + gURLBar.select(); + EventUtils.sendKey("return"); + + yield locationChangePromise; + verifyURLBarState("after a URL bar hash navigation"); + + expectURL(baseURL + "#foo"); + yield ContentTask.spawn(browser, null, function() { + let a = content.document.createElement("a"); + a.href = "#foo"; + a.textContent = "Foo Link"; + content.document.body.appendChild(a); + a.click(); + }); + + yield locationChangePromise; + verifyURLBarState("after a page link hash navigation"); + + expectURL(baseURL + "#bar"); + gBrowser.goBack(); + + yield locationChangePromise; + verifyURLBarState("after going back"); + + expectURL(baseURL + "#foo"); + gBrowser.goForward(); + + yield locationChangePromise; + verifyURLBarState("after going forward"); + + expectURL(baseURL + "#foo"); + gURLBar.select(); + EventUtils.sendKey("return"); + + yield locationChangePromise; + verifyURLBarState("after hitting enter on the same URL"); + + gBrowser.removeProgressListener(wpl); + }); +}); + +/** + * Check that initial secure loads that swap remoteness + * get the correct page icon when finished. + */ +add_task(function* () { + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:newtab", false); + // NB: CPOW usage because new tab pages can be preloaded, in which case no + // load events fire. + yield BrowserTestUtils.waitForCondition(() => !tab.linkedBrowser.contentDocument.hidden) + let url = "https://example.org/browser/browser/base/content/test/urlbar/dummy_page.html#foo"; + gURLBar.value = url; + gURLBar.select(); + EventUtils.sendKey("return"); + yield BrowserTestUtils.browserLoaded(tab.linkedBrowser); + + is(gURLBar.textValue, url, "URL bar visible value should be correct when the page loads from about:newtab"); + is(gURLBar.value, url, "URL bar value should be correct when the page loads from about:newtab"); + let identityBox = document.getElementById("identity-box"); + ok(identityBox.classList.contains("verifiedDomain"), + "Identity box should know we're doing SSL when the page loads from about:newtab"); + is(gURLBar.getAttribute("pageproxystate"), "valid", + "URL bar is in valid page proxy state when SSL page with hash loads from about:newtab"); + yield BrowserTestUtils.removeTab(tab); +}); + diff --git a/browser/base/content/test/urlbar/browser_urlbarKeepStateAcrossTabSwitches.js b/browser/base/content/test/urlbar/browser_urlbarKeepStateAcrossTabSwitches.js new file mode 100644 index 000000000000..9c89960596fe --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbarKeepStateAcrossTabSwitches.js @@ -0,0 +1,49 @@ +"use strict"; + +/** + * Verify user typed text remains in the URL bar when tab switching, even when + * loads fail. + */ +add_task(function* () { + let input = "i-definitely-dont-exist.example.com"; + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:newtab", false); + // NB: CPOW usage because new tab pages can be preloaded, in which case no + // load events fire. + yield BrowserTestUtils.waitForCondition(() => !tab.linkedBrowser.contentDocument.hidden) + let errorPageLoaded = BrowserTestUtils.waitForErrorPage(tab.linkedBrowser); + gURLBar.value = input; + gURLBar.select(); + EventUtils.sendKey("return"); + yield errorPageLoaded; + is(gURLBar.textValue, input, "Text is still in URL bar"); + yield BrowserTestUtils.switchTab(gBrowser, tab.previousSibling); + yield BrowserTestUtils.switchTab(gBrowser, tab); + is(gURLBar.textValue, input, "Text is still in URL bar after tab switch"); + yield BrowserTestUtils.removeTab(tab); +}); + +/** + * Invalid URIs fail differently (that is, immediately, in the loadURI call) + * if keyword searches are turned off. Test that this works, too. + */ +add_task(function* () { + let input = "To be or not to be-that is the question"; + yield new Promise(resolve => SpecialPowers.pushPrefEnv({set: [["keyword.enabled", false]]}, resolve)); + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:newtab", false); + // NB: CPOW usage because new tab pages can be preloaded, in which case no + // load events fire. + yield BrowserTestUtils.waitForCondition(() => !tab.linkedBrowser.contentDocument.hidden) + let errorPageLoaded = BrowserTestUtils.waitForErrorPage(tab.linkedBrowser); + gURLBar.value = input; + gURLBar.select(); + EventUtils.sendKey("return"); + yield errorPageLoaded; + is(gURLBar.textValue, input, "Text is still in URL bar"); + is(tab.linkedBrowser.userTypedValue, input, "Text still stored on browser"); + yield BrowserTestUtils.switchTab(gBrowser, tab.previousSibling); + yield BrowserTestUtils.switchTab(gBrowser, tab); + is(gURLBar.textValue, input, "Text is still in URL bar after tab switch"); + is(tab.linkedBrowser.userTypedValue, input, "Text still stored on browser"); + yield BrowserTestUtils.removeTab(tab); +}); + diff --git a/browser/base/content/test/urlbar/browser_urlbarUpdateForDomainCompletion.js b/browser/base/content/test/urlbar/browser_urlbarUpdateForDomainCompletion.js new file mode 100644 index 000000000000..c3cdf507f772 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbarUpdateForDomainCompletion.js @@ -0,0 +1,17 @@ +"use strict"; + +/** + * Disable keyword.enabled (so no keyword search), and check that when you type in + * "example" and hit enter, the browser loads and the URL bar is updated accordingly. + */ +add_task(function* () { + yield new Promise(resolve => SpecialPowers.pushPrefEnv({set: [["keyword.enabled", false]]}, resolve)); + yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank" }, function* (browser) { + gURLBar.value = "example"; + gURLBar.select(); + let loadPromise = BrowserTestUtils.browserLoaded(browser, false, url => url == "http://www.example.com/"); + EventUtils.sendKey("return"); + yield loadPromise; + is(gURLBar.textValue, "www.example.com"); + }); +}); diff --git a/browser/components/sessionstore/SessionStore.jsm b/browser/components/sessionstore/SessionStore.jsm index 1cea3dd15537..42b1744c6e01 100644 --- a/browser/components/sessionstore/SessionStore.jsm +++ b/browser/components/sessionstore/SessionStore.jsm @@ -815,11 +815,14 @@ var SessionStoreInternal = { } else { // If the user was typing into the URL bar when we crashed, but hadn't hit // enter yet, then we just need to write that value to the URL bar without - // loading anything. This must happen after the load, since it will clear + // loading anything. This must happen after the load, as the load will clear // userTypedValue. let tabData = TabState.collect(tab); if (tabData.userTypedValue && !tabData.userTypedClear) { browser.userTypedValue = tabData.userTypedValue; + if (data.didStartLoad) { + browser.userTypedClear++; + } win.URLBarSetURI(); } @@ -2527,7 +2530,6 @@ var SessionStoreInternal = { tabState.index = historyIndex + 1; tabState.index = Math.max(1, Math.min(tabState.index, tabState.entries.length)); } else { - tabState.userTypedValue = null; options.loadArguments = recentLoadArguments; } diff --git a/browser/components/sessionstore/content/content-sessionStore.js b/browser/components/sessionstore/content/content-sessionStore.js index 33cb288e0823..4fe1ed110c81 100644 --- a/browser/components/sessionstore/content/content-sessionStore.js +++ b/browser/components/sessionstore/content/content-sessionStore.js @@ -177,7 +177,7 @@ var MessageListener = { sendAsyncMessage("SessionStore:restoreTabContentComplete", {epoch, isRemotenessUpdate}); }); - sendAsyncMessage("SessionStore:restoreTabContentStarted", {epoch}); + sendAsyncMessage("SessionStore:restoreTabContentStarted", {epoch, didStartLoad}); if (!didStartLoad) { // Pretend that the load succeeded so that event handlers fire correctly. diff --git a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm index f686e3612075..01a1408638ae 100644 --- a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm +++ b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm @@ -528,6 +528,8 @@ this.BrowserTestUtils = { * event is the expected one, or false if it should be ignored and * listening should continue. If not specified, the first event with * the specified name resolves the returned promise. + * @param {bool} wantsUntrusted [optional] + * Whether to accept untrusted events * * @note Because this function is intended for testing, any error in checkFn * will cause the returned promise to be rejected instead of waiting for @@ -535,12 +537,15 @@ this.BrowserTestUtils = { * * @returns {Promise} */ - waitForContentEvent(browser, eventName, capture, checkFn) { - let parameters = { eventName, - capture, - checkFnSource: checkFn ? checkFn.toSource() : null }; + waitForContentEvent(browser, eventName, capture = false, checkFn, wantsUntrusted = false) { + let parameters = { + eventName, + capture, + checkFnSource: checkFn ? checkFn.toSource() : null, + wantsUntrusted, + }; return ContentTask.spawn(browser, parameters, - function({ eventName, capture, checkFnSource }) { + function({ eventName, capture, checkFnSource, wantsUntrusted }) { let checkFn; if (checkFnSource) { checkFn = eval(`(() => (${checkFnSource}))()`); @@ -557,11 +562,44 @@ this.BrowserTestUtils = { } removeEventListener(eventName, listener, capture); completion(); - }, capture); + }, capture, wantsUntrusted); }); }); }, + /** + * Like browserLoaded, but waits for an error page to appear. + * This explicitly deals with cases where the browser is not currently remote and a + * remoteness switch will occur before the error page is loaded, which is tricky + * because error pages don't fire 'regular' load events that we can rely on. + * + * @param {xul:browser} browser + * A xul:browser. + * + * @return {Promise} + * @resolves When an error page has been loaded in the browser. + */ + waitForErrorPage(browser) { + let waitForLoad = () => + this.waitForContentEvent(browser, "AboutNetErrorLoad", false, null, true); + + let win = browser.ownerDocument.defaultView; + let tab = win.gBrowser.getTabForBrowser(browser); + if (!tab || browser.isRemoteBrowser || !win.gMultiProcessBrowser) { + return waitForLoad(); + } + + // We're going to switch remoteness when loading an error page. We need to be + // quite careful in order to make sure we're adding the listener in time to + // get this event: + return new Promise((resolve, reject) => { + tab.addEventListener("TabRemotenessChange", function onTRC() { + tab.removeEventListener("TabRemotenessChange", onTRC); + waitForLoad().then(resolve, reject); + }); + }); + }, + /** * Versions of EventUtils.jsm synthesizeMouse functions that synthesize a * mouse event in a child process and return promises that resolve when the