diff options
Diffstat (limited to 'browser/base/content/test/urlbar')
75 files changed, 5568 insertions, 0 deletions
diff --git a/browser/base/content/test/urlbar/.eslintrc.js b/browser/base/content/test/urlbar/.eslintrc.js new file mode 100644 index 000000000..7c8021192 --- /dev/null +++ b/browser/base/content/test/urlbar/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "../../../../../testing/mochitest/browser.eslintrc.js" + ] +}; diff --git a/browser/base/content/test/urlbar/authenticate.sjs b/browser/base/content/test/urlbar/authenticate.sjs new file mode 100644 index 000000000..58da655cf --- /dev/null +++ b/browser/base/content/test/urlbar/authenticate.sjs @@ -0,0 +1,220 @@ +function handleRequest(request, response) +{ + try { + reallyHandleRequest(request, response); + } catch (e) { + response.setStatusLine("1.0", 200, "AlmostOK"); + response.write("Error handling request: " + e); + } +} + + +function reallyHandleRequest(request, response) { + var match; + var requestAuth = true, requestProxyAuth = true; + + // Allow the caller to drive how authentication is processed via the query. + // Eg, http://localhost:8888/authenticate.sjs?user=foo&realm=bar + // The extra ? allows the user/pass/realm checks to succeed if the name is + // at the beginning of the query string. + var query = "?" + request.queryString; + + var expected_user = "", expected_pass = "", realm = "mochitest"; + var proxy_expected_user = "", proxy_expected_pass = "", proxy_realm = "mochi-proxy"; + var huge = false, plugin = false, anonymous = false; + var authHeaderCount = 1; + // user=xxx + match = /[^_]user=([^&]*)/.exec(query); + if (match) + expected_user = match[1]; + + // pass=xxx + match = /[^_]pass=([^&]*)/.exec(query); + if (match) + expected_pass = match[1]; + + // realm=xxx + match = /[^_]realm=([^&]*)/.exec(query); + if (match) + realm = match[1]; + + // proxy_user=xxx + match = /proxy_user=([^&]*)/.exec(query); + if (match) + proxy_expected_user = match[1]; + + // proxy_pass=xxx + match = /proxy_pass=([^&]*)/.exec(query); + if (match) + proxy_expected_pass = match[1]; + + // proxy_realm=xxx + match = /proxy_realm=([^&]*)/.exec(query); + if (match) + proxy_realm = match[1]; + + // huge=1 + match = /huge=1/.exec(query); + if (match) + huge = true; + + // plugin=1 + match = /plugin=1/.exec(query); + if (match) + plugin = true; + + // multiple=1 + match = /multiple=([^&]*)/.exec(query); + if (match) + authHeaderCount = match[1]+0; + + // anonymous=1 + match = /anonymous=1/.exec(query); + if (match) + anonymous = true; + + // Look for an authentication header, if any, in the request. + // + // EG: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== + // + // This test only supports Basic auth. The value sent by the client is + // "username:password", obscured with base64 encoding. + + var actual_user = "", actual_pass = "", authHeader, authPresent = false; + if (request.hasHeader("Authorization")) { + authPresent = true; + authHeader = request.getHeader("Authorization"); + match = /Basic (.+)/.exec(authHeader); + if (match.length != 2) + throw "Couldn't parse auth header: " + authHeader; + + var userpass = base64ToString(match[1]); // no atob() :-( + match = /(.*):(.*)/.exec(userpass); + if (match.length != 3) + throw "Couldn't decode auth header: " + userpass; + actual_user = match[1]; + actual_pass = match[2]; + } + + var proxy_actual_user = "", proxy_actual_pass = ""; + if (request.hasHeader("Proxy-Authorization")) { + authHeader = request.getHeader("Proxy-Authorization"); + match = /Basic (.+)/.exec(authHeader); + if (match.length != 2) + throw "Couldn't parse auth header: " + authHeader; + + var userpass = base64ToString(match[1]); // no atob() :-( + match = /(.*):(.*)/.exec(userpass); + if (match.length != 3) + throw "Couldn't decode auth header: " + userpass; + proxy_actual_user = match[1]; + proxy_actual_pass = match[2]; + } + + // Don't request authentication if the credentials we got were what we + // expected. + if (expected_user == actual_user && + expected_pass == actual_pass) { + requestAuth = false; + } + if (proxy_expected_user == proxy_actual_user && + proxy_expected_pass == proxy_actual_pass) { + requestProxyAuth = false; + } + + if (anonymous) { + if (authPresent) { + response.setStatusLine("1.0", 400, "Unexpected authorization header found"); + } else { + response.setStatusLine("1.0", 200, "Authorization header not found"); + } + } else { + if (requestProxyAuth) { + response.setStatusLine("1.0", 407, "Proxy authentication required"); + for (i = 0; i < authHeaderCount; ++i) + response.setHeader("Proxy-Authenticate", "basic realm=\"" + proxy_realm + "\"", true); + } else if (requestAuth) { + response.setStatusLine("1.0", 401, "Authentication required"); + for (i = 0; i < authHeaderCount; ++i) + response.setHeader("WWW-Authenticate", "basic realm=\"" + realm + "\"", true); + } else { + response.setStatusLine("1.0", 200, "OK"); + } + } + + response.setHeader("Content-Type", "application/xhtml+xml", false); + response.write("<html xmlns='http://www.w3.org/1999/xhtml'>"); + response.write("<p>Login: <span id='ok'>" + (requestAuth ? "FAIL" : "PASS") + "</span></p>\n"); + response.write("<p>Proxy: <span id='proxy'>" + (requestProxyAuth ? "FAIL" : "PASS") + "</span></p>\n"); + response.write("<p>Auth: <span id='auth'>" + authHeader + "</span></p>\n"); + response.write("<p>User: <span id='user'>" + actual_user + "</span></p>\n"); + response.write("<p>Pass: <span id='pass'>" + actual_pass + "</span></p>\n"); + + if (huge) { + response.write("<div style='display: none'>"); + for (i = 0; i < 100000; i++) { + response.write("123456789\n"); + } + response.write("</div>"); + response.write("<span id='footnote'>This is a footnote after the huge content fill</span>"); + } + + if (plugin) { + response.write("<embed id='embedtest' style='width: 400px; height: 100px;' " + + "type='application/x-test'></embed>\n"); + } + + response.write("</html>"); +} + + +// base64 decoder +// +// Yoinked from extensions/xml-rpc/src/nsXmlRpcClient.js because btoa() +// doesn't seem to exist. :-( +/* Convert Base64 data to a string */ +const toBinaryTable = [ + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, + 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, + 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, + -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, + 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 +]; +const base64Pad = '='; + +function base64ToString(data) { + + var result = ''; + var leftbits = 0; // number of bits decoded, but yet to be appended + var leftdata = 0; // bits decoded, but yet to be appended + + // Convert one by one. + for (var i = 0; i < data.length; i++) { + var c = toBinaryTable[data.charCodeAt(i) & 0x7f]; + var padding = (data[i] == base64Pad); + // Skip illegal characters and whitespace + if (c == -1) continue; + + // Collect data into leftdata, update bitcount + leftdata = (leftdata << 6) | c; + leftbits += 6; + + // If we have 8 or more bits, append 8 bits to the result + if (leftbits >= 8) { + leftbits -= 8; + // Append if not padding. + if (!padding) + result += String.fromCharCode((leftdata >> leftbits) & 0xff); + leftdata &= (1 << leftbits) - 1; + } + } + + // If there are any bits left, the base64 string was corrupted + if (leftbits) + throw Components.Exception('Corrupted base64 string'); + + return result; +} diff --git a/browser/base/content/test/urlbar/browser.ini b/browser/base/content/test/urlbar/browser.ini new file mode 100644 index 000000000..39bc086c9 --- /dev/null +++ b/browser/base/content/test/urlbar/browser.ini @@ -0,0 +1,101 @@ +[DEFAULT] +support-files = + dummy_page.html + head.js + +[browser_URLBarSetURI.js] +skip-if = (os == "linux" || os == "mac") && debug # bug 970052, bug 970053 +[browser_action_keyword.js] +skip-if = os == "linux" # Bug 1188154 +support-files = + print_postdata.sjs +[browser_action_keyword_override.js] +[browser_action_searchengine.js] +[browser_action_searchengine_alias.js] +[browser_autocomplete_a11y_label.js] +[browser_autocomplete_autoselect.js] +[browser_autocomplete_cursor.js] +[browser_autocomplete_edit_completed.js] +[browser_autocomplete_enter_race.js] +[browser_autocomplete_no_title.js] +[browser_autocomplete_tag_star_visibility.js] +[browser_bug1104165-switchtab-decodeuri.js] +[browser_bug1003461-switchtab-override.js] +[browser_bug1024133-switchtab-override-keynav.js] +[browser_bug1025195_switchToTabHavingURI_aOpenParams.js] +[browser_bug1070778.js] +[browser_bug1225194-remotetab.js] +[browser_bug304198.js] +[browser_bug556061.js] +subsuite = clipboard +[browser_bug562649.js] +[browser_bug623155.js] +support-files = + redirect_bug623155.sjs +[browser_bug783614.js] +[browser_canonizeURL.js] +[browser_dragdropURL.js] +[browser_locationBarCommand.js] +[browser_locationBarExternalLoad.js] +[browser_moz_action_link.js] +[browser_removeUnsafeProtocolsFromURLBarPaste.js] +subsuite = clipboard +[browser_search_favicon.js] +[browser_tabMatchesInAwesomebar.js] +support-files = + moz.png +[browser_tabMatchesInAwesomebar_perwindowpb.js] +skip-if = os == 'linux' # Bug 1104755 +[browser_urlbarAboutHomeLoading.js] +[browser_urlbarAutoFillTrimURLs.js] +[browser_urlbarCopying.js] +subsuite = clipboard +support-files = + authenticate.sjs +[browser_urlbarDecode.js] +[browser_urlbarDelete.js] +[browser_urlbarEnter.js] +[browser_urlbarEnterAfterMouseOver.js] +skip-if = os == "linux" # Bug 1073339 - Investigate autocomplete test unreliability on Linux/e10s +[browser_urlbarFocusedCmdK.js] +[browser_urlbarHashChangeProxyState.js] +[browser_urlbarKeepStateAcrossTabSwitches.js] +[browser_urlbarOneOffs.js] +[browser_urlbarPrivateBrowsingWindowChange.js] +[browser_urlbarRaceWithTabs.js] +[browser_urlbarRevert.js] +[browser_urlbarSearchSingleWordNotification.js] +[browser_urlbarSearchSuggestions.js] +support-files = + searchSuggestionEngine.xml + searchSuggestionEngine.sjs +[browser_urlbarSearchSuggestionsNotification.js] +support-files = + searchSuggestionEngine.xml + searchSuggestionEngine.sjs +[browser_urlbarSearchTelemetry.js] +support-files = + searchSuggestionEngine.xml + searchSuggestionEngine.sjs +[browser_urlbarStop.js] +[browser_urlbarTrimURLs.js] +subsuite = clipboard +[browser_urlbarUpdateForDomainCompletion.js] +[browser_urlbar_autoFill_backspaced.js] +[browser_urlbar_blanking.js] +support-files = + file_blank_but_not_blank.html +[browser_urlbar_locationchange_urlbar_edit_dos.js] +support-files = + file_urlbar_edit_dos.html +[browser_urlbar_searchsettings.js] +[browser_urlbar_stop_pending.js] +support-files = + slow-page.sjs +[browser_urlbar_remoteness_switch.js] +run-if = e10s +[browser_urlHighlight.js] +[browser_wyciwyg_urlbarCopying.js] +subsuite = clipboard +support-files = + test_wyciwyg_copying.html diff --git a/browser/base/content/test/urlbar/browser_URLBarSetURI.js b/browser/base/content/test/urlbar/browser_URLBarSetURI.js new file mode 100644 index 000000000..ac8352f1a --- /dev/null +++ b/browser/base/content/test/urlbar/browser_URLBarSetURI.js @@ -0,0 +1,100 @@ +/* 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() { + waitForExplicitFinish(); + + // avoid prompting about phishing + Services.prefs.setIntPref(phishyUserPassPref, 32); + registerCleanupFunction(function () { + Services.prefs.clearUserPref(phishyUserPassPref); + }); + + nextTest(); +} + +const phishyUserPassPref = "network.http.phishy-userpass-length"; + +function nextTest() { + let test = tests.shift(); + if (test) { + test(function () { + executeSoon(nextTest); + }); + } else { + executeSoon(finish); + } +} + +var tests = [ + function revert(next) { + loadTabInWindow(window, function (tab) { + gURLBar.handleRevert(); + is(gURLBar.textValue, "example.com", "URL bar had user/pass stripped after reverting"); + gBrowser.removeTab(tab); + next(); + }); + }, + function customize(next) { + // Need to wait for delayedStartup for the customization part of the test, + // since that's where BrowserToolboxCustomizeDone is set. + BrowserTestUtils.openNewBrowserWindow().then(function(win) { + loadTabInWindow(win, function () { + openToolbarCustomizationUI(function () { + closeToolbarCustomizationUI(function () { + is(win.gURLBar.textValue, "example.com", "URL bar had user/pass stripped after customize"); + win.close(); + next(); + }, win); + }, win); + }); + }); + }, + function pageloaderror(next) { + loadTabInWindow(window, function (tab) { + // Load a new URL and then immediately stop it, to simulate a page load + // error. + tab.linkedBrowser.loadURI("http://test1.example.com"); + tab.linkedBrowser.stop(); + is(gURLBar.textValue, "example.com", "URL bar had user/pass stripped after load error"); + gBrowser.removeTab(tab); + next(); + }); + } +]; + +function loadTabInWindow(win, callback) { + info("Loading tab"); + let url = "http://user:pass@example.com/"; + let tab = win.gBrowser.selectedTab = win.gBrowser.addTab(url); + BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, url).then(() => { + info("Tab loaded"); + is(win.gURLBar.textValue, "example.com", "URL bar had user/pass stripped initially"); + callback(tab); + }, true); +} + +function openToolbarCustomizationUI(aCallback, aBrowserWin) { + if (!aBrowserWin) + aBrowserWin = window; + + aBrowserWin.gCustomizeMode.enter(); + + aBrowserWin.gNavToolbox.addEventListener("customizationready", function UI_loaded() { + aBrowserWin.gNavToolbox.removeEventListener("customizationready", UI_loaded); + executeSoon(function() { + aCallback(aBrowserWin) + }); + }); +} + +function closeToolbarCustomizationUI(aCallback, aBrowserWin) { + aBrowserWin.gNavToolbox.addEventListener("aftercustomization", function unloaded() { + aBrowserWin.gNavToolbox.removeEventListener("aftercustomization", unloaded); + executeSoon(aCallback); + }); + + aBrowserWin.gCustomizeMode.exit(); +} + diff --git a/browser/base/content/test/urlbar/browser_action_keyword.js b/browser/base/content/test/urlbar/browser_action_keyword.js new file mode 100644 index 000000000..854a7b82f --- /dev/null +++ b/browser/base/content/test/urlbar/browser_action_keyword.js @@ -0,0 +1,119 @@ +function* promise_first_result(inputText) { + yield promiseAutocompleteResultPopup(inputText); + + let firstResult = gURLBar.popup.richlistbox.firstChild; + return firstResult; +} + +const TEST_URL = "http://mochi.test:8888/browser/browser/base/content/test/urlbar/print_postdata.sjs"; + +add_task(function* setup() { + yield PlacesUtils.keywords.insert({ keyword: "get", + url: TEST_URL + "?q=%s" }); + yield PlacesUtils.keywords.insert({ keyword: "post", + url: TEST_URL, + postData: "q=%s" }); + registerCleanupFunction(function* () { + yield PlacesUtils.keywords.remove("get"); + yield PlacesUtils.keywords.remove("post"); + while (gBrowser.tabs.length > 1) { + yield BrowserTestUtils.removeTab(gBrowser.selectedTab); + } + }); +}); + +add_task(function* get_keyword() { + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla"); + + let result = yield promise_first_result("get something"); + isnot(result, null, "Expect a keyword result"); + + let types = new Set(result.getAttribute("type").split(/\s+/)); + Assert.ok(types.has("keyword")); + is(result.getAttribute("actiontype"), "keyword", "Expect correct `actiontype` attribute"); + is(result.getAttribute("title"), "mochi.test:8888", "Expect correct title"); + + // We need to make a real URI out of this to ensure it's normalised for + // comparison. + let uri = NetUtil.newURI(result.getAttribute("url")); + is(uri.spec, PlacesUtils.mozActionURI("keyword", + { url: TEST_URL + "?q=something", + input: "get something"}), + "Expect correct url"); + + let titleHbox = result._titleText.parentNode.parentNode; + ok(titleHbox.classList.contains("ac-title"), "Title hbox element sanity check"); + is_element_visible(titleHbox, "Title element should be visible"); + is(result._titleText.textContent, "mochi.test:8888: something", + "Node should contain the name of the bookmark and query"); + + let urlHbox = result._urlText.parentNode.parentNode; + ok(urlHbox.classList.contains("ac-url"), "URL hbox element sanity check"); + is_element_hidden(urlHbox, "URL element should be hidden"); + + let actionHbox = result._actionText.parentNode.parentNode; + ok(actionHbox.classList.contains("ac-action"), "Action hbox element sanity check"); + is_element_visible(actionHbox, "Action element should be visible"); + is(result._actionText.textContent, "", "Action text should be empty"); + + // Click on the result + info("Normal click on result"); + let tabPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser); + EventUtils.synthesizeMouseAtCenter(result, {}); + yield tabPromise; + is(tab.linkedBrowser.currentURI.spec, TEST_URL + "?q=something", + "Tab should have loaded from clicking on result"); + + // Middle-click on the result + info("Middle-click on result"); + result = yield promise_first_result("get somethingmore"); + isnot(result, null, "Expect a keyword result"); + // We need to make a real URI out of this to ensure it's normalised for + // comparison. + uri = NetUtil.newURI(result.getAttribute("url")); + is(uri.spec, PlacesUtils.mozActionURI("keyword", + { url: TEST_URL + "?q=somethingmore", + input: "get somethingmore" }), + "Expect correct url"); + + tabPromise = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "TabOpen"); + EventUtils.synthesizeMouseAtCenter(result, {button: 1}); + let tabOpenEvent = yield tabPromise; + let newTab = tabOpenEvent.target; + yield BrowserTestUtils.browserLoaded(newTab.linkedBrowser); + is(newTab.linkedBrowser.currentURI.spec, + TEST_URL + "?q=somethingmore", + "Tab should have loaded from middle-clicking on result"); +}); + + +add_task(function* post_keyword() { + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla"); + + let result = yield promise_first_result("post something"); + isnot(result, null, "Expect a keyword result"); + + let types = new Set(result.getAttribute("type").split(/\s+/)); + Assert.ok(types.has("keyword")); + is(result.getAttribute("actiontype"), "keyword", "Expect correct `actiontype` attribute"); + is(result.getAttribute("title"), "mochi.test:8888", "Expect correct title"); + + is(result.getAttribute("url"), + PlacesUtils.mozActionURI("keyword", { url: TEST_URL, + input: "post something", + "postData": "q=something" }), + "Expect correct url"); + + // Click on the result + info("Normal click on result"); + let tabPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser); + EventUtils.synthesizeMouseAtCenter(result, {}); + yield tabPromise; + is(tab.linkedBrowser.currentURI.spec, TEST_URL, + "Tab should have loaded from clicking on result"); + + let postData = yield ContentTask.spawn(tab.linkedBrowser, null, function* () { + return content.document.body.textContent; + }); + is(postData, "q=something", "post data was submitted correctly"); +}); diff --git a/browser/base/content/test/urlbar/browser_action_keyword_override.js b/browser/base/content/test/urlbar/browser_action_keyword_override.js new file mode 100644 index 000000000..f5a865678 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_action_keyword_override.js @@ -0,0 +1,40 @@ +add_task(function*() { + let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: "http://example.com/?q=%s", + title: "test" }); + yield PlacesUtils.keywords.insert({ keyword: "keyword", + url: "http://example.com/?q=%s" }) + + registerCleanupFunction(function* () { + yield PlacesUtils.bookmarks.remove(bm); + }); + + yield promiseAutocompleteResultPopup("keyword search"); + let result = gURLBar.popup.richlistbox.children[0]; + + info("Before override"); + let titleHbox = result._titleText.parentNode.parentNode; + ok(titleHbox.classList.contains("ac-title"), "Title hbox element sanity check"); + is_element_visible(titleHbox, "Title element should be visible"); + + let urlHbox = result._urlText.parentNode.parentNode; + ok(urlHbox.classList.contains("ac-url"), "URL hbox element sanity check"); + is_element_hidden(urlHbox, "URL element should be hidden"); + + let actionHbox = result._actionText.parentNode.parentNode; + ok(actionHbox.classList.contains("ac-action"), "Action hbox element sanity check"); + is_element_visible(actionHbox, "Action element should be visible"); + is(result._actionText.textContent, "", "Action text should be empty"); + + info("During override"); + EventUtils.synthesizeKey("VK_SHIFT", { type: "keydown" }); + is_element_visible(titleHbox, "Title element should be visible"); + is_element_hidden(urlHbox, "URL element should be hidden"); + is_element_visible(actionHbox, "Action element should be visible"); + is(result._actionText.textContent, "", "Action text should be empty"); + + EventUtils.synthesizeKey("VK_SHIFT", { type: "keyup" }); + + gURLBar.popup.hidePopup(); + yield promisePopupHidden(gURLBar.popup); +}); diff --git a/browser/base/content/test/urlbar/browser_action_searchengine.js b/browser/base/content/test/urlbar/browser_action_searchengine.js new file mode 100644 index 000000000..d2115abba --- /dev/null +++ b/browser/base/content/test/urlbar/browser_action_searchengine.js @@ -0,0 +1,36 @@ +add_task(function* () { + Services.search.addEngineWithDetails("MozSearch", "", "", "", "GET", + "http://example.com/?q={searchTerms}"); + let engine = Services.search.getEngineByName("MozSearch"); + let originalEngine = Services.search.currentEngine; + Services.search.currentEngine = engine; + + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla"); + + registerCleanupFunction(() => { + Services.search.currentEngine = originalEngine; + let engine = Services.search.getEngineByName("MozSearch"); + Services.search.removeEngine(engine); + + try { + gBrowser.removeTab(tab); + } catch (ex) { /* tab may have already been closed in case of failure */ } + + return PlacesTestUtils.clearHistory(); + }); + + yield promiseAutocompleteResultPopup("open a search"); + let result = gURLBar.popup.richlistbox.firstChild; + + isnot(result, null, "Should have a result"); + is(result.getAttribute("url"), + `moz-action:searchengine,{"engineName":"MozSearch","input":"open%20a%20search","searchQuery":"open%20a%20search"}`, + "Result should be a moz-action: for the correct search engine"); + is(result.hasAttribute("image"), false, "Result shouldn't have an image attribute"); + + let tabPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + result.click(); + yield tabPromise; + + is(gBrowser.selectedBrowser.currentURI.spec, "http://example.com/?q=open+a+search", "Correct URL should be loaded"); +}); diff --git a/browser/base/content/test/urlbar/browser_action_searchengine_alias.js b/browser/base/content/test/urlbar/browser_action_searchengine_alias.js new file mode 100644 index 000000000..1967d178a --- /dev/null +++ b/browser/base/content/test/urlbar/browser_action_searchengine_alias.js @@ -0,0 +1,35 @@ +add_task(function* () { + let iconURI = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABGklEQVQoz2NgGB6AnZ1dUlJSXl4eSDIyMhLW4Ovr%2B%2Fr168uXL69Zs4YoG%2BLi4i5dusTExMTGxsbNzd3f37937976%2BnpmZmagbHR09J49e5YvX66kpATVEBYW9ubNm2nTphkbG7e2tp44cQLIuHfvXm5urpaWFlDKysqqu7v73LlzECMYIiIiHj58mJCQoKKicvXq1bS0NKBgW1vbjh074uPjgeqAXE1NzSdPnvDz84M0AEUvXLgAsW379u1z5swBen3jxo2zZ892cHB4%2BvQp0KlAfwI1cHJyghQFBwfv2rULokFXV%2FfixYu7d%2B8GGqGgoMDKyrpu3br9%2B%2FcDuXl5eVA%2FAEWBfoWHAdAYoNuAYQ0XAeoUERFhGDYAAPoUaT2dfWJuAAAAAElFTkSuQmCC"; + Services.search.addEngineWithDetails("MozSearch", iconURI, "moz", "", "GET", + "http://example.com/?q={searchTerms}"); + let engine = Services.search.getEngineByName("MozSearch"); + let originalEngine = Services.search.currentEngine; + Services.search.currentEngine = engine; + + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla"); + + registerCleanupFunction(() => { + Services.search.currentEngine = originalEngine; + let engine = Services.search.getEngineByName("MozSearch"); + Services.search.removeEngine(engine); + + try { + gBrowser.removeTab(tab); + } catch (ex) { /* tab may have already been closed in case of failure */ } + + return PlacesTestUtils.clearHistory(); + }); + + yield promiseAutocompleteResultPopup("moz open a search"); + + let result = gURLBar.popup.richlistbox.children[0]; + ok(result.hasAttribute("image"), "Result should have an image attribute"); + ok(result.getAttribute("image") === engine.iconURI.spec, + "Image attribute should have the search engine's icon"); + + let tabPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + EventUtils.synthesizeKey("VK_RETURN", { }); + yield tabPromise; + + is(gBrowser.selectedBrowser.currentURI.spec, "http://example.com/?q=open+a+search"); +}); diff --git a/browser/base/content/test/urlbar/browser_autocomplete_a11y_label.js b/browser/base/content/test/urlbar/browser_autocomplete_a11y_label.js new file mode 100644 index 000000000..a27f9672e --- /dev/null +++ b/browser/base/content/test/urlbar/browser_autocomplete_a11y_label.js @@ -0,0 +1,57 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const SUGGEST_ALL_PREF = "browser.search.suggest.enabled"; +const SUGGEST_URLBAR_PREF = "browser.urlbar.suggest.searches"; +const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml"; + +add_task(function* switchToTab() { + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:about"); + + yield promiseAutocompleteResultPopup("% about"); + + ok(gURLBar.popup.richlistbox.children.length > 1, "Should get at least 2 results"); + let result = gURLBar.popup.richlistbox.children[1]; + is(result.getAttribute("type"), "switchtab", "Expect right type attribute"); + is(result.label, "about:about about:about Tab", "Result a11y label should be: <title> <url> Tab"); + + gURLBar.popup.hidePopup(); + yield promisePopupHidden(gURLBar.popup); + gBrowser.removeTab(tab); +}); + +add_task(function* searchSuggestions() { + let engine = yield promiseNewSearchEngine(TEST_ENGINE_BASENAME); + let oldCurrentEngine = Services.search.currentEngine; + Services.search.currentEngine = engine; + Services.prefs.setBoolPref(SUGGEST_ALL_PREF, true); + Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, true); + registerCleanupFunction(function () { + Services.search.currentEngine = oldCurrentEngine; + Services.prefs.clearUserPref(SUGGEST_ALL_PREF); + Services.prefs.clearUserPref(SUGGEST_URLBAR_PREF); + }); + + yield promiseAutocompleteResultPopup("foo"); + // Don't assume that the search doesn't match history or bookmarks left around + // by earlier tests. + Assert.ok(gURLBar.popup.richlistbox.children.length >= 3, + "Should get at least heuristic result + two search suggestions"); + // The first expected search is the search term itself since the heuristic + // result will come before the search suggestions. + let expectedSearches = [ + "foo", + "foofoo", + "foobar", + ]; + for (let child of gURLBar.popup.richlistbox.children) { + if (child.getAttribute("type").split(/\s+/).indexOf("searchengine") >= 0) { + Assert.ok(expectedSearches.length > 0); + let suggestion = expectedSearches.shift(); + Assert.equal(child.label, suggestion + " browser_searchSuggestionEngine searchSuggestionEngine.xml Search", + "Result label should be: <search term> <engine name> Search"); + } + } + Assert.ok(expectedSearches.length == 0); + gURLBar.closePopup(); +}); diff --git a/browser/base/content/test/urlbar/browser_autocomplete_autoselect.js b/browser/base/content/test/urlbar/browser_autocomplete_autoselect.js new file mode 100644 index 000000000..e4e0daa8e --- /dev/null +++ b/browser/base/content/test/urlbar/browser_autocomplete_autoselect.js @@ -0,0 +1,92 @@ +const ONEOFF_URLBAR_PREF = "browser.urlbar.oneOffSearches"; + +function repeat(limit, func) { + for (let i = 0; i < limit; i++) { + func(i); + } +} + +function is_selected(index) { + is(gURLBar.popup.richlistbox.selectedIndex, index, `Item ${index + 1} should be selected`); + + // This is true because although both the listbox and the one-offs can have + // selections, the test doesn't check that. + is(gURLBar.popup.oneOffSearchButtons.selectedButton, null, + "A result is selected, so the one-offs should not have a selection"); +} + +function is_selected_one_off(index) { + is(gURLBar.popup.oneOffSearchButtons.selectedButtonIndex, index, + "Expected one-off button should be selected"); + + // This is true because although both the listbox and the one-offs can have + // selections, the test doesn't check that. + is(gURLBar.popup.richlistbox.selectedIndex, -1, + "A one-off is selected, so the listbox should not have a selection"); +} + +add_task(function*() { + let maxResults = Services.prefs.getIntPref("browser.urlbar.maxRichResults"); + + Services.prefs.setBoolPref(ONEOFF_URLBAR_PREF, true); + registerCleanupFunction(function* () { + yield PlacesTestUtils.clearHistory(); + Services.prefs.clearUserPref(ONEOFF_URLBAR_PREF); + }); + + let visits = []; + repeat(maxResults, i => { + visits.push({ + uri: makeURI("http://example.com/autocomplete/?" + i), + }); + }); + yield PlacesTestUtils.addVisits(visits); + + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla"); + yield promiseAutocompleteResultPopup("example.com/autocomplete"); + + let popup = gURLBar.popup; + let results = popup.richlistbox.children; + is(results.length, maxResults, + "Should get maxResults=" + maxResults + " results"); + is_selected(0); + + info("Key Down to select the next item"); + EventUtils.synthesizeKey("VK_DOWN", {}); + is_selected(1); + + info("Key Down maxResults-1 times should select the first one-off"); + repeat(maxResults - 1, () => EventUtils.synthesizeKey("VK_DOWN", {})); + is_selected_one_off(0); + + info("Key Down numButtons-1 should select the last one-off"); + let numButtons = + gURLBar.popup.oneOffSearchButtons.getSelectableButtons(true).length; + repeat(numButtons - 1, () => EventUtils.synthesizeKey("VK_DOWN", {})); + is_selected_one_off(numButtons - 1); + + info("Key Down twice more should select the second result"); + repeat(2, () => EventUtils.synthesizeKey("VK_DOWN", {})); + is_selected(1); + + info("Key Down maxResults + numButtons times should wrap around"); + repeat(maxResults + numButtons, + () => EventUtils.synthesizeKey("VK_DOWN", {})); + is_selected(1); + + info("Key Up maxResults + numButtons times should wrap around the other way"); + repeat(maxResults + numButtons, () => EventUtils.synthesizeKey("VK_UP", {})); + is_selected(1); + + info("Page Up will go up the list, but not wrap"); + EventUtils.synthesizeKey("VK_PAGE_UP", {}) + is_selected(0); + + info("Page Up again will wrap around to the end of the list"); + EventUtils.synthesizeKey("VK_PAGE_UP", {}) + is_selected(maxResults - 1); + + EventUtils.synthesizeKey("VK_ESCAPE", {}); + yield promisePopupHidden(gURLBar.popup); + gBrowser.removeTab(tab); +}); diff --git a/browser/base/content/test/urlbar/browser_autocomplete_cursor.js b/browser/base/content/test/urlbar/browser_autocomplete_cursor.js new file mode 100644 index 000000000..9cc2c6eac --- /dev/null +++ b/browser/base/content/test/urlbar/browser_autocomplete_cursor.js @@ -0,0 +1,17 @@ +add_task(function*() { + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla"); + yield promiseAutocompleteResultPopup("www.mozilla.org"); + + gURLBar.selectTextRange(4, 4); + + is(gURLBar.popup.state, "open", "Popup should be open"); + is(gURLBar.popup.richlistbox.selectedIndex, 0, "Should have selected something"); + + EventUtils.synthesizeKey("VK_RIGHT", {}); + yield promisePopupHidden(gURLBar.popup); + + is(gURLBar.selectionStart, 5, "Should have moved the cursor"); + is(gURLBar.selectionEnd, 5, "And not selected anything"); + + gBrowser.removeTab(tab); +}); diff --git a/browser/base/content/test/urlbar/browser_autocomplete_edit_completed.js b/browser/base/content/test/urlbar/browser_autocomplete_edit_completed.js new file mode 100644 index 000000000..19db1a368 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_autocomplete_edit_completed.js @@ -0,0 +1,48 @@ +add_task(function*() { + yield PlacesTestUtils.clearHistory(); + + yield PlacesTestUtils.addVisits([ + { uri: makeURI("http://example.com/foo") }, + { uri: makeURI("http://example.com/foo/bar") }, + ]); + + registerCleanupFunction(function* () { + yield PlacesTestUtils.clearHistory(); + }); + + gBrowser.selectedTab = gBrowser.addTab("about:blank"); + gURLBar.focus(); + + yield promiseAutocompleteResultPopup("http://example.com"); + + let popup = gURLBar.popup; + let list = popup.richlistbox; + let initialIndex = list.selectedIndex; + + info("Key Down to select the next item."); + EventUtils.synthesizeKey("VK_DOWN", {}); + + let nextIndex = initialIndex + 1; + let nextValue = gURLBar.controller.getFinalCompleteValueAt(nextIndex); + is(list.selectedIndex, nextIndex, "The next item is selected."); + is(gURLBar.value, nextValue, "The selected URL is completed."); + + info("Press backspace"); + EventUtils.synthesizeKey("VK_BACK_SPACE", {}); + yield promiseSearchComplete(); + + let editedValue = gURLBar.textValue; + is(list.selectedIndex, initialIndex, "The initial index is selected again."); + isnot(editedValue, nextValue, "The URL has changed."); + + let docLoad = waitForDocLoadAndStopIt("http://" + editedValue); + + info("Press return to load edited URL."); + EventUtils.synthesizeKey("VK_RETURN", {}); + yield Promise.all([ + promisePopupHidden(gURLBar.popup), + docLoad, + ]); + + gBrowser.removeTab(gBrowser.selectedTab); +}); diff --git a/browser/base/content/test/urlbar/browser_autocomplete_enter_race.js b/browser/base/content/test/urlbar/browser_autocomplete_enter_race.js new file mode 100644 index 000000000..4e3c8943c --- /dev/null +++ b/browser/base/content/test/urlbar/browser_autocomplete_enter_race.js @@ -0,0 +1,122 @@ +// The order of these tests matters! + +add_task(function* setup () { + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser); + let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: "http://example.com/?q=%s", + title: "test" }); + registerCleanupFunction(function* () { + yield PlacesUtils.bookmarks.remove(bm); + yield BrowserTestUtils.removeTab(tab); + }); + yield PlacesUtils.keywords.insert({ keyword: "keyword", + url: "http://example.com/?q=%s" }); + // Needs at least one success. + ok(true, "Setup complete"); +}); + +add_task(function* test_keyword() { + yield promiseAutocompleteResultPopup("keyword bear"); + gURLBar.focus(); + EventUtils.synthesizeKey("d", {}); + EventUtils.synthesizeKey("VK_RETURN", {}); + info("wait for the page to load"); + yield BrowserTestUtils.browserLoaded(gBrowser.selectedTab.linkedBrowser, + false, "http://example.com/?q=beard"); +}); + +add_task(function* test_sametext() { + yield promiseAutocompleteResultPopup("example.com", window, true); + + // Simulate re-entering the same text searched the last time. This may happen + // through a copy paste, but clipboard handling is not much reliable, so just + // fire an input event. + info("synthesize input event"); + let event = document.createEvent("Events"); + event.initEvent("input", true, true); + gURLBar.dispatchEvent(event); + EventUtils.synthesizeKey("VK_RETURN", {}); + + info("wait for the page to load"); + yield BrowserTestUtils.browserLoaded(gBrowser.selectedTab.linkedBrowser, + false, "http://example.com/"); +}); + +add_task(function* test_after_empty_search() { + yield promiseAutocompleteResultPopup(""); + gURLBar.focus(); + gURLBar.value = "e"; + EventUtils.synthesizeKey("x", {}); + EventUtils.synthesizeKey("VK_RETURN", {}); + + info("wait for the page to load"); + yield BrowserTestUtils.browserLoaded(gBrowser.selectedTab.linkedBrowser, + false, "http://example.com/"); +}); + +add_task(function* test_disabled_ac() { + // Disable autocomplete. + let suggestHistory = Preferences.get("browser.urlbar.suggest.history"); + Preferences.set("browser.urlbar.suggest.history", false); + let suggestBookmarks = Preferences.get("browser.urlbar.suggest.bookmark"); + Preferences.set("browser.urlbar.suggest.bookmark", false); + let suggestOpenPages = Preferences.get("browser.urlbar.suggest.openpage"); + Preferences.set("browser.urlbar.suggest.openpages", false); + + Services.search.addEngineWithDetails("MozSearch", "", "", "", "GET", + "http://example.com/?q={searchTerms}"); + let engine = Services.search.getEngineByName("MozSearch"); + let originalEngine = Services.search.currentEngine; + Services.search.currentEngine = engine; + + function* cleanup() { + Preferences.set("browser.urlbar.suggest.history", suggestHistory); + Preferences.set("browser.urlbar.suggest.bookmark", suggestBookmarks); + Preferences.set("browser.urlbar.suggest.openpage", suggestOpenPages); + + Services.search.currentEngine = originalEngine; + let engine = Services.search.getEngineByName("MozSearch"); + if (engine) { + Services.search.removeEngine(engine); + } + } + registerCleanupFunction(cleanup); + + gURLBar.focus(); + gURLBar.value = "e"; + EventUtils.synthesizeKey("x", {}); + EventUtils.synthesizeKey("VK_RETURN", {}); + + info("wait for the page to load"); + yield BrowserTestUtils.browserLoaded(gBrowser.selectedTab.linkedBrowser, + false, "http://example.com/?q=ex"); + yield cleanup(); +}); + +add_task(function* test_delay() { + const TIMEOUT = 10000; + // Set a large delay. + let delay = Preferences.get("browser.urlbar.delay"); + Preferences.set("browser.urlbar.delay", TIMEOUT); + + registerCleanupFunction(function* () { + Preferences.set("browser.urlbar.delay", delay); + }); + + // This is needed to clear the current value, otherwise autocomplete may think + // the user removed text from the end. + let start = Date.now(); + yield promiseAutocompleteResultPopup(""); + Assert.ok((Date.now() - start) < TIMEOUT); + + start = Date.now(); + gURLBar.closePopup(); + gURLBar.focus(); + gURLBar.value = "e"; + EventUtils.synthesizeKey("x", {}); + EventUtils.synthesizeKey("VK_RETURN", {}); + info("wait for the page to load"); + yield BrowserTestUtils.browserLoaded(gBrowser.selectedTab.linkedBrowser, + false, "http://example.com/"); + Assert.ok((Date.now() - start) < TIMEOUT); +}); diff --git a/browser/base/content/test/urlbar/browser_autocomplete_no_title.js b/browser/base/content/test/urlbar/browser_autocomplete_no_title.js new file mode 100644 index 000000000..8d608550b --- /dev/null +++ b/browser/base/content/test/urlbar/browser_autocomplete_no_title.js @@ -0,0 +1,15 @@ +add_task(function*() { + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla"); + + let uri = NetUtil.newURI("http://bug1060642.example.com/beards/are/pretty/great"); + yield PlacesTestUtils.addVisits([{uri: uri, title: ""}]); + + yield promiseAutocompleteResultPopup("bug1060642"); + ok(gURLBar.popup.richlistbox.children.length > 1, "Should get at least 2 results"); + let result = gURLBar.popup.richlistbox.children[1]; + is(result._titleText.textContent, "bug1060642.example.com", "Result title should be as expected"); + + gURLBar.popup.hidePopup(); + yield promisePopupHidden(gURLBar.popup); + gBrowser.removeTab(tab); +}); diff --git a/browser/base/content/test/urlbar/browser_autocomplete_tag_star_visibility.js b/browser/base/content/test/urlbar/browser_autocomplete_tag_star_visibility.js new file mode 100644 index 000000000..8a69b4b44 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_autocomplete_tag_star_visibility.js @@ -0,0 +1,102 @@ +add_task(function*() { + registerCleanupFunction(() => { + PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.unfiledBookmarksFolderId); + }); + + function* addTagItem(tagName) { + let uri = NetUtil.newURI(`http://example.com/this/is/tagged/${tagName}`); + PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId, + uri, + PlacesUtils.bookmarks.DEFAULT_INDEX, + `test ${tagName}`); + PlacesUtils.tagging.tagURI(uri, [tagName]); + yield PlacesTestUtils.addVisits([{uri: uri, title: `Test page with tag ${tagName}`}]); + } + + // We use different tags for each part of the test, as otherwise the + // autocomplete code tries to be smart by using the previously cached element + // without updating it (since all parameters it knows about are the same). + + let testcases = [{ + description: "Test with suggest.bookmark=true", + tagName: "tagtest1", + prefs: { + "suggest.bookmark": true, + }, + input: "tagtest1", + expected: { + type: "bookmark", + typeImageVisible: true, + }, + }, { + description: "Test with suggest.bookmark=false", + tagName: "tagtest2", + prefs: { + "suggest.bookmark": false, + }, + input: "tagtest2", + expected: { + type: "tag", + typeImageVisible: false, + }, + }, { + description: "Test with suggest.bookmark=true (again)", + tagName: "tagtest3", + prefs: { + "suggest.bookmark": true, + }, + input: "tagtest3", + expected: { + type: "bookmark", + typeImageVisible: true, + }, + }, { + description: "Test with bookmark restriction token", + tagName: "tagtest4", + prefs: { + "suggest.bookmark": true, + }, + input: "* tagtest4", + expected: { + type: "bookmark", + typeImageVisible: true, + }, + }, { + description: "Test with history restriction token", + tagName: "tagtest5", + prefs: { + "suggest.bookmark": true, + }, + input: "^ tagtest5", + expected: { + type: "tag", + typeImageVisible: false, + }, + }]; + + for (let testcase of testcases) { + info(`Test case: ${testcase.description}`); + + yield addTagItem(testcase.tagName); + for (let prefName of Object.keys(testcase.prefs)) { + Services.prefs.setBoolPref(`browser.urlbar.${prefName}`, testcase.prefs[prefName]); + } + + yield promiseAutocompleteResultPopup(testcase.input); + let result = gURLBar.popup.richlistbox.children[1]; + ok(result && !result.collasped, "Should have result"); + + is(result.getAttribute("type"), testcase.expected.type, "Result should have expected type"); + + let typeIconStyle = window.getComputedStyle(result._typeIcon); + let imageURL = typeIconStyle.listStyleImage; + if (testcase.expected.typeImageVisible) { + ok(/^url\(.+\)$/.test(imageURL), "Type image should be visible"); + } else { + is(imageURL, "none", "Type image should be hidden"); + } + + gURLBar.popup.hidePopup(); + yield promisePopupHidden(gURLBar.popup); + } +}); diff --git a/browser/base/content/test/urlbar/browser_bug1003461-switchtab-override.js b/browser/base/content/test/urlbar/browser_bug1003461-switchtab-override.js new file mode 100644 index 000000000..89f604491 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_bug1003461-switchtab-override.js @@ -0,0 +1,61 @@ +/* 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/. */ + +add_task(function* test_switchtab_override() { + let testURL = "http://example.org/browser/browser/base/content/test/urlbar/dummy_page.html"; + + info("Opening first tab"); + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, testURL); + + info("Opening and selecting second tab"); + let secondTab = gBrowser.selectedTab = gBrowser.addTab(); + registerCleanupFunction(() => { + try { + gBrowser.removeTab(tab); + gBrowser.removeTab(secondTab); + } catch (ex) { /* tabs may have already been closed in case of failure */ } + }); + + info("Wait for autocomplete") + let deferred = Promise.defer(); + let onSearchComplete = gURLBar.onSearchComplete; + registerCleanupFunction(() => { + gURLBar.onSearchComplete = onSearchComplete; + }); + gURLBar.onSearchComplete = function () { + ok(gURLBar.popupOpen, "The autocomplete popup is correctly open"); + onSearchComplete.apply(gURLBar); + deferred.resolve(); + } + + gURLBar.focus(); + gURLBar.value = "dummy_pag"; + EventUtils.synthesizeKey("e", {}); + yield deferred.promise; + + info("Select second autocomplete popup entry"); + EventUtils.synthesizeKey("VK_DOWN", {}); + ok(/moz-action:switchtab/.test(gURLBar.value), "switch to tab entry found"); + + info("Override switch-to-tab"); + deferred = Promise.defer(); + // In case of failure this would switch tab. + let onTabSelect = event => { + deferred.reject(new Error("Should have overridden switch to tab")); + }; + gBrowser.tabContainer.addEventListener("TabSelect", onTabSelect, false); + registerCleanupFunction(() => { + gBrowser.tabContainer.removeEventListener("TabSelect", onTabSelect, false); + }); + // Otherwise it would load the page. + BrowserTestUtils.browserLoaded(secondTab.linkedBrowser).then(deferred.resolve); + + EventUtils.synthesizeKey("VK_SHIFT", { type: "keydown" }); + EventUtils.synthesizeKey("VK_RETURN", { }); + info(`gURLBar.value = ${gURLBar.value}`); + EventUtils.synthesizeKey("VK_SHIFT", { type: "keyup" }); + yield deferred.promise; + + yield PlacesTestUtils.clearHistory(); +}); diff --git a/browser/base/content/test/urlbar/browser_bug1024133-switchtab-override-keynav.js b/browser/base/content/test/urlbar/browser_bug1024133-switchtab-override-keynav.js new file mode 100644 index 000000000..2d97ea07b --- /dev/null +++ b/browser/base/content/test/urlbar/browser_bug1024133-switchtab-override-keynav.js @@ -0,0 +1,37 @@ +/* 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/. */ + +add_task(function* test_switchtab_override_keynav() { + let testURL = "http://example.org/browser/browser/base/content/test/urlbar/dummy_page.html"; + + info("Opening first tab"); + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, testURL); + + info("Opening and selecting second tab"); + let secondTab = gBrowser.selectedTab = gBrowser.addTab(); + registerCleanupFunction(() => { + try { + gBrowser.removeTab(tab); + gBrowser.removeTab(secondTab); + } catch (ex) { /* tabs may have already been closed in case of failure */ } + return PlacesTestUtils.clearHistory(); + }); + + gURLBar.focus(); + gURLBar.value = "dummy_pag"; + EventUtils.synthesizeKey("e", {}); + yield promiseSearchComplete(); + + info("Select second autocomplete popup entry"); + EventUtils.synthesizeKey("VK_DOWN", {}); + ok(/moz-action:switchtab/.test(gURLBar.value), "switch to tab entry found"); + + info("Shift+left on switch-to-tab entry"); + + EventUtils.synthesizeKey("VK_SHIFT", { type: "keydown" }); + EventUtils.synthesizeKey("VK_LEFT", { shiftKey: true }); + EventUtils.synthesizeKey("VK_SHIFT", { type: "keyup" }); + + ok(!/moz-action:switchtab/.test(gURLBar.inputField.value), "switch to tab should be hidden"); +}); diff --git a/browser/base/content/test/urlbar/browser_bug1025195_switchToTabHavingURI_aOpenParams.js b/browser/base/content/test/urlbar/browser_bug1025195_switchToTabHavingURI_aOpenParams.js new file mode 100644 index 000000000..9e779ade1 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_bug1025195_switchToTabHavingURI_aOpenParams.js @@ -0,0 +1,124 @@ +/* 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/. */ + +add_task(function* test_ignoreFragment() { + let tabRefAboutHome = + yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:home#1"); + yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla"); + let numTabsAtStart = gBrowser.tabs.length; + + switchTab("about:home#1", true); + switchTab("about:mozilla", true); + + let hashChangePromise = ContentTask.spawn(tabRefAboutHome.linkedBrowser, null, function* () { + yield ContentTaskUtils.waitForEvent(this, "hashchange", false); + }); + switchTab("about:home#2", true, { ignoreFragment: "whenComparingAndReplace" }); + is(tabRefAboutHome, gBrowser.selectedTab, "The same about:home tab should be switched to"); + yield hashChangePromise; + is(gBrowser.currentURI.ref, "2", "The ref should be updated to the new ref"); + switchTab("about:mozilla", true); + switchTab("about:home#3", true, { ignoreFragment: "whenComparing" }); + is(tabRefAboutHome, gBrowser.selectedTab, "The same about:home tab should be switched to"); + is(gBrowser.currentURI.ref, "2", "The ref should be unchanged since the fragment is only ignored when comparing"); + switchTab("about:mozilla", true); + switchTab("about:home#1", false); + isnot(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should not be initial about:blank tab"); + is(gBrowser.tabs.length, numTabsAtStart + 1, "Should have one new tab opened"); + switchTab("about:mozilla", true); + switchTab("about:home", true, {ignoreFragment: "whenComparingAndReplace"}); + yield BrowserTestUtils.waitForCondition(function() { + return tabRefAboutHome.linkedBrowser.currentURI.spec == "about:home"; + }); + is(tabRefAboutHome.linkedBrowser.currentURI.spec, "about:home", "about:home shouldn't have hash"); + switchTab("about:about", false, { ignoreFragment: "whenComparingAndReplace" }); + cleanupTestTabs(); +}); + +add_task(function* test_ignoreQueryString() { + let tabRefAboutHome = + yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:home?hello=firefox"); + yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla"); + + switchTab("about:home?hello=firefox", true); + switchTab("about:home?hello=firefoxos", false); + // Remove the last opened tab to test ignoreQueryString option. + gBrowser.removeCurrentTab(); + switchTab("about:home?hello=firefoxos", true, { ignoreQueryString: true }); + is(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should be the initial about:home tab"); + is(gBrowser.currentURI.spec, "about:home?hello=firefox", "The spec should NOT be updated to the new query string"); + cleanupTestTabs(); +}); + +add_task(function* test_replaceQueryString() { + let tabRefAboutHome = + yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:home?hello=firefox"); + yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla"); + + switchTab("about:home", false); + switchTab("about:home?hello=firefox", true); + switchTab("about:home?hello=firefoxos", false); + // Remove the last opened tab to test replaceQueryString option. + gBrowser.removeCurrentTab(); + switchTab("about:home?hello=firefoxos", true, { replaceQueryString: true }); + is(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should be the initial about:home tab"); + // Wait for the tab to load the new URI spec. + yield BrowserTestUtils.browserLoaded(tabRefAboutHome.linkedBrowser); + is(gBrowser.currentURI.spec, "about:home?hello=firefoxos", "The spec should be updated to the new spec"); + cleanupTestTabs(); +}); + +add_task(function* test_replaceQueryStringAndFragment() { + let tabRefAboutHome = + yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:home?hello=firefox#aaa"); + let tabRefAboutMozilla = + yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla?hello=firefoxos#aaa"); + + switchTab("about:home", false); + gBrowser.removeCurrentTab(); + switchTab("about:home?hello=firefox#aaa", true); + is(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should be the initial about:home tab"); + switchTab("about:mozilla?hello=firefox#bbb", true, { replaceQueryString: true, ignoreFragment: "whenComparingAndReplace" }); + is(tabRefAboutMozilla, gBrowser.selectedTab, "Selected tab should be the initial about:mozilla tab"); + switchTab("about:home?hello=firefoxos#bbb", true, { ignoreQueryString: true, ignoreFragment: "whenComparingAndReplace" }); + is(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should be the initial about:home tab"); + cleanupTestTabs(); +}); + +add_task(function* test_ignoreQueryStringIgnoresFragment() { + let tabRefAboutHome = + yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:home?hello=firefox#aaa"); + yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla?hello=firefoxos#aaa"); + + switchTab("about:home?hello=firefox#bbb", false, { ignoreQueryString: true }); + gBrowser.removeCurrentTab(); + switchTab("about:home?hello=firefoxos#aaa", true, { ignoreQueryString: true }); + is(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should be the initial about:home tab"); + cleanupTestTabs(); +}); + +// Begin helpers + +function cleanupTestTabs() { + while (gBrowser.tabs.length > 1) + gBrowser.removeCurrentTab(); +} + +function switchTab(aURI, aShouldFindExistingTab, aOpenParams = {}) { + // Build the description before switchToTabHavingURI deletes the object properties. + let msg = `Should switch to existing ${aURI} tab if one existed, ` + + `${(aOpenParams.ignoreFragment ? "ignoring" : "including")} fragment portion, `; + if (aOpenParams.replaceQueryString) { + msg += "replacing"; + } else if (aOpenParams.ignoreQueryString) { + msg += "ignoring"; + } else { + msg += "including"; + } + msg += " query string."; + let tabFound = switchToTabHavingURI(aURI, true, aOpenParams); + is(tabFound, aShouldFindExistingTab, msg); +} + +registerCleanupFunction(cleanupTestTabs); diff --git a/browser/base/content/test/urlbar/browser_bug1070778.js b/browser/base/content/test/urlbar/browser_bug1070778.js new file mode 100644 index 000000000..ab88d04d8 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_bug1070778.js @@ -0,0 +1,55 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +function is_selected(index) { + is(gURLBar.popup.richlistbox.selectedIndex, index, `Item ${index + 1} should be selected`); +} + +add_task(function*() { + let bookmarks = []; + bookmarks.push((yield PlacesUtils.bookmarks + .insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: "http://example.com/?q=%s", + title: "test" }))); + yield PlacesUtils.keywords.insert({ keyword: "keyword", + url: "http://example.com/?q=%s" }); + + // This item only needed so we can select the keyword item, select something + // else, then select the keyword item again. + bookmarks.push((yield PlacesUtils.bookmarks + .insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: "http://example.com/keyword", + title: "keyword abc" }))); + + registerCleanupFunction(function* () { + for (let bm of bookmarks) { + yield PlacesUtils.bookmarks.remove(bm); + } + }); + + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla"); + yield promiseAutocompleteResultPopup("keyword a"); + + // First item should already be selected + is_selected(0); + // Select next one (important!) + EventUtils.synthesizeKey("VK_DOWN", {}); + is_selected(1); + // Re-select keyword item + EventUtils.synthesizeKey("VK_UP", {}); + is_selected(0); + + EventUtils.synthesizeKey("b", {}); + yield promiseSearchComplete(); + + is(gURLBar.textValue, "keyword ab", "urlbar should have expected input"); + + let result = gURLBar.popup.richlistbox.firstChild; + isnot(result, null, "Should have first item"); + let uri = NetUtil.newURI(result.getAttribute("url")); + is(uri.spec, PlacesUtils.mozActionURI("keyword", {url: "http://example.com/?q=ab", input: "keyword ab"}), "Expect correct url"); + + EventUtils.synthesizeKey("VK_ESCAPE", {}); + yield promisePopupHidden(gURLBar.popup); + gBrowser.removeTab(tab); +}); diff --git a/browser/base/content/test/urlbar/browser_bug1104165-switchtab-decodeuri.js b/browser/base/content/test/urlbar/browser_bug1104165-switchtab-decodeuri.js new file mode 100644 index 000000000..d165d7304 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_bug1104165-switchtab-decodeuri.js @@ -0,0 +1,29 @@ +add_task(function* test_switchtab_decodeuri() { + info("Opening first tab"); + const TEST_URL = "http://example.org/browser/browser/base/content/test/urlbar/dummy_page.html#test%7C1"; + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL); + + info("Opening and selecting second tab"); + gBrowser.selectedTab = gBrowser.addTab(); + + info("Wait for autocomplete") + yield promiseAutocompleteResultPopup("dummy_page"); + + info("Select autocomplete popup entry"); + EventUtils.synthesizeKey("VK_DOWN", {}); + ok(gURLBar.value.startsWith("moz-action:switchtab"), "switch to tab entry found"); + + info("switch-to-tab"); + yield new Promise((resolve, reject) => { + // In case of success it should switch tab. + gBrowser.tabContainer.addEventListener("TabSelect", function select() { + gBrowser.tabContainer.removeEventListener("TabSelect", select, false); + is(gBrowser.selectedTab, tab, "Should have switched to the right tab"); + resolve(); + }, false); + EventUtils.synthesizeKey("VK_RETURN", { }); + }); + + gBrowser.removeCurrentTab(); + yield PlacesTestUtils.clearHistory(); +}); diff --git a/browser/base/content/test/urlbar/browser_bug1225194-remotetab.js b/browser/base/content/test/urlbar/browser_bug1225194-remotetab.js new file mode 100644 index 000000000..3b4a44e76 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_bug1225194-remotetab.js @@ -0,0 +1,16 @@ +add_task(function* test_remotetab_opens() { + const url = "http://example.org/browser/browser/base/content/test/urlbar/dummy_page.html"; + yield BrowserTestUtils.withNewTab({url: "about:robots", gBrowser}, function* () { + // Set the urlbar to include the moz-action + gURLBar.value = "moz-action:remotetab," + JSON.stringify({ url }); + // Focus the urlbar so we can press enter + gURLBar.focus(); + + // The URL is going to open in the current tab as it is currently about:blank + let promiseTabLoaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + EventUtils.synthesizeKey("VK_RETURN", {}); + yield promiseTabLoaded; + + Assert.equal(gBrowser.selectedTab.linkedBrowser.currentURI.spec, url, "correct URL loaded"); + }); +}); diff --git a/browser/base/content/test/urlbar/browser_bug304198.js b/browser/base/content/test/urlbar/browser_bug304198.js new file mode 100644 index 000000000..dc8d39fae --- /dev/null +++ b/browser/base/content/test/urlbar/browser_bug304198.js @@ -0,0 +1,109 @@ +/* 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/. */ + +add_task(function* () { + let charsToDelete, deletedURLTab, fullURLTab, partialURLTab, testPartialURL, testURL; + + charsToDelete = 5; + deletedURLTab = gBrowser.addTab(); + fullURLTab = gBrowser.addTab(); + partialURLTab = gBrowser.addTab(); + testURL = "http://example.org/browser/browser/base/content/test/urlbar/dummy_page.html"; + + let loaded1 = BrowserTestUtils.browserLoaded(deletedURLTab.linkedBrowser, testURL); + let loaded2 = BrowserTestUtils.browserLoaded(fullURLTab.linkedBrowser, testURL); + let loaded3 = BrowserTestUtils.browserLoaded(partialURLTab.linkedBrowser, testURL); + deletedURLTab.linkedBrowser.loadURI(testURL); + fullURLTab.linkedBrowser.loadURI(testURL); + partialURLTab.linkedBrowser.loadURI(testURL); + yield Promise.all([loaded1, loaded2, loaded3]); + + testURL = gURLBar.trimValue(testURL); + testPartialURL = testURL.substr(0, (testURL.length - charsToDelete)); + + function cleanUp() { + gBrowser.removeTab(fullURLTab); + gBrowser.removeTab(partialURLTab); + gBrowser.removeTab(deletedURLTab); + } + + function* cycleTabs() { + yield BrowserTestUtils.switchTab(gBrowser, fullURLTab); + is(gURLBar.textValue, testURL, 'gURLBar.textValue should be testURL after switching back to fullURLTab'); + + yield BrowserTestUtils.switchTab(gBrowser, partialURLTab); + is(gURLBar.textValue, testPartialURL, 'gURLBar.textValue should be testPartialURL after switching back to partialURLTab'); + yield BrowserTestUtils.switchTab(gBrowser, deletedURLTab); + is(gURLBar.textValue, '', 'gURLBar.textValue should be "" after switching back to deletedURLTab'); + + yield BrowserTestUtils.switchTab(gBrowser, fullURLTab); + is(gURLBar.textValue, testURL, 'gURLBar.textValue should be testURL after switching back to fullURLTab'); + } + + function urlbarBackspace() { + return new Promise((resolve, reject) => { + gBrowser.selectedBrowser.focus(); + gURLBar.addEventListener("input", function () { + gURLBar.removeEventListener("input", arguments.callee, false); + resolve(); + }, false); + gURLBar.focus(); + if (gURLBar.selectionStart == gURLBar.selectionEnd) { + gURLBar.selectionStart = gURLBar.selectionEnd = gURLBar.textValue.length; + } + EventUtils.synthesizeKey("VK_BACK_SPACE", {}); + }); + } + + function* prepareDeletedURLTab() { + yield BrowserTestUtils.switchTab(gBrowser, deletedURLTab); + is(gURLBar.textValue, testURL, 'gURLBar.textValue should be testURL after initial switch to deletedURLTab'); + + // simulate the user removing the whole url from the location bar + gPrefService.setBoolPref("browser.urlbar.clickSelectsAll", true); + + yield urlbarBackspace(); + is(gURLBar.textValue, "", 'gURLBar.textValue should be "" (just set)'); + if (gPrefService.prefHasUserValue("browser.urlbar.clickSelectsAll")) { + gPrefService.clearUserPref("browser.urlbar.clickSelectsAll"); + } + } + + function* prepareFullURLTab() { + yield BrowserTestUtils.switchTab(gBrowser, fullURLTab); + is(gURLBar.textValue, testURL, 'gURLBar.textValue should be testURL after initial switch to fullURLTab'); + } + + function* preparePartialURLTab() { + yield BrowserTestUtils.switchTab(gBrowser, partialURLTab); + is(gURLBar.textValue, testURL, 'gURLBar.textValue should be testURL after initial switch to partialURLTab'); + + // simulate the user removing part of the url from the location bar + gPrefService.setBoolPref("browser.urlbar.clickSelectsAll", false); + + let deleted = 0; + while (deleted < charsToDelete) { + yield urlbarBackspace(arguments.callee); + deleted++; + } + + is(gURLBar.textValue, testPartialURL, "gURLBar.textValue should be testPartialURL (just set)"); + if (gPrefService.prefHasUserValue("browser.urlbar.clickSelectsAll")) { + gPrefService.clearUserPref("browser.urlbar.clickSelectsAll"); + } + } + + // prepare the three tabs required by this test + + // First tab + yield* prepareFullURLTab(); + yield* preparePartialURLTab(); + yield* prepareDeletedURLTab(); + + // now cycle the tabs and make sure everything looks good + yield* cycleTabs(); + cleanUp(); +}); + + diff --git a/browser/base/content/test/urlbar/browser_bug556061.js b/browser/base/content/test/urlbar/browser_bug556061.js new file mode 100644 index 000000000..4c6ac5bf5 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_bug556061.js @@ -0,0 +1,98 @@ +/* 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 testURL = "http://example.org/browser/browser/base/content/test/urlbar/dummy_page.html"; +var testActionURL = "moz-action:switchtab," + JSON.stringify({url: testURL}); +testURL = gURLBar.trimValue(testURL); +var testTab; + +function runNextTest() { + if (tests.length) { + let t = tests.shift(); + waitForClipboard(t.expected, t.setup, function() { + t.success(); + runNextTest(); + }, cleanup); + } + else { + cleanup(); + } +} + +function cleanup() { + gBrowser.removeTab(testTab); + finish(); +} + +var tests = [ + { + expected: testURL, + setup: function() { + gURLBar.value = testActionURL; + gURLBar.valueIsTyped = true; + is(gURLBar.value, testActionURL, "gURLBar starts with the correct real value"); + is(gURLBar.textValue, testURL, "gURLBar starts with the correct display value"); + + // Focus the urlbar so we can select it all & copy + gURLBar.focus(); + gURLBar.select(); + goDoCommand("cmd_copy"); + }, + success: function() { + is(gURLBar.value, testActionURL, "gURLBar.value didn't change when copying"); + } + }, + { + expected: testURL.substring(0, 10), + setup: function() { + // Set selectionStart/End manually and make sure it matches the substring + gURLBar.selectionStart = 0; + gURLBar.selectionEnd = 10; + goDoCommand("cmd_copy"); + }, + success: function() { + is(gURLBar.value, testActionURL, "gURLBar.value didn't change when copying"); + } + }, + { + expected: testURL, + setup: function() { + // Setup for cut test... + // Select all + gURLBar.select(); + goDoCommand("cmd_cut"); + }, + success: function() { + is(gURLBar.value, "", "gURLBar.value is now empty"); + } + }, + { + expected: testURL.substring(testURL.length - 10, testURL.length), + setup: function() { + // Reset urlbar value + gURLBar.value = testActionURL; + gURLBar.valueIsTyped = true; + // Sanity check that we have the right value + is(gURLBar.value, testActionURL, "gURLBar starts with the correct real value"); + is(gURLBar.textValue, testURL, "gURLBar starts with the correct display value"); + + // Now just select part of the value & cut that. + gURLBar.selectionStart = testURL.length - 10; + gURLBar.selectionEnd = testURL.length; + goDoCommand("cmd_cut"); + }, + success: function() { + is(gURLBar.value, testURL.substring(0, testURL.length - 10), "gURLBar.value has the correct value"); + } + } +]; + +function test() { + waitForExplicitFinish(); + testTab = gBrowser.addTab(); + gBrowser.selectedTab = testTab; + + // Kick off the testing + runNextTest(); +} diff --git a/browser/base/content/test/urlbar/browser_bug562649.js b/browser/base/content/test/urlbar/browser_bug562649.js new file mode 100644 index 000000000..f56e430ee --- /dev/null +++ b/browser/base/content/test/urlbar/browser_bug562649.js @@ -0,0 +1,24 @@ +function test() { + const URI = "data:text/plain,bug562649"; + browserDOMWindow.openURI(makeURI(URI), + null, + Ci.nsIBrowserDOMWindow.OPEN_NEWTAB, + Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL); + + is(gBrowser.userTypedValue, URI, "userTypedValue matches test URI"); + is(gURLBar.value, URI, "location bar value matches test URI"); + + gBrowser.selectedTab = gBrowser.addTab(); + gBrowser.removeCurrentTab({ skipPermitUnload: true }); + is(gBrowser.userTypedValue, URI, "userTypedValue matches test URI after switching tabs"); + is(gURLBar.value, URI, "location bar value matches test URI after switching tabs"); + + waitForExplicitFinish(); + BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(() => { + is(gBrowser.userTypedValue, null, "userTypedValue is null as the page has loaded"); + is(gURLBar.value, URI, "location bar value matches test URI as the page has loaded"); + + gBrowser.removeCurrentTab({ skipPermitUnload: true }); + finish(); + }); +} diff --git a/browser/base/content/test/urlbar/browser_bug623155.js b/browser/base/content/test/urlbar/browser_bug623155.js new file mode 100644 index 000000000..dd6ff8c85 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_bug623155.js @@ -0,0 +1,137 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const REDIRECT_FROM = "https://example.com/browser/browser/base/content/test/urlbar/" + + "redirect_bug623155.sjs"; + +const REDIRECT_TO = "https://www.bank1.com/"; // Bad-cert host. + +function isRedirectedURISpec(aURISpec) { + return isRedirectedURI(Services.io.newURI(aURISpec, null, null)); +} + +function isRedirectedURI(aURI) { + // Compare only their before-hash portion. + return Services.io.newURI(REDIRECT_TO, null, null) + .equalsExceptRef(aURI); +} + +/* + Test. + +1. Load +https://example.com/browser/browser/base/content/test/urlbar/redirect_bug623155.sjs#BG + in a background tab. + +2. The redirected URI is <https://www.bank1.com/#BG>, which displayes a cert + error page. + +3. Switch the tab to foreground. + +4. Check the URLbar's value, expecting <https://www.bank1.com/#BG> + +5. Load +https://example.com/browser/browser/base/content/test/urlbar/redirect_bug623155.sjs#FG + in the foreground tab. + +6. The redirected URI is <https://www.bank1.com/#FG>. And this is also + a cert-error page. + +7. Check the URLbar's value, expecting <https://www.bank1.com/#FG> + +8. End. + + */ + +var gNewTab; + +function test() { + waitForExplicitFinish(); + + // Load a URI in the background. + gNewTab = gBrowser.addTab(REDIRECT_FROM + "#BG"); + gBrowser.getBrowserForTab(gNewTab) + .webProgress + .addProgressListener(gWebProgressListener, + Components.interfaces.nsIWebProgress + .NOTIFY_LOCATION); +} + +var gWebProgressListener = { + QueryInterface: function(aIID) { + if (aIID.equals(Components.interfaces.nsIWebProgressListener) || + aIID.equals(Components.interfaces.nsISupportsWeakReference) || + aIID.equals(Components.interfaces.nsISupports)) + return this; + throw Components.results.NS_NOINTERFACE; + }, + + // --------------------------------------------------------------------------- + // NOTIFY_LOCATION mode should work fine without these methods. + // + // onStateChange: function() {}, + // onStatusChange: function() {}, + // onProgressChange: function() {}, + // onSecurityChange: function() {}, + // ---------------------------------------------------------------------------- + + onLocationChange: function(aWebProgress, aRequest, aLocation, aFlags) { + if (!aRequest) { + // This is bug 673752, or maybe initial "about:blank". + return; + } + + ok(gNewTab, "There is a new tab."); + ok(isRedirectedURI(aLocation), + "onLocationChange catches only redirected URI."); + + if (aLocation.ref == "BG") { + // This is background tab's request. + isnot(gNewTab, gBrowser.selectedTab, "This is a background tab."); + } else if (aLocation.ref == "FG") { + // This is foreground tab's request. + is(gNewTab, gBrowser.selectedTab, "This is a foreground tab."); + } + else { + // We shonuld not reach here. + ok(false, "This URI hash is not expected:" + aLocation.ref); + } + + let isSelectedTab = gNewTab.selected; + setTimeout(delayed, 0, isSelectedTab); + } +}; + +function delayed(aIsSelectedTab) { + // Switch tab and confirm URL bar. + if (!aIsSelectedTab) { + gBrowser.selectedTab = gNewTab; + } + + let currentURI = gBrowser.selectedBrowser.currentURI.spec; + ok(isRedirectedURISpec(currentURI), + "The content area is redirected. aIsSelectedTab:" + aIsSelectedTab); + is(gURLBar.value, currentURI, + "The URL bar shows the content URI. aIsSelectedTab:" + aIsSelectedTab); + + if (!aIsSelectedTab) { + // If this was a background request, go on a foreground request. + gBrowser.selectedBrowser.loadURI(REDIRECT_FROM + "#FG"); + } + else { + // Othrewise, nothing to do remains. + finish(); + } +} + +/* Cleanup */ +registerCleanupFunction(function() { + if (gNewTab) { + gBrowser.getBrowserForTab(gNewTab) + .webProgress + .removeProgressListener(gWebProgressListener); + + gBrowser.removeTab(gNewTab); + } + gNewTab = null; +}); diff --git a/browser/base/content/test/urlbar/browser_bug783614.js b/browser/base/content/test/urlbar/browser_bug783614.js new file mode 100644 index 000000000..ebc62e8fa --- /dev/null +++ b/browser/base/content/test/urlbar/browser_bug783614.js @@ -0,0 +1,13 @@ +/* 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() { + gURLBar.focus(); + gURLBar.inputField.value = "https://example.com/"; + gURLBar.selectionStart = 4; + gURLBar.selectionEnd = 5; + goDoCommand("cmd_cut"); + is(gURLBar.inputField.value, "http://example.com/", "location bar value after cutting 's' from https"); + gURLBar.handleRevert(); +} diff --git a/browser/base/content/test/urlbar/browser_canonizeURL.js b/browser/base/content/test/urlbar/browser_canonizeURL.js new file mode 100644 index 000000000..59ab54ca0 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_canonizeURL.js @@ -0,0 +1,42 @@ +add_task(function*() { + let testcases = [ + ["example", "http://www.example.net/", { shiftKey: true }], + // Check that a direct load is not overwritten by a previous canonization. + ["http://example.com/test/", "http://example.com/test/", {}], + ["ex-ample", "http://www.ex-ample.net/", { shiftKey: true }], + [" example ", "http://www.example.net/", { shiftKey: true }], + [" example/foo ", "http://www.example.net/foo", { shiftKey: true }], + [" example/foo bar ", "http://www.example.net/foo%20bar", { shiftKey: true }], + ["example.net", "http://example.net/", { shiftKey: true }], + ["http://example", "http://example/", { shiftKey: true }], + ["example:8080", "http://example:8080/", { shiftKey: true }], + ["ex-ample.foo", "http://ex-ample.foo/", { shiftKey: true }], + ["example.foo/bar ", "http://example.foo/bar", { shiftKey: true }], + ["1.1.1.1", "http://1.1.1.1/", { shiftKey: true }], + ["ftp://example", "ftp://example/", { shiftKey: true }], + ["ftp.example.bar", "http://ftp.example.bar/", { shiftKey: true }], + ["ex ample", Services.search.defaultEngine.getSubmission("ex ample", null, "keyword").uri.spec, { shiftKey: true }], + ]; + + // Disable autoFill for this test, since it could mess up the results. + let autoFill = Preferences.get("browser.urlbar.autoFill"); + Preferences.set("browser.urlbar.autoFill", false); + registerCleanupFunction(() => { + Preferences.set("browser.urlbar.autoFill", autoFill); + }); + + for (let [inputValue, expectedURL, options] of testcases) { + let promiseLoad = waitForDocLoadAndStopIt(expectedURL); + gURLBar.focus(); + if (Object.keys(options).length > 0) { + gURLBar.selectionStart = gURLBar.selectionEnd = + gURLBar.inputField.value.length; + gURLBar.inputField.value = inputValue.slice(0, -1); + EventUtils.synthesizeKey(inputValue.slice(-1), {}); + } else { + gURLBar.textValue = inputValue; + } + EventUtils.synthesizeKey("VK_RETURN", options); + yield promiseLoad; + } +}); diff --git a/browser/base/content/test/urlbar/browser_dragdropURL.js b/browser/base/content/test/urlbar/browser_dragdropURL.js new file mode 100644 index 000000000..ec2906700 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_dragdropURL.js @@ -0,0 +1,15 @@ +"use strict"; + +const TEST_URL = "data:text/html,a test page"; +const DRAG_URL = "http://www.example.com/"; + +add_task(function* checkURLBarUpdateForDrag() { + yield BrowserTestUtils.withNewTab(TEST_URL, function* (browser) { + // Have to use something other than the URL bar as a source, so picking the + // downloads button somewhat arbitrarily: + EventUtils.synthesizeDrop(document.getElementById("downloads-button"), gURLBar, + [[{type: "text/plain", data: DRAG_URL}]], "copy", window); + is(gURLBar.value, TEST_URL, "URL bar value should not have changed"); + is(gBrowser.selectedBrowser.userTypedValue, null, "Stored URL bar value should not have changed"); + }); +}); diff --git a/browser/base/content/test/urlbar/browser_locationBarCommand.js b/browser/base/content/test/urlbar/browser_locationBarCommand.js new file mode 100644 index 000000000..935bdf758 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_locationBarCommand.js @@ -0,0 +1,218 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const TEST_VALUE = "example.com"; +const START_VALUE = "example.org"; + +add_task(function* setup() { + Services.prefs.setBoolPref("browser.altClickSave", true); + + registerCleanupFunction(() => { + Services.prefs.clearUserPref("browser.altClickSave"); + }); +}); + +add_task(function* alt_left_click_test() { + info("Running test: Alt left click"); + + // Monkey patch saveURL() to avoid dealing with file save code paths. + let oldSaveURL = saveURL; + let saveURLPromise = new Promise(resolve => { + saveURL = () => { + // Restore old saveURL() value. + saveURL = oldSaveURL; + resolve(); + }; + }); + + triggerCommand(true, {altKey: true}); + + yield saveURLPromise; + ok(true, "SaveURL was called"); + is(gURLBar.value, "", "Urlbar reverted to original value"); +}); + +add_task(function* shift_left_click_test() { + info("Running test: Shift left click"); + + let newWindowPromise = BrowserTestUtils.waitForNewWindow(); + triggerCommand(true, {shiftKey: true}); + let win = yield newWindowPromise; + + // Wait for the initial browser to load. + let browser = win.gBrowser.selectedBrowser; + let destinationURL = "http://" + TEST_VALUE + "/"; + yield Promise.all([ + BrowserTestUtils.browserLoaded(browser), + BrowserTestUtils.waitForLocationChange(win.gBrowser, destinationURL) + ]); + + info("URL should be loaded in a new window"); + is(gURLBar.value, "", "Urlbar reverted to original value"); + yield promiseCheckChildNoFocusedElement(gBrowser.selectedBrowser); + is(document.activeElement, gBrowser.selectedBrowser, "Content window should be focused"); + is(win.gURLBar.textValue, TEST_VALUE, "New URL is loaded in new window"); + + // Cleanup. + yield BrowserTestUtils.closeWindow(win); +}); + +add_task(function* right_click_test() { + info("Running test: Right click on go button"); + + // Add a new tab. + yield* promiseOpenNewTab(); + + triggerCommand(true, {button: 2}); + + // Right click should do nothing (context menu will be shown). + is(gURLBar.value, TEST_VALUE, "Urlbar still has the value we entered"); + + // Cleanup. + gBrowser.removeCurrentTab(); +}); + +add_task(function* shift_accel_left_click_test() { + info("Running test: Shift+Ctrl/Cmd left click on go button"); + + // Add a new tab. + let tab = yield* promiseOpenNewTab(); + + let loadStartedPromise = promiseLoadStarted(); + triggerCommand(true, {accelKey: true, shiftKey: true}); + yield loadStartedPromise; + + // Check the load occurred in a new background tab. + info("URL should be loaded in a new background tab"); + is(gURLBar.value, "", "Urlbar reverted to original value"); + ok(!gURLBar.focused, "Urlbar is no longer focused after urlbar command"); + is(gBrowser.selectedTab, tab, "Focus did not change to the new tab"); + + // Select the new background tab + gBrowser.selectedTab = gBrowser.selectedTab.nextSibling; + is(gURLBar.value, TEST_VALUE, "New URL is loaded in new tab"); + + // Cleanup. + gBrowser.removeCurrentTab(); + gBrowser.removeCurrentTab(); +}); + +add_task(function* load_in_current_tab_test() { + let tests = [ + {desc: "Simple return keypress"}, + {desc: "Left click on go button", click: true}, + {desc: "Ctrl/Cmd+Return keypress", event: {accelKey: true}}, + {desc: "Alt+Return keypress in a blank tab", event: {altKey: true}} + ]; + + for (let test of tests) { + info(`Running test: ${test.desc}`); + + // Add a new tab. + let tab = yield* promiseOpenNewTab(); + + // Trigger a load and check it occurs in the current tab. + let loadStartedPromise = promiseLoadStarted(); + triggerCommand(test.click || false, test.event || {}); + yield loadStartedPromise; + + info("URL should be loaded in the current tab"); + is(gURLBar.value, TEST_VALUE, "Urlbar still has the value we entered"); + yield promiseCheckChildNoFocusedElement(gBrowser.selectedBrowser); + is(document.activeElement, gBrowser.selectedBrowser, "Content window should be focused"); + is(gBrowser.selectedTab, tab, "New URL was loaded in the current tab"); + + // Cleanup. + gBrowser.removeCurrentTab(); + } +}); + +add_task(function* load_in_new_tab_test() { + let tests = [ + {desc: "Ctrl/Cmd left click on go button", click: true, event: {accelKey: true}}, + {desc: "Alt+Return keypress in a dirty tab", event: {altKey: true}, url: START_VALUE} + ]; + + for (let test of tests) { + info(`Running test: ${test.desc}`); + + // Add a new tab. + let tab = yield* promiseOpenNewTab(test.url || "about:blank"); + + // Trigger a load and check it occurs in the current tab. + let tabSwitchedPromise = promiseNewTabSwitched(); + triggerCommand(test.click || false, test.event || {}); + yield tabSwitchedPromise; + + // Check the load occurred in a new tab. + info("URL should be loaded in a new focused tab"); + is(gURLBar.inputField.value, TEST_VALUE, "Urlbar still has the value we entered"); + yield promiseCheckChildNoFocusedElement(gBrowser.selectedBrowser); + is(document.activeElement, gBrowser.selectedBrowser, "Content window should be focused"); + isnot(gBrowser.selectedTab, tab, "New URL was loaded in a new tab"); + + // Cleanup. + gBrowser.removeCurrentTab(); + gBrowser.removeCurrentTab(); + } +}); + +function triggerCommand(shouldClick, event) { + gURLBar.value = TEST_VALUE; + gURLBar.focus(); + + if (shouldClick) { + is(gURLBar.getAttribute("pageproxystate"), "invalid", + "page proxy state must be invalid for go button to be visible"); + + let goButton = document.getElementById("urlbar-go-button"); + EventUtils.synthesizeMouseAtCenter(goButton, event); + } else { + EventUtils.synthesizeKey("VK_RETURN", event); + } +} + +function promiseLoadStarted() { + return new Promise(resolve => { + gBrowser.addTabsProgressListener({ + onStateChange(browser, webProgress, req, flags, status) { + if (flags & Ci.nsIWebProgressListener.STATE_START) { + gBrowser.removeTabsProgressListener(this); + resolve(); + } + } + }); + }); +} + +function* promiseOpenNewTab(url = "about:blank") { + let tab = gBrowser.addTab(url); + let tabSwitchPromise = promiseNewTabSwitched(tab); + gBrowser.selectedTab = tab; + yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + yield tabSwitchPromise; + return tab; +} + +function promiseNewTabSwitched() { + return new Promise(resolve => { + gBrowser.addEventListener("TabSwitchDone", function onSwitch() { + gBrowser.removeEventListener("TabSwitchDone", onSwitch); + executeSoon(resolve); + }); + }); +} + +function promiseCheckChildNoFocusedElement(browser) +{ + if (!gMultiProcessBrowser) { + Assert.equal(Services.focus.focusedElement, null, "There should be no focused element"); + return null; + } + + return ContentTask.spawn(browser, { }, function* () { + const fm = Components.classes["@mozilla.org/focus-manager;1"]. + getService(Components.interfaces.nsIFocusManager); + Assert.equal(fm.focusedElement, null, "There should be no focused element"); + }); +} diff --git a/browser/base/content/test/urlbar/browser_locationBarExternalLoad.js b/browser/base/content/test/urlbar/browser_locationBarExternalLoad.js new file mode 100644 index 000000000..31fc84768 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_locationBarExternalLoad.js @@ -0,0 +1,65 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const url = "data:text/html,<body>hi"; + +add_task(function*() { + yield* testURL(url, urlEnter); + yield* testURL(url, urlClick); +}); + +function urlEnter(url) { + gURLBar.value = url; + gURLBar.focus(); + EventUtils.synthesizeKey("VK_RETURN", {}); +} + +function urlClick(url) { + gURLBar.value = url; + gURLBar.focus(); + let goButton = document.getElementById("urlbar-go-button"); + EventUtils.synthesizeMouseAtCenter(goButton, {}); +} + +function promiseNewTabSwitched() { + return new Promise(resolve => { + gBrowser.addEventListener("TabSwitchDone", function onSwitch() { + gBrowser.removeEventListener("TabSwitchDone", onSwitch); + executeSoon(resolve); + }); + }); +} + +function* testURL(url, loadFunc, endFunc) { + let tabSwitchedPromise = promiseNewTabSwitched(); + let tab = gBrowser.selectedTab = gBrowser.addTab(); + let browser = gBrowser.selectedBrowser; + + let pageshowPromise = BrowserTestUtils.waitForContentEvent(browser, "pageshow"); + + yield tabSwitchedPromise; + yield pageshowPromise; + + let pagePrincipal = gBrowser.contentPrincipal; + loadFunc(url); + + yield BrowserTestUtils.waitForContentEvent(browser, "pageshow"); + + yield ContentTask.spawn(browser, { isRemote: gMultiProcessBrowser }, + function* (arg) { + const fm = Components.classes["@mozilla.org/focus-manager;1"]. + getService(Components.interfaces.nsIFocusManager); + Assert.equal(fm.focusedElement, null, "focusedElement not null"); + + if (arg.isRemote) { + Assert.equal(fm.activeWindow, content, "activeWindow not correct"); + } + }); + + is(document.activeElement, browser, "content window should be focused"); + + ok(!gBrowser.contentPrincipal.equals(pagePrincipal), + "load of " + url + " by " + loadFunc.name + " should produce a page with a different principal"); + + gBrowser.removeTab(tab); +} diff --git a/browser/base/content/test/urlbar/browser_moz_action_link.js b/browser/base/content/test/urlbar/browser_moz_action_link.js new file mode 100644 index 000000000..ed2d36ee5 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_moz_action_link.js @@ -0,0 +1,31 @@ +"use strict"; + +const kURIs = [ + "moz-action:foo,", + "moz-action:foo", +]; + +add_task(function*() { + for (let uri of kURIs) { + let dataURI = `data:text/html,<a id=a href="${uri}" target=_blank>Link</a>`; + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, dataURI); + + let tabSwitchPromise = BrowserTestUtils.switchTab(gBrowser, function() {}); + yield ContentTask.spawn(tab.linkedBrowser, null, function*() { + content.document.getElementById("a").click(); + }); + yield tabSwitchPromise; + isnot(gBrowser.selectedTab, tab, "Switched to new tab!"); + is(gURLBar.value, "about:blank", "URL bar should be displaying about:blank"); + let newTab = gBrowser.selectedTab; + yield BrowserTestUtils.switchTab(gBrowser, tab); + yield BrowserTestUtils.switchTab(gBrowser, newTab); + is(gBrowser.selectedTab, newTab, "Switched to new tab again!"); + is(gURLBar.value, "about:blank", "URL bar should be displaying about:blank after tab switch"); + // Finally, check that directly setting it produces the right results, too: + URLBarSetURI(makeURI(uri)); + is(gURLBar.value, "about:blank", "URL bar should still be displaying about:blank"); + yield BrowserTestUtils.removeTab(newTab); + yield BrowserTestUtils.removeTab(tab); + } +}); diff --git a/browser/base/content/test/urlbar/browser_removeUnsafeProtocolsFromURLBarPaste.js b/browser/base/content/test/urlbar/browser_removeUnsafeProtocolsFromURLBarPaste.js new file mode 100644 index 000000000..e9ba8d989 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_removeUnsafeProtocolsFromURLBarPaste.js @@ -0,0 +1,49 @@ +function test() { + waitForExplicitFinish(); + testNext(); +} + +var pairs = [ + ["javascript:", ""], + ["javascript:1+1", "1+1"], + ["javascript:document.domain", "document.domain"], + ["data:text/html,<body>hi</body>", "data:text/html,<body>hi</body>"], + // Nested things get confusing because some things don't parse as URIs: + ["javascript:javascript:alert('hi!')", "alert('hi!')"], + ["data:data:text/html,<body>hi</body>", "data:data:text/html,<body>hi</body>"], + ["javascript:data:javascript:alert('hi!')", "data:javascript:alert('hi!')"], + ["javascript:data:text/html,javascript:alert('hi!')", "data:text/html,javascript:alert('hi!')"], + ["data:data:text/html,javascript:alert('hi!')", "data:data:text/html,javascript:alert('hi!')"], +]; + +var clipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper); + +function paste(input, cb) { + waitForClipboard(input, function() { + clipboardHelper.copyString(input); + }, function() { + document.commandDispatcher.getControllerForCommand("cmd_paste").doCommand("cmd_paste"); + cb(); + }, function() { + ok(false, "Failed to copy string '" + input + "' to clipboard"); + cb(); + }); +} + +function testNext() { + gURLBar.value = ''; + if (!pairs.length) { + finish(); + return; + } + + let [inputValue, expectedURL] = pairs.shift(); + + gURLBar.focus(); + paste(inputValue, function() { + is(gURLBar.textValue, expectedURL, "entering '" + inputValue + "' strips relevant bits."); + + setTimeout(testNext, 0); + }); +} + diff --git a/browser/base/content/test/urlbar/browser_search_favicon.js b/browser/base/content/test/urlbar/browser_search_favicon.js new file mode 100644 index 000000000..a8e6dbbcd --- /dev/null +++ b/browser/base/content/test/urlbar/browser_search_favicon.js @@ -0,0 +1,52 @@ +var gOriginalEngine; +var gEngine; +var gRestyleSearchesPref = "browser.urlbar.restyleSearches"; + +registerCleanupFunction(() => { + Services.prefs.clearUserPref(gRestyleSearchesPref); + Services.search.currentEngine = gOriginalEngine; + Services.search.removeEngine(gEngine); + return PlacesTestUtils.clearHistory(); +}); + +add_task(function*() { + Services.prefs.setBoolPref(gRestyleSearchesPref, true); +}); + +add_task(function*() { + + Services.search.addEngineWithDetails("SearchEngine", "", "", "", + "GET", "http://s.example.com/search"); + gEngine = Services.search.getEngineByName("SearchEngine"); + gEngine.addParam("q", "{searchTerms}", null); + gOriginalEngine = Services.search.currentEngine; + Services.search.currentEngine = gEngine; + + let uri = NetUtil.newURI("http://s.example.com/search?q=foo&client=1"); + yield PlacesTestUtils.addVisits({ uri: uri, title: "Foo - SearchEngine Search" }); + + yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla"); + + // The first autocomplete result has the action searchengine, while + // the second result is the "search favicon" element. + yield promiseAutocompleteResultPopup("foo"); + let result = gURLBar.popup.richlistbox.children[1]; + + isnot(result, null, "Expect a search result"); + is(result.getAttribute("type"), "searchengine", "Expect correct `type` attribute"); + + let titleHbox = result._titleText.parentNode.parentNode; + ok(titleHbox.classList.contains("ac-title"), "Title hbox sanity check"); + is_element_visible(titleHbox, "Title element should be visible"); + + let urlHbox = result._urlText.parentNode.parentNode; + ok(urlHbox.classList.contains("ac-url"), "URL hbox sanity check"); + is_element_hidden(urlHbox, "URL element should be hidden"); + + let actionHbox = result._actionText.parentNode.parentNode; + ok(actionHbox.classList.contains("ac-action"), "Action hbox sanity check"); + is_element_hidden(actionHbox, "Action element should be hidden because it is not selected"); + is(result._actionText.textContent, "Search with SearchEngine", "Action text should be as expected"); + + gBrowser.removeCurrentTab(); +}); diff --git a/browser/base/content/test/urlbar/browser_tabMatchesInAwesomebar.js b/browser/base/content/test/urlbar/browser_tabMatchesInAwesomebar.js new file mode 100644 index 000000000..d207092d4 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_tabMatchesInAwesomebar.js @@ -0,0 +1,216 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim:set ts=2 sw=2 sts=2 et: + * 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/. */ + +requestLongerTimeout(2); + +const TEST_URL_BASES = [ + "http://example.org/browser/browser/base/content/test/urlbar/dummy_page.html#tabmatch", + "http://example.org/browser/browser/base/content/test/urlbar/moz.png#tabmatch" +]; + +var gController = Cc["@mozilla.org/autocomplete/controller;1"]. + getService(Ci.nsIAutoCompleteController); + +var gTabCounter = 0; + +add_task(function* step_1() { + info("Running step 1"); + let maxResults = Services.prefs.getIntPref("browser.urlbar.maxRichResults"); + let promises = []; + for (let i = 0; i < maxResults - 1; i++) { + let tab = gBrowser.addTab(); + promises.push(loadTab(tab, TEST_URL_BASES[0] + (++gTabCounter))); + } + + yield Promise.all(promises); + yield ensure_opentabs_match_db(); +}); + +add_task(function* step_2() { + info("Running step 2"); + gBrowser.selectTabAtIndex(1); + gBrowser.removeCurrentTab(); + gBrowser.selectTabAtIndex(1); + gBrowser.removeCurrentTab(); + + let promises = []; + for (let i = 1; i < gBrowser.tabs.length; i++) + promises.push(loadTab(gBrowser.tabs[i], TEST_URL_BASES[1] + (++gTabCounter))); + + yield Promise.all(promises); + yield ensure_opentabs_match_db(); +}); + +add_task(function* step_3() { + info("Running step 3"); + let promises = []; + for (let i = 1; i < gBrowser.tabs.length; i++) + promises.push(loadTab(gBrowser.tabs[i], TEST_URL_BASES[0] + gTabCounter)); + + yield Promise.all(promises); + yield ensure_opentabs_match_db(); +}); + +add_task(function* step_4() { + info("Running step 4 - ensure we don't register subframes as open pages"); + let tab = gBrowser.addTab(); + tab.linkedBrowser.loadURI('data:text/html,<body><iframe src=""></iframe></body>'); + yield BrowserTestUtils.browserLoaded(tab.linkedBrowser); + + yield ContentTask.spawn(tab.linkedBrowser, null, function* () { + let iframe_loaded = ContentTaskUtils.waitForEvent(content.document, "load", true); + content.document.querySelector("iframe").src = "http://test2.example.org/"; + yield iframe_loaded; + }); + + yield ensure_opentabs_match_db(); +}); + +add_task(function* step_5() { + info("Running step 5 - remove tab immediately"); + let tab = gBrowser.addTab("about:logo"); + yield BrowserTestUtils.removeTab(tab); + yield ensure_opentabs_match_db(); +}); + +add_task(function* step_6() { + info("Running step 6 - check swapBrowsersAndCloseOther preserves registered switch-to-tab result"); + let tabToKeep = gBrowser.addTab(); + let tab = gBrowser.addTab(); + tab.linkedBrowser.loadURI("about:mozilla"); + yield BrowserTestUtils.browserLoaded(tab.linkedBrowser); + + gBrowser.updateBrowserRemoteness(tabToKeep.linkedBrowser, tab.linkedBrowser.isRemoteBrowser); + gBrowser.swapBrowsersAndCloseOther(tabToKeep, tab); + + yield ensure_opentabs_match_db() + + yield BrowserTestUtils.removeTab(tabToKeep); + + yield ensure_opentabs_match_db(); +}); + +add_task(function* step_7() { + info("Running step 7 - close all tabs"); + + Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand"); + + gBrowser.addTab("about:blank", {skipAnimation: true}); + while (gBrowser.tabs.length > 1) { + info("Removing tab: " + gBrowser.tabs[0].linkedBrowser.currentURI.spec); + gBrowser.selectTabAtIndex(0); + gBrowser.removeCurrentTab(); + } + + yield ensure_opentabs_match_db(); +}); + +add_task(function* cleanup() { + info("Cleaning up"); + + yield PlacesTestUtils.clearHistory(); +}); + +function loadTab(tab, url) { + // Because adding visits is async, we will not be notified immediately. + let loaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser); + let visited = new Promise(resolve => { + Services.obs.addObserver( + function observer(aSubject, aTopic, aData) { + if (url != aSubject.QueryInterface(Ci.nsIURI).spec) + return; + Services.obs.removeObserver(observer, aTopic); + resolve(); + }, + "uri-visit-saved", + false + ); + }); + + info("Loading page: " + url); + tab.linkedBrowser.loadURI(url); + return Promise.all([ loaded, visited ]); +} + +function ensure_opentabs_match_db() { + var tabs = {}; + + var winEnum = Services.wm.getEnumerator("navigator:browser"); + while (winEnum.hasMoreElements()) { + let browserWin = winEnum.getNext(); + // skip closed-but-not-destroyed windows + if (browserWin.closed) + continue; + + for (let i = 0; i < browserWin.gBrowser.tabContainer.childElementCount; i++) { + let browser = browserWin.gBrowser.getBrowserAtIndex(i); + let url = browser.currentURI.spec; + if (browserWin.isBlankPageURL(url)) + continue; + if (!(url in tabs)) + tabs[url] = 1; + else + tabs[url]++; + } + } + + return new Promise(resolve => { + checkAutocompleteResults(tabs, resolve); + }); +} + +function checkAutocompleteResults(aExpected, aCallback) +{ + gController.input = { + timeout: 10, + textValue: "", + searches: ["unifiedcomplete"], + searchParam: "enable-actions", + popupOpen: false, + minResultsForPopup: 0, + invalidate: function() {}, + disableAutoComplete: false, + completeDefaultIndex: false, + get popup() { return this; }, + onSearchBegin: function() {}, + onSearchComplete: function () + { + info("Found " + gController.matchCount + " matches."); + // Check to see the expected uris and titles match up (in any order) + for (let i = 0; i < gController.matchCount; i++) { + if (gController.getStyleAt(i).includes("heuristic")) { + info("Skip heuristic match"); + continue; + } + let action = gURLBar.popup.input._parseActionUrl(gController.getValueAt(i)); + let uri = action.params.url; + + info("Search for '" + uri + "' in open tabs."); + let expected = uri in aExpected; + ok(expected, uri + " was found in autocomplete, was " + (expected ? "" : "not ") + "expected"); + // Remove the found entry from expected results. + delete aExpected[uri]; + } + + // Make sure there is no reported open page that is not open. + for (let entry in aExpected) { + ok(false, "'" + entry + "' should be found in autocomplete"); + } + + executeSoon(aCallback); + }, + setSelectedIndex: function() {}, + get searchCount() { return this.searches.length; }, + getSearchAt: function(aIndex) { return this.searches[aIndex]; }, + QueryInterface: XPCOMUtils.generateQI([ + Ci.nsIAutoCompleteInput, + Ci.nsIAutoCompletePopup, + ]) + }; + + info("Searching open pages."); + gController.startSearch(Services.prefs.getCharPref("browser.urlbar.restrict.openpage")); +} diff --git a/browser/base/content/test/urlbar/browser_tabMatchesInAwesomebar_perwindowpb.js b/browser/base/content/test/urlbar/browser_tabMatchesInAwesomebar_perwindowpb.js new file mode 100644 index 000000000..08a18b38a --- /dev/null +++ b/browser/base/content/test/urlbar/browser_tabMatchesInAwesomebar_perwindowpb.js @@ -0,0 +1,84 @@ +let testURL = "http://example.org/browser/browser/base/content/test/urlbar/dummy_page.html"; + +add_task(function*() { + let normalWindow = yield BrowserTestUtils.openNewBrowserWindow(); + let privateWindow = yield BrowserTestUtils.openNewBrowserWindow({private: true}); + yield runTest(normalWindow, privateWindow, false); + yield BrowserTestUtils.closeWindow(normalWindow); + yield BrowserTestUtils.closeWindow(privateWindow); + + normalWindow = yield BrowserTestUtils.openNewBrowserWindow(); + privateWindow = yield BrowserTestUtils.openNewBrowserWindow({private: true}); + yield runTest(privateWindow, normalWindow, false); + yield BrowserTestUtils.closeWindow(normalWindow); + yield BrowserTestUtils.closeWindow(privateWindow); + + privateWindow = yield BrowserTestUtils.openNewBrowserWindow({private: true}); + yield runTest(privateWindow, privateWindow, false); + yield BrowserTestUtils.closeWindow(privateWindow); + + normalWindow = yield BrowserTestUtils.openNewBrowserWindow(); + yield runTest(normalWindow, normalWindow, true); + yield BrowserTestUtils.closeWindow(normalWindow); +}); + +function* runTest(aSourceWindow, aDestWindow, aExpectSwitch, aCallback) { + yield BrowserTestUtils.openNewForegroundTab(aSourceWindow.gBrowser, testURL); + let testTab = yield BrowserTestUtils.openNewForegroundTab(aDestWindow.gBrowser); + + info("waiting for focus on the window"); + yield SimpleTest.promiseFocus(aDestWindow); + info("got focus on the window"); + + // Select the testTab + aDestWindow.gBrowser.selectedTab = testTab; + + // Ensure that this tab has no history entries + let sessionHistoryCount = yield new Promise(resolve => { + SessionStore.getSessionHistory(gBrowser.selectedTab, function(sessionHistory) { + resolve(sessionHistory.entries.length); + }); + }); + + ok(sessionHistoryCount < 2, + `The test tab has 1 or fewer history entries. sessionHistoryCount=${sessionHistoryCount}`); + // Ensure that this tab is on about:blank + is(testTab.linkedBrowser.currentURI.spec, "about:blank", + "The test tab is on about:blank"); + // Ensure that this tab's document has no child nodes + yield ContentTask.spawn(testTab.linkedBrowser, null, function*() { + ok(!content.document.body.hasChildNodes(), + "The test tab has no child nodes"); + }); + ok(!testTab.hasAttribute("busy"), + "The test tab doesn't have the busy attribute"); + + // Wait for the Awesomebar popup to appear. + yield promiseAutocompleteResultPopup(testURL, aDestWindow); + + info(`awesomebar popup appeared. aExpectSwitch: ${aExpectSwitch}`); + // Make sure the last match is selected. + let {controller, popup} = aDestWindow.gURLBar; + while (popup.selectedIndex < controller.matchCount - 1) { + info("handling key navigation for DOM_VK_DOWN key"); + controller.handleKeyNavigation(KeyEvent.DOM_VK_DOWN); + } + + let awaitTabSwitch; + if (aExpectSwitch) { + awaitTabSwitch = BrowserTestUtils.removeTab(testTab, {dontRemove: true}) + } + + // Execute the selected action. + controller.handleEnter(true); + info("sent Enter command to the controller"); + + if (aExpectSwitch) { + // If we expect a tab switch then the current tab + // will be closed and we switch to the other tab. + yield awaitTabSwitch; + } else { + // If we don't expect a tab switch then wait for the tab to load. + yield BrowserTestUtils.browserLoaded(testTab.linkedBrowser); + } +} diff --git a/browser/base/content/test/urlbar/browser_urlHighlight.js b/browser/base/content/test/urlbar/browser_urlHighlight.js new file mode 100644 index 000000000..ba1537d91 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlHighlight.js @@ -0,0 +1,134 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +function testVal(aExpected) { + gURLBar.value = aExpected.replace(/[<>]/g, ""); + + let selectionController = gURLBar.editor.selectionController; + let selection = selectionController.getSelection(selectionController.SELECTION_URLSECONDARY); + let value = gURLBar.editor.rootElement.textContent; + let result = ""; + for (let i = 0; i < selection.rangeCount; i++) { + let range = selection.getRangeAt(i).toString(); + let pos = value.indexOf(range); + result += value.substring(0, pos) + "<" + range + ">"; + value = value.substring(pos + range.length); + } + result += value; + is(result, aExpected, + "Correct part of the urlbar contents is highlighted"); +} + +function test() { + const prefname = "browser.urlbar.formatting.enabled"; + + registerCleanupFunction(function () { + Services.prefs.clearUserPref(prefname); + URLBarSetURI(); + }); + + Services.prefs.setBoolPref(prefname, true); + + gURLBar.focus(); + + testVal("https://mozilla.org"); + + gBrowser.selectedBrowser.focus(); + + testVal("<https://>mozilla.org"); + testVal("<https://>mözilla.org"); + testVal("<https://>mozilla.imaginatory"); + + testVal("<https://www.>mozilla.org"); + testVal("<https://sub.>mozilla.org"); + testVal("<https://sub1.sub2.sub3.>mozilla.org"); + testVal("<www.>mozilla.org"); + testVal("<sub.>mozilla.org"); + testVal("<sub1.sub2.sub3.>mozilla.org"); + testVal("<mozilla.com.>mozilla.com"); + testVal("<https://mozilla.com:mozilla.com@>mozilla.com"); + testVal("<mozilla.com:mozilla.com@>mozilla.com"); + + testVal("<ftp.>mozilla.org"); + testVal("<ftp://ftp.>mozilla.org"); + + testVal("<https://sub.>mozilla.org"); + testVal("<https://sub1.sub2.sub3.>mozilla.org"); + testVal("<https://user:pass@sub1.sub2.sub3.>mozilla.org"); + testVal("<https://user:pass@>mozilla.org"); + testVal("<user:pass@sub1.sub2.sub3.>mozilla.org"); + testVal("<user:pass@>mozilla.org"); + + testVal("<https://>mozilla.org< >"); + testVal("mozilla.org< >"); + + testVal("<https://>mozilla.org</file.ext>"); + testVal("<https://>mozilla.org</sub/file.ext>"); + testVal("<https://>mozilla.org</sub/file.ext?foo>"); + testVal("<https://>mozilla.org</sub/file.ext?foo&bar>"); + testVal("<https://>mozilla.org</sub/file.ext?foo&bar#top>"); + testVal("<https://>mozilla.org</sub/file.ext?foo&bar#top>"); + testVal("foo.bar<?q=test>"); + testVal("foo.bar<#mozilla.org>"); + testVal("foo.bar<?somewhere.mozilla.org>"); + testVal("foo.bar<?@mozilla.org>"); + testVal("foo.bar<#x@mozilla.org>"); + testVal("foo.bar<#@x@mozilla.org>"); + testVal("foo.bar<?x@mozilla.org>"); + testVal("foo.bar<?@x@mozilla.org>"); + testVal("<foo.bar@x@>mozilla.org"); + testVal("<foo.bar@:baz@>mozilla.org"); + testVal("<foo.bar:@baz@>mozilla.org"); + testVal("<foo.bar@:ba:z@>mozilla.org"); + testVal("<foo.:bar:@baz@>mozilla.org"); + + testVal("<https://sub.>mozilla.org<:666/file.ext>"); + testVal("<sub.>mozilla.org<:666/file.ext>"); + testVal("localhost<:666/file.ext>"); + + let IPs = ["192.168.1.1", + "[::]", + "[::1]", + "[1::]", + "[::]", + "[::1]", + "[1::]", + "[1:2:3:4:5:6:7::]", + "[::1:2:3:4:5:6:7]", + "[1:2:a:B:c:D:e:F]", + "[1::8]", + "[1:2::8]", + "[fe80::222:19ff:fe11:8c76]", + "[0000:0123:4567:89AB:CDEF:abcd:ef00:0000]", + "[::192.168.1.1]", + "[1::0.0.0.0]", + "[1:2::255.255.255.255]", + "[1:2:3::255.255.255.255]", + "[1:2:3:4::255.255.255.255]", + "[1:2:3:4:5::255.255.255.255]", + "[1:2:3:4:5:6:255.255.255.255]"]; + IPs.forEach(function (IP) { + testVal(IP); + testVal(IP + "</file.ext>"); + testVal(IP + "<:666/file.ext>"); + testVal("<https://>" + IP); + testVal("<https://>" + IP + "</file.ext>"); + testVal("<https://user:pass@>" + IP + "<:666/file.ext>"); + testVal("<user:pass@>" + IP + "<:666/file.ext>"); + }); + + testVal("mailto:admin@mozilla.org"); + testVal("gopher://mozilla.org/"); + testVal("about:config"); + testVal("jar:http://mozilla.org/example.jar!/"); + testVal("view-source:http://mozilla.org/"); + testVal("foo9://mozilla.org/"); + testVal("foo+://mozilla.org/"); + testVal("foo.://mozilla.org/"); + testVal("foo-://mozilla.org/"); + + Services.prefs.setBoolPref(prefname, false); + + testVal("https://mozilla.org"); +} diff --git a/browser/base/content/test/urlbar/browser_urlbarAboutHomeLoading.js b/browser/base/content/test/urlbar/browser_urlbarAboutHomeLoading.js new file mode 100644 index 000000000..792826eb1 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbarAboutHomeLoading.js @@ -0,0 +1,104 @@ +"use strict"; + +const {TabStateFlusher} = Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {}); + +/** + * Test what happens if loading a URL that should clear the + * location bar after a parent process URL. + */ +add_task(function* clearURLBarAfterParentProcessURL() { + let tab = yield new Promise(resolve => { + gBrowser.selectedTab = gBrowser.addTab("about:preferences"); + let newTabBrowser = gBrowser.getBrowserForTab(gBrowser.selectedTab); + newTabBrowser.addEventListener("Initialized", function onInit() { + newTabBrowser.removeEventListener("Initialized", onInit, true); + resolve(gBrowser.selectedTab); + }, true); + }); + document.getElementById("home-button").click(); + yield BrowserTestUtils.browserLoaded(tab.linkedBrowser); + is(gURLBar.value, "", "URL bar should be empty"); + is(tab.linkedBrowser.userTypedValue, null, "The browser should have no recorded userTypedValue"); + yield BrowserTestUtils.removeTab(tab); +}); + +/** + * Same as above, but open the tab without passing the URL immediately + * which changes behaviour in tabbrowser.xml. + */ +add_task(function* clearURLBarAfterParentProcessURLInExistingTab() { + let tab = yield new Promise(resolve => { + gBrowser.selectedTab = gBrowser.addTab(); + let newTabBrowser = gBrowser.getBrowserForTab(gBrowser.selectedTab); + newTabBrowser.addEventListener("Initialized", function onInit() { + newTabBrowser.removeEventListener("Initialized", onInit, true); + resolve(gBrowser.selectedTab); + }, true); + newTabBrowser.loadURI("about:preferences"); + }); + document.getElementById("home-button").click(); + yield BrowserTestUtils.browserLoaded(tab.linkedBrowser); + is(gURLBar.value, "", "URL bar should be empty"); + is(tab.linkedBrowser.userTypedValue, null, "The browser should have no recorded userTypedValue"); + yield BrowserTestUtils.removeTab(tab); +}); + +/** + * Load about:home directly from an about:newtab page. Because it is an + * 'initial' page, we need to treat this specially if the user actually + * loads a page like this from the URL bar. + */ +add_task(function* clearURLBarAfterManuallyLoadingAboutHome() { + let promiseTabOpenedAndSwitchedTo = BrowserTestUtils.switchTab(gBrowser, () => {}); + // This opens about:newtab: + BrowserOpenTab(); + let tab = yield promiseTabOpenedAndSwitchedTo; + is(gURLBar.value, "", "URL bar should be empty"); + is(tab.linkedBrowser.userTypedValue, null, "userTypedValue should be null"); + + gURLBar.value = "about:home"; + gURLBar.select(); + let aboutHomeLoaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, "about:home"); + EventUtils.sendKey("return"); + yield aboutHomeLoaded; + + is(gURLBar.value, "", "URL bar should be empty"); + is(tab.linkedBrowser.userTypedValue, null, "userTypedValue should be null"); + yield BrowserTestUtils.removeTab(tab); +}); + +/** + * Ensure we don't show 'about:home' in the URL bar temporarily in new tabs + * while we're switching remoteness (when the URL we're loading and the + * default content principal are different). + */ +add_task(function* dontTemporarilyShowAboutHome() { + yield SpecialPowers.pushPrefEnv({set: [["browser.startup.page", 1]]}); + let windowOpenedPromise = BrowserTestUtils.waitForNewWindow(); + let win = OpenBrowserWindow(); + yield windowOpenedPromise; + let promiseTabSwitch = BrowserTestUtils.switchTab(win.gBrowser, () => {}); + win.BrowserOpenTab(); + yield promiseTabSwitch; + yield TabStateFlusher.flush(win.gBrowser.selectedBrowser); + yield BrowserTestUtils.closeWindow(win); + ok(SessionStore.getClosedWindowCount(), "Should have a closed window"); + + windowOpenedPromise = BrowserTestUtils.waitForNewWindow(); + win = SessionStore.undoCloseWindow(0); + yield windowOpenedPromise; + let wpl = { + onLocationChange(wpl, request, location, flags) { + is(win.gURLBar.value, "", "URL bar value should stay empty."); + }, + }; + win.gBrowser.addProgressListener(wpl); + let otherTab = win.gBrowser.selectedTab.previousSibling; + let tabLoaded = BrowserTestUtils.browserLoaded(otherTab.linkedBrowser, false, "about:home"); + yield BrowserTestUtils.switchTab(win.gBrowser, otherTab); + yield tabLoaded; + win.gBrowser.removeProgressListener(wpl); + is(win.gURLBar.value, "", "URL bar value should be empty."); + + yield BrowserTestUtils.closeWindow(win); +}); diff --git a/browser/base/content/test/urlbar/browser_urlbarAutoFillTrimURLs.js b/browser/base/content/test/urlbar/browser_urlbarAutoFillTrimURLs.js new file mode 100644 index 000000000..8101c101d --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbarAutoFillTrimURLs.js @@ -0,0 +1,49 @@ +// This test ensures that autoFilled values are not trimmed, unless the user +// selects from the autocomplete popup. + +add_task(function* setup() { + const PREF_TRIMURL = "browser.urlbar.trimURLs"; + const PREF_AUTOFILL = "browser.urlbar.autoFill"; + + registerCleanupFunction(function* () { + Services.prefs.clearUserPref(PREF_TRIMURL); + Services.prefs.clearUserPref(PREF_AUTOFILL); + yield PlacesTestUtils.clearHistory(); + gURLBar.handleRevert(); + }); + Services.prefs.setBoolPref(PREF_TRIMURL, true); + Services.prefs.setBoolPref(PREF_AUTOFILL, true); + + // Adding a tab would hit switch-to-tab, so it's safer to just add a visit. + yield PlacesTestUtils.addVisits({ + uri: "http://www.autofilltrimurl.com/whatever", + transition: Ci.nsINavHistoryService.TRANSITION_TYPED, + }); +}); + +function* promiseSearch(searchtext) { + gURLBar.focus(); + gURLBar.inputField.value = searchtext.substr(0, searchtext.length -1); + EventUtils.synthesizeKey(searchtext.substr(-1, 1), {}); + yield promiseSearchComplete(); +} + +add_task(function* () { + yield promiseSearch("http://"); + is(gURLBar.inputField.value, "http://", "Autofilled value is as expected"); +}); + +add_task(function* () { + yield promiseSearch("http://au"); + is(gURLBar.inputField.value, "http://autofilltrimurl.com/", "Autofilled value is as expected"); +}); + +add_task(function* () { + yield promiseSearch("http://www.autofilltrimurl.com"); + is(gURLBar.inputField.value, "http://www.autofilltrimurl.com/", "Autofilled value is as expected"); + + // Now ensure selecting from the popup correctly trims. + is(gURLBar.controller.matchCount, 2, "Found the expected number of matches"); + EventUtils.synthesizeKey("VK_DOWN", {}); + is(gURLBar.inputField.value, "www.autofilltrimurl.com/whatever", "trim was applied correctly"); +}); diff --git a/browser/base/content/test/urlbar/browser_urlbarCopying.js b/browser/base/content/test/urlbar/browser_urlbarCopying.js new file mode 100644 index 000000000..8d5562b61 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbarCopying.js @@ -0,0 +1,232 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const trimPref = "browser.urlbar.trimURLs"; +const phishyUserPassPref = "network.http.phishy-userpass-length"; + +function toUnicode(input) { + let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"] + .createInstance(Ci.nsIScriptableUnicodeConverter); + converter.charset = "UTF-8"; + + return converter.ConvertToUnicode(input); +} + +function test() { + + let tab = gBrowser.selectedTab = gBrowser.addTab(); + + registerCleanupFunction(function () { + gBrowser.removeTab(tab); + Services.prefs.clearUserPref(trimPref); + Services.prefs.clearUserPref(phishyUserPassPref); + URLBarSetURI(); + }); + + Services.prefs.setBoolPref(trimPref, true); + Services.prefs.setIntPref(phishyUserPassPref, 32); // avoid prompting about phishing + + waitForExplicitFinish(); + + nextTest(); +} + +var tests = [ + // pageproxystate="invalid" + { + setURL: "http://example.com/", + expectedURL: "example.com", + copyExpected: "example.com" + }, + { + copyVal: "<e>xample.com", + copyExpected: "e" + }, + + // pageproxystate="valid" from this point on (due to the load) + { + loadURL: "http://example.com/", + expectedURL: "example.com", + copyExpected: "http://example.com/" + }, + { + copyVal: "<example.co>m", + copyExpected: "example.co" + }, + { + copyVal: "e<x>ample.com", + copyExpected: "x" + }, + { + copyVal: "<e>xample.com", + copyExpected: "e" + }, + + { + loadURL: "http://example.com/foo", + expectedURL: "example.com/foo", + copyExpected: "http://example.com/foo" + }, + { + copyVal: "<example.com>/foo", + copyExpected: "http://example.com" + }, + { + copyVal: "<example>.com/foo", + copyExpected: "example" + }, + + // Test that userPass is stripped out + { + loadURL: "http://user:pass@mochi.test:8888/browser/browser/base/content/test/urlbar/authenticate.sjs?user=user&pass=pass", + expectedURL: "mochi.test:8888/browser/browser/base/content/test/urlbar/authenticate.sjs?user=user&pass=pass", + copyExpected: "http://mochi.test:8888/browser/browser/base/content/test/urlbar/authenticate.sjs?user=user&pass=pass" + }, + + // Test escaping + { + loadURL: "http://example.com/()%28%29%C3%A9", + expectedURL: "example.com/()()\xe9", + copyExpected: "http://example.com/()%28%29%C3%A9" + }, + { + copyVal: "<example.com/(>)()\xe9", + copyExpected: "http://example.com/(" + }, + { + copyVal: "e<xample.com/(>)()\xe9", + copyExpected: "xample.com/(" + }, + + { + loadURL: "http://example.com/%C3%A9%C3%A9", + expectedURL: "example.com/\xe9\xe9", + copyExpected: "http://example.com/%C3%A9%C3%A9" + }, + { + copyVal: "e<xample.com/\xe9>\xe9", + copyExpected: "xample.com/\xe9" + }, + { + copyVal: "<example.com/\xe9>\xe9", + copyExpected: "http://example.com/\xe9" + }, + + { + loadURL: "http://example.com/?%C3%B7%C3%B7", + expectedURL: "example.com/?\xf7\xf7", + copyExpected: "http://example.com/?%C3%B7%C3%B7" + }, + { + copyVal: "e<xample.com/?\xf7>\xf7", + copyExpected: "xample.com/?\xf7" + }, + { + copyVal: "<example.com/?\xf7>\xf7", + copyExpected: "http://example.com/?\xf7" + }, + { + loadURL: "http://example.com/a%20test", + expectedURL: "example.com/a test", + copyExpected: "http://example.com/a%20test" + }, + { + loadURL: "http://example.com/a%E3%80%80test", + expectedURL: toUnicode("example.com/a test"), + copyExpected: "http://example.com/a%E3%80%80test" + }, + { + loadURL: "http://example.com/a%20%C2%A0test", + expectedURL: "example.com/a%20%C2%A0test", + copyExpected: "http://example.com/a%20%C2%A0test" + }, + { + loadURL: "http://example.com/%20%20%20", + expectedURL: "example.com/%20%20%20", + copyExpected: "http://example.com/%20%20%20" + }, + { + loadURL: "http://example.com/%E3%80%80%E3%80%80", + expectedURL: "example.com/%E3%80%80%E3%80%80", + copyExpected: "http://example.com/%E3%80%80%E3%80%80" + }, + + // data: and javsacript: URIs shouldn't be encoded + { + loadURL: "javascript:('%C3%A9%20%25%50')", + expectedURL: "javascript:('%C3%A9 %25P')", + copyExpected: "javascript:('%C3%A9 %25P')" + }, + { + copyVal: "<javascript:(>'%C3%A9 %25P')", + copyExpected: "javascript:(" + }, + + { + loadURL: "data:text/html,(%C3%A9%20%25%50)", + expectedURL: "data:text/html,(%C3%A9 %25P)", + copyExpected: "data:text/html,(%C3%A9 %25P)", + }, + { + copyVal: "<data:text/html,(>%C3%A9 %25P)", + copyExpected: "data:text/html,(" + }, + { + copyVal: "<data:text/html,(%C3%A9 %25P>)", + copyExpected: "data:text/html,(%C3%A9 %25P", + } +]; + +function nextTest() { + let test = tests.shift(); + if (tests.length == 0) + runTest(test, finish); + else + runTest(test, nextTest); +} + +function runTest(test, cb) { + function doCheck() { + if (test.setURL || test.loadURL) { + gURLBar.valueIsTyped = !!test.setURL; + is(gURLBar.textValue, test.expectedURL, "url bar value set"); + } + + testCopy(test.copyVal, test.copyExpected, cb); + } + + if (test.loadURL) { + loadURL(test.loadURL, doCheck); + } else { + if (test.setURL) + gURLBar.value = test.setURL; + doCheck(); + } +} + +function testCopy(copyVal, targetValue, cb) { + info("Expecting copy of: " + targetValue); + waitForClipboard(targetValue, function () { + gURLBar.focus(); + if (copyVal) { + let startBracket = copyVal.indexOf("<"); + let endBracket = copyVal.indexOf(">"); + if (startBracket == -1 || endBracket == -1 || + startBracket > endBracket || + copyVal.replace("<", "").replace(">", "") != gURLBar.textValue) { + ok(false, "invalid copyVal: " + copyVal); + } + gURLBar.selectionStart = startBracket; + gURLBar.selectionEnd = endBracket - 1; + } else { + gURLBar.select(); + } + + goDoCommand("cmd_copy"); + }, cb, cb); +} + +function loadURL(aURL, aCB) { + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, aURL); + BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, aURL).then(aCB); +} diff --git a/browser/base/content/test/urlbar/browser_urlbarDecode.js b/browser/base/content/test/urlbar/browser_urlbarDecode.js new file mode 100644 index 000000000..6a2c421ef --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbarDecode.js @@ -0,0 +1,97 @@ +"use strict"; + +// This test makes sure (1) you can't break the urlbar by typing particular JSON +// or JS fragments into it, (2) urlbar.textValue shows URLs unescaped, and (3) +// the urlbar also shows the URLs embedded in action URIs unescaped. See bug +// 1233672. + +add_task(function* injectJSON() { + let inputStrs = [ + 'http://example.com/ ", "url": "bar', + 'http://example.com/\\', + 'http://example.com/"', + 'http://example.com/","url":"evil.com', + 'http://mozilla.org/\\u0020', + 'http://www.mozilla.org/","url":1e6,"some-key":"foo', + 'http://www.mozilla.org/","url":null,"some-key":"foo', + 'http://www.mozilla.org/","url":["foo","bar"],"some-key":"foo', + ]; + for (let inputStr of inputStrs) { + yield checkInput(inputStr); + } + gURLBar.value = ""; + gURLBar.handleRevert(); + gURLBar.blur(); +}); + +add_task(function losslessDecode() { + let urlNoScheme = "example.com/\u30a2\u30a4\u30a6\u30a8\u30aa"; + let url = "http://" + urlNoScheme; + gURLBar.textValue = url; + // Since this is directly setting textValue, it is expected to be trimmed. + Assert.equal(gURLBar.inputField.value, urlNoScheme, + "The string displayed in the textbox should not be escaped"); + gURLBar.value = ""; + gURLBar.handleRevert(); + gURLBar.blur(); +}); + +add_task(function* actionURILosslessDecode() { + let urlNoScheme = "example.com/\u30a2\u30a4\u30a6\u30a8\u30aa"; + let url = "http://" + urlNoScheme; + yield promiseAutocompleteResultPopup(url); + + // At this point the heuristic result is selected but the urlbar's value is + // simply `url`. Key down and back around until the heuristic result is + // selected again, and at that point the urlbar's value should be a visiturl + // moz-action. + + do { + gURLBar.controller.handleKeyNavigation(KeyEvent.DOM_VK_DOWN); + } while (gURLBar.popup.selectedIndex != 0); + + let [, type, ] = gURLBar.value.match(/^moz-action:([^,]+),(.*)$/); + Assert.equal(type, "visiturl", + "visiturl action URI should be in the urlbar"); + + Assert.equal(gURLBar.inputField.value, urlNoScheme, + "The string displayed in the textbox should not be escaped"); + + gURLBar.value = ""; + gURLBar.handleRevert(); + gURLBar.blur(); +}); + +function* checkInput(inputStr) { + yield promiseAutocompleteResultPopup(inputStr); + + let item = gURLBar.popup.richlistbox.firstChild; + Assert.ok(item, "Should have a result"); + + // visiturl matches have their param.urls fixed up. + let fixupInfo = Services.uriFixup.getFixupURIInfo(inputStr, + Ci.nsIURIFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS | + Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP + ); + let expectedVisitURL = fixupInfo.fixedURI.spec; + + let type = "visiturl"; + let params = { + url: expectedVisitURL, + input: inputStr, + }; + for (let key in params) { + params[key] = encodeURIComponent(params[key]); + } + let expectedURL = "moz-action:" + type + "," + JSON.stringify(params); + Assert.equal(item.getAttribute("url"), expectedURL, "url"); + + Assert.equal(item.getAttribute("title"), inputStr.replace("\\", "/"), "title"); + Assert.equal(item.getAttribute("text"), inputStr, "text"); + + let itemType = item.getAttribute("type"); + Assert.equal(itemType, "visiturl"); + + Assert.equal(item._titleText.textContent, inputStr.replace("\\", "/"), "Visible title"); + Assert.equal(item._actionText.textContent, "Visit", "Visible action"); +} diff --git a/browser/base/content/test/urlbar/browser_urlbarDelete.js b/browser/base/content/test/urlbar/browser_urlbarDelete.js new file mode 100644 index 000000000..d4eb6c856 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbarDelete.js @@ -0,0 +1,39 @@ +add_task(function*() { + let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: "http://bug1105244.example.com/", + title: "test" }); + + registerCleanupFunction(function* () { + yield PlacesUtils.bookmarks.remove(bm); + }); + + yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank" }, testDelete); +}); + +function sendHome() { + // unclear why VK_HOME doesn't work on Mac, but it doesn't... + if (Services.appinfo.OS == "Darwin") { + EventUtils.synthesizeKey("VK_LEFT", { altKey: true }); + } else { + EventUtils.synthesizeKey("VK_HOME", {}); + } +} + +function sendDelete() { + EventUtils.synthesizeKey("VK_DELETE", {}); +} + +function* testDelete() { + yield promiseAutocompleteResultPopup("bug1105244"); + + // move to the start. + sendHome(); + // delete the first few chars - each delete should operate on the input field. + sendDelete(); + Assert.equal(gURLBar.inputField.value, "ug1105244"); + + yield promisePopupShown(gURLBar.popup); + + sendDelete(); + Assert.equal(gURLBar.inputField.value, "g1105244"); +} diff --git a/browser/base/content/test/urlbar/browser_urlbarEnter.js b/browser/base/content/test/urlbar/browser_urlbarEnter.js new file mode 100644 index 000000000..32cbaf2be --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbarEnter.js @@ -0,0 +1,45 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_VALUE = "example.com/\xF7?\xF7"; +const START_VALUE = "example.com/%C3%B7?%C3%B7"; + +add_task(function* () { + info("Simple return keypress"); + let tab = gBrowser.selectedTab = gBrowser.addTab(START_VALUE); + + gURLBar.focus(); + EventUtils.synthesizeKey("VK_RETURN", {}); + yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + + // Check url bar and selected tab. + is(gURLBar.textValue, TEST_VALUE, "Urlbar should preserve the value on return keypress"); + is(gBrowser.selectedTab, tab, "New URL was loaded in the current tab"); + + // Cleanup. + yield BrowserTestUtils.removeTab(gBrowser.selectedTab); +}); + +add_task(function* () { + info("Alt+Return keypress"); + // due to bug 691608, we must wait for the load event, else isTabEmpty() will + // return true on e10s for this tab, so it will be reused even with altKey. + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, START_VALUE); + + let tabOpenPromise = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "TabOpen"); + gURLBar.focus(); + EventUtils.synthesizeKey("VK_RETURN", {altKey: true}); + + // wait for the new tab to appear. + yield tabOpenPromise; + + // Check url bar and selected tab. + is(gURLBar.textValue, TEST_VALUE, "Urlbar should preserve the value on return keypress"); + isnot(gBrowser.selectedTab, tab, "New URL was loaded in a new tab"); + + // Cleanup. + yield BrowserTestUtils.removeTab(tab); + yield BrowserTestUtils.removeTab(gBrowser.selectedTab); +}); diff --git a/browser/base/content/test/urlbar/browser_urlbarEnterAfterMouseOver.js b/browser/base/content/test/urlbar/browser_urlbarEnterAfterMouseOver.js new file mode 100644 index 000000000..22e336f91 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbarEnterAfterMouseOver.js @@ -0,0 +1,69 @@ +function repeat(limit, func) { + for (let i = 0; i < limit; i++) { + func(i); + } +} + +function* promiseAutoComplete(inputText) { + gURLBar.focus(); + gURLBar.value = inputText.slice(0, -1); + EventUtils.synthesizeKey(inputText.slice(-1), {}); + yield promiseSearchComplete(); +} + +function is_selected(index) { + is(gURLBar.popup.richlistbox.selectedIndex, index, `Item ${index + 1} should be selected`); +} + +let gMaxResults; + +add_task(function*() { + registerCleanupFunction(function* () { + yield PlacesTestUtils.clearHistory(); + }); + + yield PlacesTestUtils.clearHistory(); + + gMaxResults = Services.prefs.getIntPref("browser.urlbar.maxRichResults"); + + let visits = []; + repeat(gMaxResults, i => { + visits.push({ + uri: makeURI("http://example.com/autocomplete/?" + i), + }); + }); + yield PlacesTestUtils.addVisits(visits); + + gBrowser.selectedTab = gBrowser.addTab("about:blank"); + yield promiseAutoComplete("http://example.com/autocomplete/"); + + let popup = gURLBar.popup; + let results = popup.richlistbox.children; + is(results.length, gMaxResults, + "Should get gMaxResults=" + gMaxResults + " results"); + + let initiallySelected = gURLBar.popup.richlistbox.selectedIndex; + + info("Key Down to select the next item"); + EventUtils.synthesizeKey("VK_DOWN", {}); + is_selected(initiallySelected + 1); + let expectedURL = gURLBar.controller.getFinalCompleteValueAt(initiallySelected + 1); + + is(gURLBar.value, gURLBar.controller.getValueAt(initiallySelected + 1), + "Value in the URL bar should be updated by keyboard selection"); + + // Verify that what we're about to do changes the selectedIndex: + isnot(initiallySelected + 1, 3, "Shouldn't be changing the selectedIndex to the same index we keyboard-selected."); + + // Would love to use a synthetic mousemove event here, but that doesn't seem to do anything. + // EventUtils.synthesizeMouseAtCenter(results[3], {type: "mousemove"}); + gURLBar.popup.richlistbox.selectedIndex = 3; + is_selected(3); + + let autocompletePopupHidden = promisePopupHidden(gURLBar.popup); + let openedExpectedPage = waitForDocLoadAndStopIt(expectedURL); + EventUtils.synthesizeKey("VK_RETURN", {}); + yield Promise.all([autocompletePopupHidden, openedExpectedPage]); + + gBrowser.removeCurrentTab(); +}); diff --git a/browser/base/content/test/urlbar/browser_urlbarFocusedCmdK.js b/browser/base/content/test/urlbar/browser_urlbarFocusedCmdK.js new file mode 100644 index 000000000..8c9e2c9f2 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbarFocusedCmdK.js @@ -0,0 +1,17 @@ +/* Any copyright is dedicated to the Public Domain. +* http://creativecommons.org/publicdomain/zero/1.0/ */ + +add_task(function*() { + // Remove the search bar from toolbar + CustomizableUI.removeWidgetFromArea("search-container"); + + // Test that Ctrl/Cmd + K will focus the url bar + let focusPromise = BrowserTestUtils.waitForEvent(gURLBar, "focus"); + EventUtils.synthesizeKey("k", { accelKey: true }); + yield focusPromise; + Assert.equal(document.activeElement, gURLBar.inputField, "URL Bar should be focused"); + + // Reset changes made to toolbar + CustomizableUI.reset(); +}); + 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 000000000..152106dad --- /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 000000000..9c8996059 --- /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_urlbarOneOffs.js b/browser/base/content/test/urlbar/browser_urlbarOneOffs.js new file mode 100644 index 000000000..1f58b8edd --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbarOneOffs.js @@ -0,0 +1,232 @@ +const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml"; + +let gMaxResults; + +add_task(function* init() { + Services.prefs.setBoolPref("browser.urlbar.oneOffSearches", true); + gMaxResults = Services.prefs.getIntPref("browser.urlbar.maxRichResults"); + + // Add a search suggestion engine and move it to the front so that it appears + // as the first one-off. + let engine = yield promiseNewSearchEngine(TEST_ENGINE_BASENAME); + Services.search.moveEngine(engine, 0); + + registerCleanupFunction(function* () { + yield hidePopup(); + yield PlacesTestUtils.clearHistory(); + }); + + yield PlacesTestUtils.clearHistory(); + + let visits = []; + for (let i = 0; i < gMaxResults; i++) { + visits.push({ + uri: makeURI("http://example.com/browser_urlbarOneOffs.js/?" + i), + // TYPED so that the visit shows up when the urlbar's drop-down arrow is + // pressed. + transition: Ci.nsINavHistoryService.TRANSITION_TYPED, + }); + } + yield PlacesTestUtils.addVisits(visits); +}); + +// Keys up and down through the history panel, i.e., the panel that's shown when +// there's no text in the textbox. +add_task(function* history() { + gURLBar.focus(); + EventUtils.synthesizeKey("VK_DOWN", {}) + yield promisePopupShown(gURLBar.popup); + + assertState(-1, -1, ""); + + // Key down through each result. + for (let i = 0; i < gMaxResults; i++) { + EventUtils.synthesizeKey("VK_DOWN", {}) + assertState(i, -1, + "example.com/browser_urlbarOneOffs.js/?" + (gMaxResults - i - 1)); + } + + // Key down through each one-off. + let numButtons = + gURLBar.popup.oneOffSearchButtons.getSelectableButtons(true).length; + for (let i = 0; i < numButtons; i++) { + EventUtils.synthesizeKey("VK_DOWN", {}) + assertState(-1, i, ""); + } + + // Key down once more. Nothing should be selected. + EventUtils.synthesizeKey("VK_DOWN", {}) + assertState(-1, -1, ""); + + // Once more. The first result should be selected. + EventUtils.synthesizeKey("VK_DOWN", {}) + assertState(0, -1, + "example.com/browser_urlbarOneOffs.js/?" + (gMaxResults - 1)); + + // Now key up. Nothing should be selected again. + EventUtils.synthesizeKey("VK_UP", {}) + assertState(-1, -1, ""); + + // Key up through each one-off. + for (let i = numButtons - 1; i >= 0; i--) { + EventUtils.synthesizeKey("VK_UP", {}) + assertState(-1, i, ""); + } + + // Key up through each result. + for (let i = gMaxResults - 1; i >= 0; i--) { + EventUtils.synthesizeKey("VK_UP", {}) + assertState(i, -1, + "example.com/browser_urlbarOneOffs.js/?" + (gMaxResults - i - 1)); + } + + // Key up once more. Nothing should be selected. + EventUtils.synthesizeKey("VK_UP", {}) + assertState(-1, -1, ""); + + yield hidePopup(); +}); + +// Keys up and down through the non-history panel, i.e., the panel that's shown +// when you type something in the textbox. +add_task(function* typedValue() { + // Use a typed value that returns the visits added above but that doesn't + // trigger autofill since that would complicate the test. + let typedValue = "browser_urlbarOneOffs"; + yield promiseAutocompleteResultPopup(typedValue, window, true); + + assertState(0, -1, typedValue); + + // Key down through each result. The first result is already selected, which + // is why gMaxResults - 1 is the correct number of times to do this. + for (let i = 0; i < gMaxResults - 1; i++) { + EventUtils.synthesizeKey("VK_DOWN", {}) + // i starts at zero so that the textValue passed to assertState is correct. + // But that means that i + 1 is the expected selected index, since initially + // (when this loop starts) the first result is selected. + assertState(i + 1, -1, + "example.com/browser_urlbarOneOffs.js/?" + (gMaxResults - i - 1)); + } + + // Key down through each one-off. + let numButtons = + gURLBar.popup.oneOffSearchButtons.getSelectableButtons(true).length; + for (let i = 0; i < numButtons; i++) { + EventUtils.synthesizeKey("VK_DOWN", {}) + assertState(-1, i, typedValue); + } + + // Key down once more. The selection should wrap around to the first result. + EventUtils.synthesizeKey("VK_DOWN", {}) + assertState(0, -1, typedValue); + + // Now key up. The selection should wrap back around to the one-offs. Key + // up through all the one-offs. + for (let i = numButtons - 1; i >= 0; i--) { + EventUtils.synthesizeKey("VK_UP", {}) + assertState(-1, i, typedValue); + } + + // Key up through each non-heuristic result. + for (let i = gMaxResults - 2; i >= 0; i--) { + EventUtils.synthesizeKey("VK_UP", {}) + assertState(i + 1, -1, + "example.com/browser_urlbarOneOffs.js/?" + (gMaxResults - i - 1)); + } + + // Key up once more. The heuristic result should be selected. + EventUtils.synthesizeKey("VK_UP", {}) + assertState(0, -1, typedValue); + + yield hidePopup(); +}); + +// Checks that "Search with Current Search Engine" items are updated to "Search +// with One-Off Engine" when a one-off is selected. +add_task(function* searchWith() { + let typedValue = "foo"; + yield promiseAutocompleteResultPopup(typedValue); + + assertState(0, -1, typedValue); + + let item = gURLBar.popup.richlistbox.firstChild; + Assert.equal(item._actionText.textContent, + "Search with " + Services.search.currentEngine.name, + "Sanity check: first result's action text"); + + // Alt+Down to the first one-off. Now the first result and the first one-off + // should both be selected. + EventUtils.synthesizeKey("VK_DOWN", { altKey: true }) + assertState(0, 0, typedValue); + + let engineName = gURLBar.popup.oneOffSearchButtons.selectedButton.engine.name; + Assert.notEqual(engineName, Services.search.currentEngine.name, + "Sanity check: First one-off engine should not be " + + "the current engine"); + Assert.equal(item._actionText.textContent, + "Search with " + engineName, + "First result's action text should be updated"); + + yield hidePopup(); +}); + +// Clicks a one-off. +add_task(function* oneOffClick() { + gBrowser.selectedTab = gBrowser.addTab(); + + // We are explicitly using something that looks like a url, to make the test + // stricter. Even if it looks like a url, we should search. + let typedValue = "foo.bar"; + yield promiseAutocompleteResultPopup(typedValue); + + assertState(0, -1, typedValue); + + let oneOffs = gURLBar.popup.oneOffSearchButtons.getSelectableButtons(true); + let resultsPromise = + BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, + "http://mochi.test:8888/"); + EventUtils.synthesizeMouseAtCenter(oneOffs[0], {}); + yield resultsPromise; + + gBrowser.removeTab(gBrowser.selectedTab); +}); + +// Presses the Return key when a one-off is selected. +add_task(function* oneOffReturn() { + gBrowser.selectedTab = gBrowser.addTab(); + + // We are explicitly using something that looks like a url, to make the test + // stricter. Even if it looks like a url, we should search. + let typedValue = "foo.bar"; + yield promiseAutocompleteResultPopup(typedValue, window, true); + + assertState(0, -1, typedValue); + + // Alt+Down to select the first one-off. + EventUtils.synthesizeKey("VK_DOWN", { altKey: true }) + assertState(0, 0, typedValue); + + let resultsPromise = + BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, + "http://mochi.test:8888/"); + EventUtils.synthesizeKey("VK_RETURN", {}) + yield resultsPromise; + + gBrowser.removeTab(gBrowser.selectedTab); +}); + + +function assertState(result, oneOff, textValue = undefined) { + Assert.equal(gURLBar.popup.selectedIndex, result, + "Expected result should be selected"); + Assert.equal(gURLBar.popup.oneOffSearchButtons.selectedButtonIndex, oneOff, + "Expected one-off should be selected"); + if (textValue !== undefined) { + Assert.equal(gURLBar.textValue, textValue, "Expected textValue"); + } +} + +function* hidePopup() { + EventUtils.synthesizeKey("VK_ESCAPE", {}); + yield promisePopupHidden(gURLBar.popup); +} diff --git a/browser/base/content/test/urlbar/browser_urlbarPrivateBrowsingWindowChange.js b/browser/base/content/test/urlbar/browser_urlbarPrivateBrowsingWindowChange.js new file mode 100644 index 000000000..5db0f0ea6 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbarPrivateBrowsingWindowChange.js @@ -0,0 +1,41 @@ +"use strict"; + +/** + * Test that when opening a private browsing window and typing in it before about:privatebrowsing + * loads, we don't clear the URL bar. + */ +add_task(function*() { + let urlbarTestValue = "Mary had a little lamb"; + let win = OpenBrowserWindow({private: true}); + yield BrowserTestUtils.waitForEvent(win, "load"); + let urlbar = win.document.getElementById("urlbar"); + urlbar.value = urlbarTestValue; + // Need this so the autocomplete controller attaches: + let focusEv = new FocusEvent("focus", {}); + urlbar.dispatchEvent(focusEv); + // And so we know input happened: + let inputEv = new InputEvent("input", {data: "", view: win, bubbles: true}); + urlbar.onInput(inputEv); + // Check it worked: + is(urlbar.value, urlbarTestValue, "URL bar value should be there"); + is(win.gBrowser.selectedBrowser.userTypedValue, urlbarTestValue, "browser object should know the url bar value"); + + let continueTest; + let continuePromise = new Promise(resolve => continueTest = resolve); + let wpl = { + onLocationChange(aWebProgress, aRequest, aLocation) { + if (aLocation && aLocation.spec == "about:privatebrowsing") { + continueTest(); + } + }, + }; + win.gBrowser.addProgressListener(wpl); + + yield continuePromise; + is(urlbar.value, urlbarTestValue, + "URL bar value should be the same once about:privatebrowsing has loaded"); + is(win.gBrowser.selectedBrowser.userTypedValue, urlbarTestValue, + "browser object should still know url bar value once about:privatebrowsing has loaded"); + win.gBrowser.removeProgressListener(wpl); + yield BrowserTestUtils.closeWindow(win); +}); diff --git a/browser/base/content/test/urlbar/browser_urlbarRaceWithTabs.js b/browser/base/content/test/urlbar/browser_urlbarRaceWithTabs.js new file mode 100644 index 000000000..d66514c5a --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbarRaceWithTabs.js @@ -0,0 +1,57 @@ +const kURL = "http://example.org/browser/browser/base/content/test/urlbar/dummy_page.html"; + +function* addBookmark(bookmark) { + if (bookmark.keyword) { + yield PlacesUtils.keywords.insert({ + keyword: bookmark.keyword, + url: bookmark.url, + }); + } + + let bm = yield PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: bookmark.url, + title: bookmark.title, + }); + + registerCleanupFunction(function* () { + yield PlacesUtils.bookmarks.remove(bm); + if (bookmark.keyword) { + yield PlacesUtils.keywords.remove(bookmark.keyword); + } + }); +} + +/** + * Check that if the user hits enter and ctrl-t at the same time, we open the URL in the right tab. + */ +add_task(function* hitEnterLoadInRightTab() { + info("Opening new tab"); + let oldTabCreatedPromise = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "TabOpen"); + BrowserOpenTab(); + let oldTab = (yield oldTabCreatedPromise).target; + let oldTabLoadedPromise = BrowserTestUtils.browserLoaded(oldTab.linkedBrowser, false, kURL); + oldTabLoadedPromise.then(() => info("Old tab loaded")); + let newTabCreatedPromise = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "TabOpen"); + + info("Creating bookmark and keyword"); + yield addBookmark({title: "Test for keyword bookmark and URL", url: kURL, keyword: "urlbarkeyword"}); + info("Filling URL bar, sending <return> and opening a tab"); + gURLBar.value = "urlbarkeyword"; + gURLBar.select(); + EventUtils.sendKey("return"); + BrowserOpenTab(); + info("Waiting for new tab"); + let newTab = (yield newTabCreatedPromise).target; + info("Created new tab; waiting for either tab to load"); + let newTabLoadedPromise = BrowserTestUtils.browserLoaded(newTab.linkedBrowser, false, kURL); + newTabLoadedPromise.then(() => info("New tab loaded")); + yield Promise.race([newTabLoadedPromise, oldTabLoadedPromise]); + is(newTab.linkedBrowser.currentURI.spec, "about:newtab", "New tab still has about:newtab"); + is(oldTab.linkedBrowser.currentURI.spec, kURL, "Old tab loaded URL"); + info("Closing new tab"); + yield BrowserTestUtils.removeTab(newTab); + info("Closing old tab"); + yield BrowserTestUtils.removeTab(oldTab); + info("Finished"); +}); diff --git a/browser/base/content/test/urlbar/browser_urlbarRevert.js b/browser/base/content/test/urlbar/browser_urlbarRevert.js new file mode 100644 index 000000000..0ce3c8fac --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbarRevert.js @@ -0,0 +1,37 @@ +var tab = null; + +function test() { + waitForExplicitFinish(); + + let pageLoaded = { + onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) { + if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP && + aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) { + gBrowser.removeProgressListener(this); + executeSoon(checkURLBarRevert); + } + } + } + + gBrowser.addProgressListener(pageLoaded); + tab = gBrowser.addTab("http://example.com"); + gBrowser.selectedTab = tab; +} + +function checkURLBarRevert() { + let originalValue = gURLBar.value; + + gBrowser.userTypedValue = "foobar"; + gBrowser.selectedTab = gBrowser.tabs[0]; + gBrowser.selectedTab = tab; + is(gURLBar.value, "foobar", "location bar displays typed value"); + + gURLBar.focus(); + + EventUtils.synthesizeKey("VK_ESCAPE", {}); + + is(gURLBar.value, originalValue, "ESC reverted the location bar value"); + + gBrowser.removeTab(tab); + finish(); +} diff --git a/browser/base/content/test/urlbar/browser_urlbarSearchSingleWordNotification.js b/browser/base/content/test/urlbar/browser_urlbarSearchSingleWordNotification.js new file mode 100644 index 000000000..ee0342055 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbarSearchSingleWordNotification.js @@ -0,0 +1,198 @@ +"use strict"; + +var notificationObserver; +registerCleanupFunction(function() { + Services.prefs.clearUserPref("browser.fixup.domainwhitelist.localhost"); + if (notificationObserver) { + notificationObserver.disconnect(); + } +}); + +function promiseNotification(aBrowser, value, expected, input) { + let deferred = Promise.defer(); + let notificationBox = aBrowser.getNotificationBox(aBrowser.selectedBrowser); + if (expected) { + info("Waiting for " + value + " notification"); + let checkForNotification = function() { + if (notificationBox.getNotificationWithValue(value)) { + info("Saw the notification"); + notificationObserver.disconnect(); + notificationObserver = null; + deferred.resolve(); + } + } + if (notificationObserver) { + notificationObserver.disconnect(); + } + notificationObserver = new MutationObserver(checkForNotification); + notificationObserver.observe(notificationBox, {childList: true}); + } else { + setTimeout(() => { + is(notificationBox.getNotificationWithValue(value), null, + `We are expecting to not get a notification for ${input}`); + deferred.resolve(); + }, 1000); + } + return deferred.promise; +} + +function* runURLBarSearchTest({valueToOpen, expectSearch, expectNotification, aWindow=window}) { + aWindow.gURLBar.value = valueToOpen; + let expectedURI; + if (!expectSearch) { + expectedURI = "http://" + valueToOpen + "/"; + } else { + yield new Promise(resolve => { + Services.search.init(resolve); + }); + expectedURI = Services.search.defaultEngine.getSubmission(valueToOpen, null, "keyword").uri.spec; + } + aWindow.gURLBar.focus(); + let docLoadPromise = waitForDocLoadAndStopIt(expectedURI, aWindow.gBrowser.selectedBrowser); + EventUtils.synthesizeKey("VK_RETURN", {}, aWindow); + + yield Promise.all([ + docLoadPromise, + promiseNotification(aWindow.gBrowser, "keyword-uri-fixup", expectNotification, valueToOpen) + ]); +} + +add_task(function* test_navigate_full_domain() { + let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank"); + yield BrowserTestUtils.browserLoaded(tab.linkedBrowser); + yield* runURLBarSearchTest({ + valueToOpen: "www.mozilla.org", + expectSearch: false, + expectNotification: false, + }); + gBrowser.removeTab(tab); +}); + +add_task(function* test_navigate_decimal_ip() { + let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank"); + yield BrowserTestUtils.browserLoaded(tab.linkedBrowser); + yield* runURLBarSearchTest({ + valueToOpen: "1234", + expectSearch: true, + expectNotification: false, + }); + gBrowser.removeTab(tab); +}); + +add_task(function* test_navigate_decimal_ip_with_path() { + let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank"); + yield BrowserTestUtils.browserLoaded(tab.linkedBrowser); + yield* runURLBarSearchTest({ + valueToOpen: "1234/12", + expectSearch: true, + expectNotification: false, + }); + gBrowser.removeTab(tab); +}); + +add_task(function* test_navigate_large_number() { + let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank"); + yield BrowserTestUtils.browserLoaded(tab.linkedBrowser); + yield* runURLBarSearchTest({ + valueToOpen: "123456789012345", + expectSearch: true, + expectNotification: false + }); + gBrowser.removeTab(tab); +}); + +add_task(function* test_navigate_small_hex_number() { + let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank"); + yield BrowserTestUtils.browserLoaded(tab.linkedBrowser); + yield* runURLBarSearchTest({ + valueToOpen: "0x1f00ffff", + expectSearch: true, + expectNotification: false + }); + gBrowser.removeTab(tab); +}); + +add_task(function* test_navigate_large_hex_number() { + let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank"); + yield BrowserTestUtils.browserLoaded(tab.linkedBrowser); + yield* runURLBarSearchTest({ + valueToOpen: "0x7f0000017f000001", + expectSearch: true, + expectNotification: false + }); + gBrowser.removeTab(tab); +}); + +function get_test_function_for_localhost_with_hostname(hostName, isPrivate) { + return function* test_navigate_single_host() { + const pref = "browser.fixup.domainwhitelist.localhost"; + let win; + if (isPrivate) { + let promiseWin = BrowserTestUtils.waitForNewWindow(); + win = OpenBrowserWindow({private: true}); + yield promiseWin; + let deferredOpenFocus = Promise.defer(); + waitForFocus(deferredOpenFocus.resolve, win); + yield deferredOpenFocus.promise; + } else { + win = window; + } + let browser = win.gBrowser; + let tab = yield BrowserTestUtils.openNewForegroundTab(browser); + + Services.prefs.setBoolPref(pref, false); + yield* runURLBarSearchTest({ + valueToOpen: hostName, + expectSearch: true, + expectNotification: true, + aWindow: win, + }); + + let notificationBox = browser.getNotificationBox(tab.linkedBrowser); + let notification = notificationBox.getNotificationWithValue("keyword-uri-fixup"); + let docLoadPromise = waitForDocLoadAndStopIt("http://" + hostName + "/", tab.linkedBrowser); + notification.querySelector(".notification-button-default").click(); + + // check pref value + let prefValue = Services.prefs.getBoolPref(pref); + is(prefValue, !isPrivate, "Pref should have the correct state."); + + yield docLoadPromise; + browser.removeTab(tab); + + // Now try again with the pref set. + tab = browser.selectedTab = browser.addTab("about:blank"); + yield BrowserTestUtils.browserLoaded(tab.linkedBrowser); + // In a private window, the notification should appear again. + yield* runURLBarSearchTest({ + valueToOpen: hostName, + expectSearch: isPrivate, + expectNotification: isPrivate, + aWindow: win, + }); + browser.removeTab(tab); + if (isPrivate) { + info("Waiting for private window to close"); + yield BrowserTestUtils.closeWindow(win); + let deferredFocus = Promise.defer(); + info("Waiting for focus"); + waitForFocus(deferredFocus.resolve, window); + yield deferredFocus.promise; + } + } +} + +add_task(get_test_function_for_localhost_with_hostname("localhost")); +add_task(get_test_function_for_localhost_with_hostname("localhost.")); +add_task(get_test_function_for_localhost_with_hostname("localhost", true)); + +add_task(function* test_navigate_invalid_url() { + let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank"); + yield BrowserTestUtils.browserLoaded(tab.linkedBrowser); + yield* runURLBarSearchTest({ + valueToOpen: "mozilla is awesome", + expectSearch: true, + expectNotification: false, + }); + gBrowser.removeTab(tab); +}); diff --git a/browser/base/content/test/urlbar/browser_urlbarSearchSuggestions.js b/browser/base/content/test/urlbar/browser_urlbarSearchSuggestions.js new file mode 100644 index 000000000..5146ba98c --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbarSearchSuggestions.js @@ -0,0 +1,66 @@ +const SUGGEST_URLBAR_PREF = "browser.urlbar.suggest.searches"; +const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml"; + +// Must run first. +add_task(function* prepare() { + Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, true); + let engine = yield promiseNewSearchEngine(TEST_ENGINE_BASENAME); + let oldCurrentEngine = Services.search.currentEngine; + Services.search.currentEngine = engine; + registerCleanupFunction(function* () { + Services.prefs.clearUserPref(SUGGEST_URLBAR_PREF); + Services.search.currentEngine = oldCurrentEngine; + + // Clicking suggestions causes visits to search results pages, so clear that + // history now. + yield PlacesTestUtils.clearHistory(); + + // Make sure the popup is closed for the next test. + gURLBar.blur(); + Assert.ok(!gURLBar.popup.popupOpen, "popup should be closed"); + }); +}); + +add_task(function* clickSuggestion() { + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser); + gURLBar.focus(); + yield promiseAutocompleteResultPopup("foo"); + let [idx, suggestion, engineName] = yield promiseFirstSuggestion(); + Assert.equal(engineName, + "browser_searchSuggestionEngine%20searchSuggestionEngine.xml", + "Expected suggestion engine"); + let item = gURLBar.popup.richlistbox.getItemAtIndex(idx); + + let uri = Services.search.currentEngine.getSubmission(suggestion).uri; + let loadPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, + false, uri.spec); + item.click(); + yield loadPromise; + yield BrowserTestUtils.removeTab(tab); +}); + +function getFirstSuggestion() { + let controller = gURLBar.popup.input.controller; + let matchCount = controller.matchCount; + for (let i = 0; i < matchCount; i++) { + let url = controller.getValueAt(i); + let mozActionMatch = url.match(/^moz-action:([^,]+),(.*)$/); + if (mozActionMatch) { + let [, type, paramStr] = mozActionMatch; + let params = JSON.parse(paramStr); + if (type == "searchengine" && "searchSuggestion" in params) { + return [i, params.searchSuggestion, params.engineName]; + } + } + } + return [-1, null, null]; +} + +function* promiseFirstSuggestion() { + let tuple = [-1, null, null]; + yield BrowserTestUtils.waitForCondition(() => { + tuple = getFirstSuggestion(); + return tuple[0] >= 0; + }); + return tuple; +} diff --git a/browser/base/content/test/urlbar/browser_urlbarSearchSuggestionsNotification.js b/browser/base/content/test/urlbar/browser_urlbarSearchSuggestionsNotification.js new file mode 100644 index 000000000..94ae8a3ff --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbarSearchSuggestionsNotification.js @@ -0,0 +1,254 @@ +const SUGGEST_ALL_PREF = "browser.search.suggest.enabled"; +const SUGGEST_URLBAR_PREF = "browser.urlbar.suggest.searches"; +const CHOICE_PREF = "browser.urlbar.userMadeSearchSuggestionsChoice"; +const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml"; + +// Must run first. +add_task(function* prepare() { + let engine = yield promiseNewSearchEngine(TEST_ENGINE_BASENAME); + let oldCurrentEngine = Services.search.currentEngine; + Services.search.currentEngine = engine; + registerCleanupFunction(function* () { + Services.search.currentEngine = oldCurrentEngine; + Services.prefs.clearUserPref(SUGGEST_ALL_PREF); + Services.prefs.clearUserPref(SUGGEST_URLBAR_PREF); + + // Disable the notification for future tests so it doesn't interfere with + // them. clearUserPref() won't work because by default the pref is false. + yield setUserMadeChoicePref(true); + + // Make sure the popup is closed for the next test. + gURLBar.blur(); + Assert.ok(!gURLBar.popup.popupOpen, "popup should be closed"); + }); +}); + +add_task(function* focus() { + // Focusing the urlbar used to open the popup in order to show the + // notification, but it doesn't anymore. Make sure it does not. + Services.prefs.setBoolPref(SUGGEST_ALL_PREF, true); + yield setUserMadeChoicePref(false); + gURLBar.blur(); + gURLBar.focus(); + Assert.ok(!gURLBar.popup.popupOpen, "popup should remain closed"); +}); + +add_task(function* dismissWithoutResults() { + Services.prefs.setBoolPref(SUGGEST_ALL_PREF, true); + yield setUserMadeChoicePref(false); + gURLBar.blur(); + gURLBar.focus(); + let popupPromise = promisePopupShown(gURLBar.popup); + gURLBar.openPopup(); + yield popupPromise; + Assert.ok(gURLBar.popup.popupOpen, "popup should be open"); + assertVisible(true); + Assert.equal(gURLBar.popup._matchCount, 0, "popup should have no results"); + let disableButton = document.getAnonymousElementByAttribute( + gURLBar.popup, "anonid", "search-suggestions-notification-disable" + ); + let transitionPromise = promiseTransition(); + disableButton.click(); + yield transitionPromise; + Assert.ok(!gURLBar.popup.popupOpen, "popup should be closed"); + gURLBar.blur(); + gURLBar.focus(); + Assert.ok(!gURLBar.popup.popupOpen, "popup should remain closed"); + yield promiseAutocompleteResultPopup("foo"); + Assert.ok(gURLBar.popup.popupOpen, "popup should be open"); + assertVisible(false); +}); + +add_task(function* dismissWithResults() { + Services.prefs.setBoolPref(SUGGEST_ALL_PREF, true); + yield setUserMadeChoicePref(false); + gURLBar.blur(); + gURLBar.focus(); + yield promiseAutocompleteResultPopup("foo"); + Assert.ok(gURLBar.popup.popupOpen, "popup should be open"); + assertVisible(true); + Assert.ok(gURLBar.popup._matchCount > 0, "popup should have results"); + let disableButton = document.getAnonymousElementByAttribute( + gURLBar.popup, "anonid", "search-suggestions-notification-disable" + ); + let transitionPromise = promiseTransition(); + disableButton.click(); + yield transitionPromise; + Assert.ok(gURLBar.popup.popupOpen, "popup should remain open"); + gURLBar.blur(); + gURLBar.focus(); + Assert.ok(!gURLBar.popup.popupOpen, "popup should remain closed"); + yield promiseAutocompleteResultPopup("foo"); + Assert.ok(gURLBar.popup.popupOpen, "popup should be open"); + assertVisible(false); +}); + +add_task(function* disable() { + Services.prefs.setBoolPref(SUGGEST_ALL_PREF, true); + yield setUserMadeChoicePref(false); + gURLBar.blur(); + gURLBar.focus(); + yield promiseAutocompleteResultPopup("foo"); + Assert.ok(gURLBar.popup.popupOpen, "popup should be open"); + assertVisible(true); + let disableButton = document.getAnonymousElementByAttribute( + gURLBar.popup, "anonid", "search-suggestions-notification-disable" + ); + let transitionPromise = promiseTransition(); + disableButton.click(); + yield transitionPromise; + gURLBar.blur(); + yield promiseAutocompleteResultPopup("foo"); + Assert.ok(!suggestionsPresent()); +}); + +add_task(function* enable() { + Services.prefs.setBoolPref(SUGGEST_ALL_PREF, true); + Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, false); + yield setUserMadeChoicePref(false); + gURLBar.blur(); + gURLBar.focus(); + yield promiseAutocompleteResultPopup("foo"); + assertVisible(true); + Assert.ok(!suggestionsPresent()); + let enableButton = document.getAnonymousElementByAttribute( + gURLBar.popup, "anonid", "search-suggestions-notification-enable" + ); + let searchPromise = BrowserTestUtils.waitForCondition(suggestionsPresent, + "waiting for suggestions"); + enableButton.click(); + yield searchPromise; + // Clicking Yes should trigger a new search so that suggestions appear + // immediately. + Assert.ok(suggestionsPresent()); + gURLBar.blur(); + gURLBar.focus(); + // Suggestions should still be present in a new search of course. + yield promiseAutocompleteResultPopup("bar"); + Assert.ok(suggestionsPresent()); +}); + +add_task(function* privateWindow() { + // Since suggestions are disabled in private windows, the notification should + // not appear even when suggestions are otherwise enabled. + let win = yield BrowserTestUtils.openNewBrowserWindow({ private: true }); + win.gURLBar.blur(); + win.gURLBar.focus(); + yield promiseAutocompleteResultPopup("foo", win); + assertVisible(false, win); + win.gURLBar.blur(); + yield BrowserTestUtils.closeWindow(win); +}); + +add_task(function* multipleWindows() { + // Opening multiple windows, using their urlbars, and then dismissing the + // notification in one should dismiss the notification in all. + Services.prefs.setBoolPref(SUGGEST_ALL_PREF, true); + Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, false); + yield setUserMadeChoicePref(false); + + gURLBar.focus(); + yield promiseAutocompleteResultPopup("win1"); + assertVisible(true); + + let win2 = yield BrowserTestUtils.openNewBrowserWindow(); + win2.gURLBar.focus(); + yield promiseAutocompleteResultPopup("win2", win2); + assertVisible(true, win2); + + let win3 = yield BrowserTestUtils.openNewBrowserWindow(); + win3.gURLBar.focus(); + yield promiseAutocompleteResultPopup("win3", win3); + assertVisible(true, win3); + + let enableButton = win3.document.getAnonymousElementByAttribute( + win3.gURLBar.popup, "anonid", "search-suggestions-notification-enable" + ); + let transitionPromise = promiseTransition(win3); + enableButton.click(); + yield transitionPromise; + assertVisible(false, win3); + + win2.gURLBar.focus(); + yield promiseAutocompleteResultPopup("win2done", win2); + assertVisible(false, win2); + + gURLBar.focus(); + yield promiseAutocompleteResultPopup("win1done"); + assertVisible(false); + + yield BrowserTestUtils.closeWindow(win2); + yield BrowserTestUtils.closeWindow(win3); +}); + +add_task(function* enableOutsideNotification() { + // Setting the suggest.searches pref outside the notification (e.g., by + // ticking the checkbox in the preferences window) should hide it. + Services.prefs.setBoolPref(SUGGEST_ALL_PREF, true); + Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, false); + yield setUserMadeChoicePref(false); + + Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, true); + gURLBar.focus(); + yield promiseAutocompleteResultPopup("foo"); + assertVisible(false); +}); + +/** + * Setting the choice pref triggers a pref observer in the urlbar, which hides + * the notification if it's present. This function returns a promise that's + * resolved once the observer fires. + * + * @param userMadeChoice A boolean, the pref's new value. + * @return A Promise that's resolved when the observer fires -- or, if the pref + * is currently the given value, that's resolved immediately. + */ +function setUserMadeChoicePref(userMadeChoice) { + return new Promise(resolve => { + let currentUserMadeChoice = Services.prefs.getBoolPref(CHOICE_PREF); + if (currentUserMadeChoice != userMadeChoice) { + Services.prefs.addObserver(CHOICE_PREF, function obs(subj, topic, data) { + Services.prefs.removeObserver(CHOICE_PREF, obs); + resolve(); + }, false); + } + Services.prefs.setBoolPref(CHOICE_PREF, userMadeChoice); + if (currentUserMadeChoice == userMadeChoice) { + resolve(); + } + }); +} + +function suggestionsPresent() { + let controller = gURLBar.popup.input.controller; + let matchCount = controller.matchCount; + for (let i = 0; i < matchCount; i++) { + let url = controller.getValueAt(i); + let mozActionMatch = url.match(/^moz-action:([^,]+),(.*)$/); + if (mozActionMatch) { + let [, type, paramStr] = mozActionMatch; + let params = JSON.parse(paramStr); + if (type == "searchengine" && "searchSuggestion" in params) { + return true; + } + } + } + return false; +} + +function assertVisible(visible, win=window) { + let style = + win.getComputedStyle(win.gURLBar.popup.searchSuggestionsNotification); + Assert.equal(style.visibility, visible ? "visible" : "collapse"); +} + +function promiseTransition(win=window) { + return new Promise(resolve => { + win.gURLBar.popup.addEventListener("transitionend", function onEnd() { + win.gURLBar.popup.removeEventListener("transitionend", onEnd, true); + // The urlbar needs to handle the transitionend first, but that happens + // naturally since promises are resolved at the end of the current tick. + resolve(); + }, true); + }); +} diff --git a/browser/base/content/test/urlbar/browser_urlbarSearchTelemetry.js b/browser/base/content/test/urlbar/browser_urlbarSearchTelemetry.js new file mode 100644 index 000000000..8c28401ea --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbarSearchTelemetry.js @@ -0,0 +1,216 @@ +"use strict"; + +Cu.import("resource:///modules/BrowserUITelemetry.jsm"); + +const SUGGEST_URLBAR_PREF = "browser.urlbar.suggest.searches"; +const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml"; + +// Must run first. +add_task(function* prepare() { + Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, true); + let engine = yield promiseNewSearchEngine(TEST_ENGINE_BASENAME); + let oldCurrentEngine = Services.search.currentEngine; + Services.search.currentEngine = engine; + + registerCleanupFunction(function* () { + Services.prefs.clearUserPref(SUGGEST_URLBAR_PREF); + Services.search.currentEngine = oldCurrentEngine; + + // Clicking urlbar results causes visits to their associated pages, so clear + // that history now. + yield PlacesTestUtils.clearHistory(); + + // Make sure the popup is closed for the next test. + gURLBar.blur(); + Assert.ok(!gURLBar.popup.popupOpen, "popup should be closed"); + }); + + // Move the mouse away from the urlbar one-offs so that a one-off engine is + // not inadvertently selected. + yield new Promise(resolve => { + EventUtils.synthesizeNativeMouseMove(window.document.documentElement, 0, 0, + resolve); + }); +}); + +add_task(function* heuristicResultMouse() { + yield compareCounts(function* () { + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser); + gURLBar.focus(); + yield promiseAutocompleteResultPopup("heuristicResult"); + let action = getActionAtIndex(0); + Assert.ok(!!action, "there should be an action at index 0"); + Assert.equal(action.type, "searchengine", "type should be searchengine"); + let loadPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser); + gURLBar.popup.richlistbox.getItemAtIndex(0).click(); + yield loadPromise; + yield BrowserTestUtils.removeTab(tab); + }); +}); + +add_task(function* heuristicResultKeyboard() { + yield compareCounts(function* () { + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser); + gURLBar.focus(); + yield promiseAutocompleteResultPopup("heuristicResult"); + let action = getActionAtIndex(0); + Assert.ok(!!action, "there should be an action at index 0"); + Assert.equal(action.type, "searchengine", "type should be searchengine"); + let loadPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser); + EventUtils.sendKey("return"); + yield loadPromise; + yield BrowserTestUtils.removeTab(tab); + }); +}); + +add_task(function* searchSuggestionMouse() { + yield compareCounts(function* () { + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser); + gURLBar.focus(); + yield promiseAutocompleteResultPopup("searchSuggestion"); + let idx = getFirstSuggestionIndex(); + Assert.ok(idx >= 0, "there should be a first suggestion"); + let loadPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser); + gURLBar.popup.richlistbox.getItemAtIndex(idx).click(); + yield loadPromise; + yield BrowserTestUtils.removeTab(tab); + }); +}); + +add_task(function* searchSuggestionKeyboard() { + yield compareCounts(function* () { + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser); + gURLBar.focus(); + yield promiseAutocompleteResultPopup("searchSuggestion"); + let idx = getFirstSuggestionIndex(); + Assert.ok(idx >= 0, "there should be a first suggestion"); + let loadPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser); + while (idx--) { + EventUtils.sendKey("down"); + } + EventUtils.sendKey("return"); + yield loadPromise; + yield BrowserTestUtils.removeTab(tab); + }); +}); + +/** + * This does three things: gets current telemetry/FHR counts, calls + * clickCallback, gets telemetry/FHR counts again to compare them to the old + * counts. + * + * @param clickCallback Use this to open the urlbar popup and choose and click a + * result. + */ +function* compareCounts(clickCallback) { + // Search events triggered by clicks (not the Return key in the urlbar) are + // recorded in three places: + // * BrowserUITelemetry + // * Telemetry histogram named "SEARCH_COUNTS" + // * FHR + + let engine = Services.search.currentEngine; + let engineID = "org.mozilla.testsearchsuggestions"; + + // First, get the current counts. + + // BrowserUITelemetry + let uiTelemCount = 0; + let bucket = BrowserUITelemetry.currentBucket; + let events = BrowserUITelemetry.getToolbarMeasures().countableEvents; + if (events[bucket] && + events[bucket].search && + events[bucket].search.urlbar) { + uiTelemCount = events[bucket].search.urlbar; + } + + // telemetry histogram SEARCH_COUNTS + let histogramCount = 0; + let histogramKey = engineID + ".urlbar"; + let histogram; + try { + histogram = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS"); + } catch (ex) { + // No searches performed yet, not a problem. + } + if (histogram) { + let snapshot = histogram.snapshot(); + if (histogramKey in snapshot) { + histogramCount = snapshot[histogramKey].sum; + } + } + + // FHR -- first make sure the engine has an identifier so that FHR is happy. + Object.defineProperty(engine.wrappedJSObject, "identifier", + { value: engineID }); + + gURLBar.focus(); + yield clickCallback(); + + // Now get the new counts and compare them to the old. + + // BrowserUITelemetry + events = BrowserUITelemetry.getToolbarMeasures().countableEvents; + Assert.ok(bucket in events, "bucket should be recorded"); + events = events[bucket]; + Assert.ok("search" in events, "search should be recorded"); + events = events.search; + Assert.ok("urlbar" in events, "urlbar should be recorded"); + Assert.equal(events.urlbar, uiTelemCount + 1, + "clicked suggestion should be recorded"); + + // telemetry histogram SEARCH_COUNTS + histogram = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS"); + let snapshot = histogram.snapshot(); + Assert.ok(histogramKey in snapshot, "histogram with key should be recorded"); + Assert.equal(snapshot[histogramKey].sum, histogramCount + 1, + "histogram sum should be incremented"); +} + +/** + * Returns the "action" object at the given index in the urlbar results: + * { type, params: {}} + * + * @param index The index in the urlbar results. + * @return An action object, or null if index >= number of results. + */ +function getActionAtIndex(index) { + let controller = gURLBar.popup.input.controller; + if (controller.matchCount <= index) { + return null; + } + let url = controller.getValueAt(index); + let mozActionMatch = url.match(/^moz-action:([^,]+),(.*)$/); + if (!mozActionMatch) { + let msg = "result at index " + index + " is not a moz-action: " + url; + Assert.ok(false, msg); + throw new Error(msg); + } + let [, type, paramStr] = mozActionMatch; + return { + type: type, + params: JSON.parse(paramStr), + }; +} + +/** + * Returns the index of the first search suggestion in the urlbar results. + * + * @return An index, or -1 if there are no search suggestions. + */ +function getFirstSuggestionIndex() { + let controller = gURLBar.popup.input.controller; + let matchCount = controller.matchCount; + for (let i = 0; i < matchCount; i++) { + let url = controller.getValueAt(i); + let mozActionMatch = url.match(/^moz-action:([^,]+),(.*)$/); + if (mozActionMatch) { + let [, type, paramStr] = mozActionMatch; + let params = JSON.parse(paramStr); + if (type == "searchengine" && "searchSuggestion" in params) { + return i; + } + } + } + return -1; +} diff --git a/browser/base/content/test/urlbar/browser_urlbarStop.js b/browser/base/content/test/urlbar/browser_urlbarStop.js new file mode 100644 index 000000000..8cf9d8017 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbarStop.js @@ -0,0 +1,30 @@ +"use strict"; + +const goodURL = "http://mochi.test:8888/"; +const badURL = "http://mochi.test:8888/whatever.html"; + +add_task(function* () { + gBrowser.selectedTab = gBrowser.addTab(goodURL); + yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + is(gURLBar.textValue, gURLBar.trimValue(goodURL), "location bar reflects loaded page"); + + yield typeAndSubmitAndStop(badURL); + is(gURLBar.textValue, gURLBar.trimValue(goodURL), "location bar reflects loaded page after stop()"); + gBrowser.removeCurrentTab(); + + gBrowser.selectedTab = gBrowser.addTab("about:blank"); + is(gURLBar.textValue, "", "location bar is empty"); + + yield typeAndSubmitAndStop(badURL); + is(gURLBar.textValue, gURLBar.trimValue(badURL), "location bar reflects stopped page in an empty tab"); + gBrowser.removeCurrentTab(); +}); + +function* typeAndSubmitAndStop(url) { + yield promiseAutocompleteResultPopup(url, window, true); + is(gURLBar.textValue, gURLBar.trimValue(url), "location bar reflects loading page"); + + let promise = waitForDocLoadAndStopIt(url, gBrowser.selectedBrowser, false); + gURLBar.handleCommand(); + yield promise; +} diff --git a/browser/base/content/test/urlbar/browser_urlbarTrimURLs.js b/browser/base/content/test/urlbar/browser_urlbarTrimURLs.js new file mode 100644 index 000000000..913e99a8e --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbarTrimURLs.js @@ -0,0 +1,98 @@ +add_task(function* () { + const PREF_TRIMURLS = "browser.urlbar.trimURLs"; + + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser); + + registerCleanupFunction(function* () { + yield BrowserTestUtils.removeTab(tab); + Services.prefs.clearUserPref(PREF_TRIMURLS); + URLBarSetURI(); + }); + + Services.prefs.setBoolPref(PREF_TRIMURLS, true); + + testVal("http://mozilla.org/", "mozilla.org"); + testVal("https://mozilla.org/", "https://mozilla.org"); + testVal("http://mözilla.org/", "mözilla.org"); + testVal("http://mozilla.imaginatory/", "mozilla.imaginatory"); + testVal("http://www.mozilla.org/", "www.mozilla.org"); + testVal("http://sub.mozilla.org/", "sub.mozilla.org"); + testVal("http://sub1.sub2.sub3.mozilla.org/", "sub1.sub2.sub3.mozilla.org"); + testVal("http://mozilla.org/file.ext", "mozilla.org/file.ext"); + testVal("http://mozilla.org/sub/", "mozilla.org/sub/"); + + testVal("http://ftp.mozilla.org/", "ftp.mozilla.org"); + testVal("http://ftp1.mozilla.org/", "ftp1.mozilla.org"); + testVal("http://ftp42.mozilla.org/", "ftp42.mozilla.org"); + testVal("http://ftpx.mozilla.org/", "ftpx.mozilla.org"); + testVal("ftp://ftp.mozilla.org/", "ftp://ftp.mozilla.org"); + testVal("ftp://ftp1.mozilla.org/", "ftp://ftp1.mozilla.org"); + testVal("ftp://ftp42.mozilla.org/", "ftp://ftp42.mozilla.org"); + testVal("ftp://ftpx.mozilla.org/", "ftp://ftpx.mozilla.org"); + + testVal("https://user:pass@mozilla.org/", "https://user:pass@mozilla.org"); + testVal("https://user@mozilla.org/", "https://user@mozilla.org"); + testVal("http://user:pass@mozilla.org/", "user:pass@mozilla.org"); + testVal("http://user@mozilla.org/", "user@mozilla.org"); + testVal("http://sub.mozilla.org:666/", "sub.mozilla.org:666"); + + testVal("https://[fe80::222:19ff:fe11:8c76]/file.ext"); + testVal("http://[fe80::222:19ff:fe11:8c76]/", "[fe80::222:19ff:fe11:8c76]"); + testVal("https://user:pass@[fe80::222:19ff:fe11:8c76]:666/file.ext"); + testVal("http://user:pass@[fe80::222:19ff:fe11:8c76]:666/file.ext", "user:pass@[fe80::222:19ff:fe11:8c76]:666/file.ext"); + + testVal("mailto:admin@mozilla.org"); + testVal("gopher://mozilla.org/"); + testVal("about:config"); + testVal("jar:http://mozilla.org/example.jar!/"); + testVal("view-source:http://mozilla.org/"); + + // Behaviour for hosts with no dots depends on the whitelist: + let fixupWhitelistPref = "browser.fixup.domainwhitelist.localhost"; + Services.prefs.setBoolPref(fixupWhitelistPref, false); + testVal("http://localhost"); + Services.prefs.setBoolPref(fixupWhitelistPref, true); + testVal("http://localhost", "localhost"); + Services.prefs.clearUserPref(fixupWhitelistPref); + + testVal("http:// invalid url"); + + testVal("http://someotherhostwithnodots"); + testVal("http://localhost/ foo bar baz"); + testVal("http://localhost.localdomain/ foo bar baz", "localhost.localdomain/ foo bar baz"); + + Services.prefs.setBoolPref(PREF_TRIMURLS, false); + + testVal("http://mozilla.org/"); + + Services.prefs.setBoolPref(PREF_TRIMURLS, true); + + let promiseLoaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, + false, "http://example.com/"); + gBrowser.loadURI("http://example.com/"); + yield promiseLoaded; + + yield testCopy("example.com", "http://example.com/") + + SetPageProxyState("invalid"); + gURLBar.valueIsTyped = true; + yield testCopy("example.com", "example.com"); +}); + +function testVal(originalValue, targetValue) { + gURLBar.value = originalValue; + gURLBar.valueIsTyped = false; + is(gURLBar.textValue, targetValue || originalValue, "url bar value set"); +} + +function testCopy(originalValue, targetValue) { + return new Promise((resolve, reject) => { + waitForClipboard(targetValue, function () { + is(gURLBar.textValue, originalValue, "url bar copy value set"); + + gURLBar.focus(); + gURLBar.select(); + goDoCommand("cmd_copy"); + }, resolve, reject); + }); +} 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 000000000..c3cdf507f --- /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/base/content/test/urlbar/browser_urlbar_autoFill_backspaced.js b/browser/base/content/test/urlbar/browser_urlbar_autoFill_backspaced.js new file mode 100644 index 000000000..7fefd3f77 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbar_autoFill_backspaced.js @@ -0,0 +1,146 @@ +/* This test ensures that backspacing autoFilled values still allows to + * confirm the remaining value. + */ + +function* test_autocomplete(data) { + let {desc, typed, autofilled, modified, keys, action, onAutoFill} = data; + info(desc); + + yield promiseAutocompleteResultPopup(typed); + is(gURLBar.textValue, autofilled, "autofilled value is as expected"); + if (onAutoFill) + onAutoFill() + + keys.forEach(key => EventUtils.synthesizeKey(key, {})); + + is(gURLBar.textValue, modified, "backspaced value is as expected"); + + yield promiseSearchComplete(); + + ok(gURLBar.popup.richlistbox.children.length > 0, "Should get at least 1 result"); + let result = gURLBar.popup.richlistbox.children[0]; + let type = result.getAttribute("type"); + let types = type.split(/\s+/); + ok(types.indexOf(action) >= 0, `The type attribute "${type}" includes the expected action "${action}"`); + + gURLBar.popup.hidePopup(); + yield promisePopupHidden(gURLBar.popup); + gURLBar.blur(); +} + +add_task(function* () { + registerCleanupFunction(function* () { + Services.prefs.clearUserPref("browser.urlbar.autoFill"); + gURLBar.handleRevert(); + yield PlacesTestUtils.clearHistory(); + }); + Services.prefs.setBoolPref("browser.urlbar.autoFill", true); + + // Add a typed visit, so it will be autofilled. + yield PlacesTestUtils.addVisits({ + uri: NetUtil.newURI("http://example.com/"), + transition: Ci.nsINavHistoryService.TRANSITION_TYPED + }); + + yield test_autocomplete({ desc: "DELETE the autofilled part should search", + typed: "exam", + autofilled: "example.com/", + modified: "exam", + keys: ["VK_DELETE"], + action: "searchengine" + }); + yield test_autocomplete({ desc: "DELETE the final slash should visit", + typed: "example.com", + autofilled: "example.com/", + modified: "example.com", + keys: ["VK_DELETE"], + action: "visiturl" + }); + + yield test_autocomplete({ desc: "BACK_SPACE the autofilled part should search", + typed: "exam", + autofilled: "example.com/", + modified: "exam", + keys: ["VK_BACK_SPACE"], + action: "searchengine" + }); + yield test_autocomplete({ desc: "BACK_SPACE the final slash should visit", + typed: "example.com", + autofilled: "example.com/", + modified: "example.com", + keys: ["VK_BACK_SPACE"], + action: "visiturl" + }); + + yield test_autocomplete({ desc: "DELETE the autofilled part, then BACK_SPACE, should search", + typed: "exam", + autofilled: "example.com/", + modified: "exa", + keys: ["VK_DELETE", "VK_BACK_SPACE"], + action: "searchengine" + }); + yield test_autocomplete({ desc: "DELETE the final slash, then BACK_SPACE, should search", + typed: "example.com", + autofilled: "example.com/", + modified: "example.co", + keys: ["VK_DELETE", "VK_BACK_SPACE"], + action: "visiturl" + }); + + yield test_autocomplete({ desc: "BACK_SPACE the autofilled part, then BACK_SPACE, should search", + typed: "exam", + autofilled: "example.com/", + modified: "exa", + keys: ["VK_BACK_SPACE", "VK_BACK_SPACE"], + action: "searchengine" + }); + yield test_autocomplete({ desc: "BACK_SPACE the final slash, then BACK_SPACE, should search", + typed: "example.com", + autofilled: "example.com/", + modified: "example.co", + keys: ["VK_BACK_SPACE", "VK_BACK_SPACE"], + action: "visiturl" + }); + + yield test_autocomplete({ desc: "BACK_SPACE after blur should search", + typed: "ex", + autofilled: "example.com/", + modified: "e", + keys: ["VK_BACK_SPACE"], + action: "searchengine", + onAutoFill: () => { + gURLBar.blur(); + gURLBar.focus(); + gURLBar.selectionStart = 1; + gURLBar.selectionEnd = 12; + } + }); + yield test_autocomplete({ desc: "DELETE after blur should search", + typed: "ex", + autofilled: "example.com/", + modified: "e", + keys: ["VK_DELETE"], + action: "searchengine", + onAutoFill: () => { + gURLBar.blur(); + gURLBar.focus(); + gURLBar.selectionStart = 1; + gURLBar.selectionEnd = 12; + } + }); + yield test_autocomplete({ desc: "double BACK_SPACE after blur should search", + typed: "ex", + autofilled: "example.com/", + modified: "e", + keys: ["VK_BACK_SPACE", "VK_BACK_SPACE"], + action: "searchengine", + onAutoFill: () => { + gURLBar.blur(); + gURLBar.focus(); + gURLBar.selectionStart = 2; + gURLBar.selectionEnd = 12; + } + }); + + yield PlacesTestUtils.clearHistory(); +}); diff --git a/browser/base/content/test/urlbar/browser_urlbar_blanking.js b/browser/base/content/test/urlbar/browser_urlbar_blanking.js new file mode 100644 index 000000000..13660edab --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbar_blanking.js @@ -0,0 +1,35 @@ +"use strict"; + +add_task(function*() { + for (let page of gInitialPages) { + if (page == "about:newtab") { + // New tab preloading makes this a pain to test, so skip + continue; + } + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, page); + ok(!gURLBar.value, "The URL bar should be empty if we load a plain " + page + " page."); + yield BrowserTestUtils.removeTab(tab); + } +}); + +add_task(function*() { + const URI = "http://www.example.com/browser/browser/base/content/test/urlbar/file_blank_but_not_blank.html"; + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, URI); + is(gURLBar.value, URI, "The URL bar should match the URI"); + let browserLoaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser); + ContentTask.spawn(tab.linkedBrowser, null, function() { + content.document.querySelector('a').click(); + }); + yield browserLoaded; + ok(gURLBar.value.startsWith("javascript"), "The URL bar should have the JS URI"); + // When reloading, the javascript: uri we're using will throw an exception. + // That's deliberate, so we need to tell mochitest to ignore it: + SimpleTest.expectUncaughtException(true); + yield ContentTask.spawn(tab.linkedBrowser, null, function*() { + // This is sync, so by the time we return we should have changed the URL bar. + content.location.reload(); + }); + ok(!!gURLBar.value, "URL bar should not be blank."); + yield BrowserTestUtils.removeTab(tab); + SimpleTest.expectUncaughtException(false); +}); diff --git a/browser/base/content/test/urlbar/browser_urlbar_locationchange_urlbar_edit_dos.js b/browser/base/content/test/urlbar/browser_urlbar_locationchange_urlbar_edit_dos.js new file mode 100644 index 000000000..63ed58a62 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbar_locationchange_urlbar_edit_dos.js @@ -0,0 +1,41 @@ +"use strict"; + +function* checkURLBarValueStays(browser) { + gURLBar.select(); + EventUtils.synthesizeKey("a", {}); + is(gURLBar.value, "a", "URL bar value should match after sending a key"); + yield new Promise(resolve => { + let listener = { + onLocationChange(aWebProgress, aRequest, aLocation, aFlags) { + ok(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT, + "Should only get a same document location change"); + gBrowser.selectedBrowser.removeProgressListener(filter); + filter = null; + resolve(); + }, + }; + let filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"] + .createInstance(Ci.nsIWebProgress); + filter.addProgressListener(listener, Ci.nsIWebProgress.NOTIFY_ALL); + gBrowser.selectedBrowser.addProgressListener(filter); + }); + is(gURLBar.value, "a", "URL bar should not have been changed by location changes."); +} + +add_task(function*() { + yield BrowserTestUtils.withNewTab({ + gBrowser, + url: "http://example.com/browser/browser/base/content/test/urlbar/file_urlbar_edit_dos.html" + }, function*(browser) { + yield ContentTask.spawn(browser, "", function() { + content.wrappedJSObject.dos_hash(); + }); + yield checkURLBarValueStays(browser); + yield ContentTask.spawn(browser, "", function() { + content.clearTimeout(content.wrappedJSObject.dos_timeout); + content.wrappedJSObject.dos_pushState(); + }); + yield checkURLBarValueStays(browser); + }); +}); + diff --git a/browser/base/content/test/urlbar/browser_urlbar_remoteness_switch.js b/browser/base/content/test/urlbar/browser_urlbar_remoteness_switch.js new file mode 100644 index 000000000..9a1df0505 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbar_remoteness_switch.js @@ -0,0 +1,39 @@ +"use strict"; + +/** + * Verify that when loading and going back/forward through history between URLs + * loaded in the content process, and URLs loaded in the parent process, we + * don't set the URL for the tab to about:blank inbetween the loads. + */ +add_task(function*() { + let url = "http://www.example.com/foo.html"; + yield BrowserTestUtils.withNewTab({gBrowser, url}, function*(browser) { + let wpl = { + onLocationChange(wpl, request, location, flags) { + if (location.schemeIs("about")) { + is(location.spec, "about:config", "Only about: location change should be for about:preferences"); + } else { + is(location.spec, url, "Only non-about: location change should be for the http URL we're dealing with."); + } + }, + }; + gBrowser.addProgressListener(wpl); + + let didLoad = BrowserTestUtils.browserLoaded(browser, null, function(loadedURL) { + return loadedURL == "about:config"; + }); + yield BrowserTestUtils.loadURI(browser, "about:config"); + yield didLoad; + + gBrowser.goBack(); + yield BrowserTestUtils.browserLoaded(browser, null, function(loadedURL) { + return url == loadedURL; + }); + gBrowser.goForward(); + yield BrowserTestUtils.browserLoaded(browser, null, function(loadedURL) { + return loadedURL == "about:config"; + }); + gBrowser.removeProgressListener(wpl); + }); +}); + diff --git a/browser/base/content/test/urlbar/browser_urlbar_searchsettings.js b/browser/base/content/test/urlbar/browser_urlbar_searchsettings.js new file mode 100644 index 000000000..04b1c508b --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbar_searchsettings.js @@ -0,0 +1,30 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(function*() { + let button = document.getElementById("urlbar-search-settings"); + if (!button) { + ok("Skipping test"); + return; + } + + yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank" }, function* () { + let popupopened = BrowserTestUtils.waitForEvent(gURLBar.popup, "popupshown"); + + gURLBar.focus(); + EventUtils.synthesizeKey("a", {}); + yield popupopened; + + // Since the current tab is blank the preferences pane will load there + let loaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + let popupclosed = BrowserTestUtils.waitForEvent(gURLBar.popup, "popuphidden"); + EventUtils.synthesizeMouseAtCenter(button, {}); + yield loaded; + yield popupclosed; + + is(gBrowser.selectedBrowser.currentURI.spec, "about:preferences#search", + "Should have loaded the right page"); + }); +}); diff --git a/browser/base/content/test/urlbar/browser_urlbar_stop_pending.js b/browser/base/content/test/urlbar/browser_urlbar_stop_pending.js new file mode 100644 index 000000000..6b6a10ea3 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbar_stop_pending.js @@ -0,0 +1,138 @@ +"use strict"; + +const SLOW_PAGE = "http://www.example.com/browser/browser/base/content/test/urlbar/slow-page.sjs"; +const SLOW_PAGE2 = "http://mochi.test:8888/browser/browser/base/content/test/urlbar/slow-page.sjs?faster"; + +/** + * Check that if we: + * 1) have a loaded page + * 2) load a separate URL + * 3) before the URL for step 2 has finished loading, load a third URL + * we don't revert to the URL from (1). + */ +add_task(function*() { + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com", true, true); + + let expectedURLBarChange = SLOW_PAGE; + let sawChange = false; + let handler = e => { + sawChange = true; + is(gURLBar.value, expectedURLBarChange, "Should not change URL bar value!"); + }; + + let obs = new MutationObserver(handler); + + obs.observe(gURLBar, {attributes: true}); + gURLBar.value = SLOW_PAGE; + gURLBar.handleCommand(); + + // If this ever starts going intermittent, we've broken this. + yield new Promise(resolve => setTimeout(resolve, 200)); + expectedURLBarChange = SLOW_PAGE2; + let pageLoadPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser); + gURLBar.value = expectedURLBarChange; + gURLBar.handleCommand(); + is(gURLBar.value, expectedURLBarChange, "Should not have changed URL bar value synchronously."); + yield pageLoadPromise; + ok(sawChange, "The URL bar change handler should have been called by the time the page was loaded"); + obs.disconnect(); + obs = null; + yield BrowserTestUtils.removeTab(tab); +}); + +/** + * Check that if we: + * 1) middle-click a link to a separate page whose server doesn't respond + * 2) we switch to that tab and stop the request + * + * The URL bar continues to contain the URL of the page we wanted to visit. + */ +add_task(function*() { + let socket = Cc["@mozilla.org/network/server-socket;1"].createInstance(Ci.nsIServerSocket); + socket.init(-1, true, -1); + const PORT = socket.port; + registerCleanupFunction(() => { socket.close(); }); + + const TEST_PATH = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com"); + const BASE_PAGE = TEST_PATH + "dummy_page.html"; + const SLOW_HOST = `https://localhost:${PORT}/`; + info("Using URLs: " + SLOW_HOST); + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, BASE_PAGE); + info("opened tab"); + yield ContentTask.spawn(tab.linkedBrowser, SLOW_HOST, URL => { + let link = content.document.createElement("a"); + link.href = URL; + link.textContent = "click me to open a slow page"; + link.id = "clickme" + content.document.body.appendChild(link); + }); + info("added link"); + let newTabPromise = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "TabOpen"); + // Middle click the link: + yield BrowserTestUtils.synthesizeMouseAtCenter("#clickme", { button: 1 }, tab.linkedBrowser); + // get new tab, switch to it + let newTab = (yield newTabPromise).target; + yield BrowserTestUtils.switchTab(gBrowser, newTab); + is(gURLBar.value, SLOW_HOST, "Should have slow page in URL bar"); + let browserStoppedPromise = BrowserTestUtils.browserStopped(newTab.linkedBrowser); + BrowserStop(); + yield browserStoppedPromise; + + is(gURLBar.value, SLOW_HOST, "Should still have slow page in URL bar after stop"); + yield BrowserTestUtils.removeTab(newTab); + yield BrowserTestUtils.removeTab(tab); +}); +/** + * Check that if we: + * 1) middle-click a link to a separate page whose server doesn't respond + * 2) we alter the URL on that page to some other server that doesn't respond + * 3) we stop the request + * + * The URL bar continues to contain the second URL. + */ +add_task(function*() { + let socket = Cc["@mozilla.org/network/server-socket;1"].createInstance(Ci.nsIServerSocket); + socket.init(-1, true, -1); + const PORT1 = socket.port; + let socket2 = Cc["@mozilla.org/network/server-socket;1"].createInstance(Ci.nsIServerSocket); + socket2.init(-1, true, -1); + const PORT2 = socket2.port; + registerCleanupFunction(() => { socket.close(); socket2.close(); }); + + const TEST_PATH = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com"); + const BASE_PAGE = TEST_PATH + "dummy_page.html"; + const SLOW_HOST1 = `https://localhost:${PORT1}/`; + const SLOW_HOST2 = `https://localhost:${PORT2}/`; + info("Using URLs: " + SLOW_HOST1 + " and " + SLOW_HOST2); + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, BASE_PAGE); + info("opened tab"); + yield ContentTask.spawn(tab.linkedBrowser, SLOW_HOST1, URL => { + let link = content.document.createElement("a"); + link.href = URL; + link.textContent = "click me to open a slow page"; + link.id = "clickme" + content.document.body.appendChild(link); + }); + info("added link"); + let newTabPromise = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "TabOpen"); + // Middle click the link: + yield BrowserTestUtils.synthesizeMouseAtCenter("#clickme", { button: 1 }, tab.linkedBrowser); + // get new tab, switch to it + let newTab = (yield newTabPromise).target; + yield BrowserTestUtils.switchTab(gBrowser, newTab); + is(gURLBar.value, SLOW_HOST1, "Should have slow page in URL bar"); + let browserStoppedPromise = BrowserTestUtils.browserStopped(newTab.linkedBrowser); + gURLBar.value = SLOW_HOST2; + gURLBar.handleCommand(); + yield browserStoppedPromise; + + is(gURLBar.value, SLOW_HOST2, "Should have second slow page in URL bar"); + browserStoppedPromise = BrowserTestUtils.browserStopped(newTab.linkedBrowser); + BrowserStop(); + yield browserStoppedPromise; + + is(gURLBar.value, SLOW_HOST2, "Should still have second slow page in URL bar after stop"); + yield BrowserTestUtils.removeTab(newTab); + yield BrowserTestUtils.removeTab(tab); +}); + diff --git a/browser/base/content/test/urlbar/browser_wyciwyg_urlbarCopying.js b/browser/base/content/test/urlbar/browser_wyciwyg_urlbarCopying.js new file mode 100644 index 000000000..54b174aa8 --- /dev/null +++ b/browser/base/content/test/urlbar/browser_wyciwyg_urlbarCopying.js @@ -0,0 +1,31 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function testURLBarCopy(targetValue) { + return new Promise((resolve, reject) => { + info("Expecting copy of: " + targetValue); + waitForClipboard(targetValue, function () { + gURLBar.focus(); + gURLBar.select(); + + goDoCommand("cmd_copy"); + }, resolve, () => { + ok(false, "Clipboard copy failed"); + reject(); + }); + }); +} + +add_task(function* () { + const url = "http://mochi.test:8888/browser/browser/base/content/test/urlbar/test_wyciwyg_copying.html"; + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url); + + yield BrowserTestUtils.synthesizeMouseAtCenter("#btn", {}, tab.linkedBrowser); + let currentURL = gBrowser.currentURI.spec; + ok(/^wyciwyg:\/\//i.test(currentURL), currentURL + " is a wyciwyg URI"); + + yield testURLBarCopy(url); + + while (gBrowser.tabs.length > 1) + gBrowser.removeCurrentTab(); +}); diff --git a/browser/base/content/test/urlbar/dummy_page.html b/browser/base/content/test/urlbar/dummy_page.html new file mode 100644 index 000000000..1a87e2840 --- /dev/null +++ b/browser/base/content/test/urlbar/dummy_page.html @@ -0,0 +1,9 @@ +<html> +<head> +<title>Dummy test page</title> +<meta http-equiv="Content-Type" content="text/html;charset=utf-8"></meta> +</head> +<body> +<p>Dummy test page</p> +</body> +</html> diff --git a/browser/base/content/test/urlbar/file_blank_but_not_blank.html b/browser/base/content/test/urlbar/file_blank_but_not_blank.html new file mode 100644 index 000000000..1f5fea8dc --- /dev/null +++ b/browser/base/content/test/urlbar/file_blank_but_not_blank.html @@ -0,0 +1,2 @@ +<script>var q = "1";</script> +<a href="javascript:q">Click me</a> diff --git a/browser/base/content/test/urlbar/file_urlbar_edit_dos.html b/browser/base/content/test/urlbar/file_urlbar_edit_dos.html new file mode 100644 index 000000000..5a6e7d109 --- /dev/null +++ b/browser/base/content/test/urlbar/file_urlbar_edit_dos.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<html> +<head> +<title>Try editing the URL bar</title> +<meta http-equiv="Content-Type" content="text/html;charset=utf-8"></meta> +</head> +<body> +<script> +var dos_timeout = null; +function dos_hash() { + dos_timeout = setTimeout(function() { + location.hash = "#"; + }, 50); +} + +function dos_pushState() { + dos_timeout = setTimeout(function() { + history.pushState({}, "Some title", ""); + }, 50); +} +</script> +</body> +</html> diff --git a/browser/base/content/test/urlbar/head.js b/browser/base/content/test/urlbar/head.js new file mode 100644 index 000000000..427dba080 --- /dev/null +++ b/browser/base/content/test/urlbar/head.js @@ -0,0 +1,205 @@ +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "Promise", + "resource://gre/modules/Promise.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Task", + "resource://gre/modules/Task.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", + "resource://gre/modules/PlacesUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils", + "resource://testing-common/PlacesTestUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Preferences", + "resource://gre/modules/Preferences.jsm"); + +/** + * Waits for the next top-level document load in the current browser. The URI + * of the document is compared against aExpectedURL. The load is then stopped + * before it actually starts. + * + * @param aExpectedURL + * The URL of the document that is expected to load. + * @param aStopFromProgressListener + * Whether to cancel the load directly from the progress listener. Defaults to true. + * If you're using this method to avoid hitting the network, you want the default (true). + * However, the browser UI will behave differently for loads stopped directly from + * the progress listener (effectively in the middle of a call to loadURI) and so there + * are cases where you may want to avoid stopping the load directly from within the + * progress listener callback. + * @return promise + */ +function waitForDocLoadAndStopIt(aExpectedURL, aBrowser=gBrowser.selectedBrowser, aStopFromProgressListener=true) { + function content_script(aStopFromProgressListener) { + let { interfaces: Ci, utils: Cu } = Components; + Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + let wp = docShell.QueryInterface(Ci.nsIWebProgress); + + function stopContent(now, uri) { + if (now) { + /* Hammer time. */ + content.stop(); + + /* Let the parent know we're done. */ + sendAsyncMessage("Test:WaitForDocLoadAndStopIt", { uri }); + } else { + setTimeout(stopContent.bind(null, true, uri), 0); + } + } + + let progressListener = { + onStateChange: function (webProgress, req, flags, status) { + dump("waitForDocLoadAndStopIt: onStateChange " + flags.toString(16) + ": " + req.name + "\n"); + + if (webProgress.isTopLevel && + flags & Ci.nsIWebProgressListener.STATE_START) { + wp.removeProgressListener(progressListener); + + let chan = req.QueryInterface(Ci.nsIChannel); + dump(`waitForDocLoadAndStopIt: Document start: ${chan.URI.spec}\n`); + + stopContent(aStopFromProgressListener, chan.originalURI.spec); + } + }, + QueryInterface: XPCOMUtils.generateQI(["nsISupportsWeakReference"]) + }; + wp.addProgressListener(progressListener, wp.NOTIFY_STATE_WINDOW); + + /** + * As |this| is undefined and we can't extend |docShell|, adding an unload + * event handler is the easiest way to ensure the weakly referenced + * progress listener is kept alive as long as necessary. + */ + addEventListener("unload", function () { + try { + wp.removeProgressListener(progressListener); + } catch (e) { /* Will most likely fail. */ } + }); + } + + return new Promise((resolve, reject) => { + function complete({ data }) { + is(data.uri, aExpectedURL, "waitForDocLoadAndStopIt: The expected URL was loaded"); + mm.removeMessageListener("Test:WaitForDocLoadAndStopIt", complete); + resolve(); + } + + let mm = aBrowser.messageManager; + mm.loadFrameScript("data:,(" + content_script.toString() + ")(" + aStopFromProgressListener + ");", true); + mm.addMessageListener("Test:WaitForDocLoadAndStopIt", complete); + info("waitForDocLoadAndStopIt: Waiting for URL: " + aExpectedURL); + }); +} + +function is_hidden(element) { + var style = element.ownerGlobal.getComputedStyle(element); + if (style.display == "none") + return true; + if (style.visibility != "visible") + return true; + if (style.display == "-moz-popup") + return ["hiding", "closed"].indexOf(element.state) != -1; + + // Hiding a parent element will hide all its children + if (element.parentNode != element.ownerDocument) + return is_hidden(element.parentNode); + + return false; +} + +function is_visible(element) { + var style = element.ownerGlobal.getComputedStyle(element); + if (style.display == "none") + return false; + if (style.visibility != "visible") + return false; + if (style.display == "-moz-popup" && element.state != "open") + return false; + + // Hiding a parent element will hide all its children + if (element.parentNode != element.ownerDocument) + return is_visible(element.parentNode); + + return true; +} + +function is_element_visible(element, msg) { + isnot(element, null, "Element should not be null, when checking visibility"); + ok(is_visible(element), msg || "Element should be visible"); +} + +function is_element_hidden(element, msg) { + isnot(element, null, "Element should not be null, when checking visibility"); + ok(is_hidden(element), msg || "Element should be hidden"); +} + +function promisePopupEvent(popup, eventSuffix) { + let endState = {shown: "open", hidden: "closed"}[eventSuffix]; + + if (popup.state == endState) + return Promise.resolve(); + + let eventType = "popup" + eventSuffix; + let deferred = Promise.defer(); + popup.addEventListener(eventType, function onPopupShown(event) { + popup.removeEventListener(eventType, onPopupShown); + deferred.resolve(); + }); + + return deferred.promise; +} + +function promisePopupShown(popup) { + return promisePopupEvent(popup, "shown"); +} + +function promisePopupHidden(popup) { + return promisePopupEvent(popup, "hidden"); +} + +function promiseSearchComplete(win = window) { + return promisePopupShown(win.gURLBar.popup).then(() => { + function searchIsComplete() { + return win.gURLBar.controller.searchStatus >= + Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH; + } + + // Wait until there are at least two matches. + return BrowserTestUtils.waitForCondition(searchIsComplete, "waiting urlbar search to complete"); + }); +} + +function promiseAutocompleteResultPopup(inputText, + win = window, + fireInputEvent = false) { + waitForFocus(() => { + win.gURLBar.focus(); + win.gURLBar.value = inputText; + if (fireInputEvent) { + // This is necessary to get the urlbar to set gBrowser.userTypedValue. + let event = document.createEvent("Events"); + event.initEvent("input", true, true); + win.gURLBar.dispatchEvent(event); + } + win.gURLBar.controller.startSearch(inputText); + }, win); + + return promiseSearchComplete(win); +} + +function promiseNewSearchEngine(basename) { + return new Promise((resolve, reject) => { + info("Waiting for engine to be added: " + basename); + let url = getRootDirectory(gTestPath) + basename; + Services.search.addEngine(url, null, "", false, { + onSuccess: function (engine) { + info("Search engine added: " + basename); + registerCleanupFunction(() => Services.search.removeEngine(engine)); + resolve(engine); + }, + onError: function (errCode) { + Assert.ok(false, "addEngine failed with error code " + errCode); + reject(); + }, + }); + }); +} + diff --git a/browser/base/content/test/urlbar/moz.png b/browser/base/content/test/urlbar/moz.png Binary files differnew file mode 100644 index 000000000..769c63634 --- /dev/null +++ b/browser/base/content/test/urlbar/moz.png diff --git a/browser/base/content/test/urlbar/print_postdata.sjs b/browser/base/content/test/urlbar/print_postdata.sjs new file mode 100644 index 000000000..4175a2480 --- /dev/null +++ b/browser/base/content/test/urlbar/print_postdata.sjs @@ -0,0 +1,22 @@ +const CC = Components.Constructor; +const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream"); + +function handleRequest(request, response) { + response.setHeader("Content-Type", "text/plain", false); + if (request.method == "GET") { + response.write(request.queryString); + } else { + var body = new BinaryInputStream(request.bodyInputStream); + + var avail; + var bytes = []; + + while ((avail = body.available()) > 0) + Array.prototype.push.apply(bytes, body.readByteArray(avail)); + + var data = String.fromCharCode.apply(null, bytes); + response.bodyOutputStream.write(data, data.length); + } +} diff --git a/browser/base/content/test/urlbar/redirect_bug623155.sjs b/browser/base/content/test/urlbar/redirect_bug623155.sjs new file mode 100644 index 000000000..64c6f143b --- /dev/null +++ b/browser/base/content/test/urlbar/redirect_bug623155.sjs @@ -0,0 +1,16 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +const REDIRECT_TO = "https://www.bank1.com/"; // Bad-cert host. + +function handleRequest(aRequest, aResponse) { + // Set HTTP Status + aResponse.setStatusLine(aRequest.httpVersion, 301, "Moved Permanently"); + + // Set redirect URI, mirroring the hash value. + let hash = (/\#.+/.test(aRequest.path))? + "#" + aRequest.path.split("#")[1]: + ""; + aResponse.setHeader("Location", REDIRECT_TO + hash); +} diff --git a/browser/base/content/test/urlbar/searchSuggestionEngine.sjs b/browser/base/content/test/urlbar/searchSuggestionEngine.sjs new file mode 100644 index 000000000..1978b4f66 --- /dev/null +++ b/browser/base/content/test/urlbar/searchSuggestionEngine.sjs @@ -0,0 +1,9 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +function handleRequest(req, resp) { + let suffixes = ["foo", "bar"]; + let data = [req.queryString, suffixes.map(s => req.queryString + s)]; + resp.setHeader("Content-Type", "application/json", false); + resp.write(JSON.stringify(data)); +} diff --git a/browser/base/content/test/urlbar/searchSuggestionEngine.xml b/browser/base/content/test/urlbar/searchSuggestionEngine.xml new file mode 100644 index 000000000..a5659792e --- /dev/null +++ b/browser/base/content/test/urlbar/searchSuggestionEngine.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> + +<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/"> +<ShortName>browser_searchSuggestionEngine searchSuggestionEngine.xml</ShortName> +<Url type="application/x-suggestions+json" method="GET" template="http://mochi.test:8888/browser/browser/base/content/test/urlbar/searchSuggestionEngine.sjs?{searchTerms}"/> +<Url type="text/html" method="GET" template="http://mochi.test:8888/" rel="searchform"/> +</SearchPlugin> diff --git a/browser/base/content/test/urlbar/slow-page.sjs b/browser/base/content/test/urlbar/slow-page.sjs new file mode 100644 index 000000000..f428d66e4 --- /dev/null +++ b/browser/base/content/test/urlbar/slow-page.sjs @@ -0,0 +1,22 @@ +"use strict"; + +const Cc = Components.classes; +const Ci = Components.interfaces; + +let timer; + +const DELAY_MS = 5000; +function handleRequest(request, response) { + if (request.queryString.endsWith("faster")) { + response.setHeader("Content-Type", "text/html", false); + response.write("<body>Not so slow!</body>"); + return; + } + response.processAsync(); + timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + timer.init(() => { + response.setHeader("Content-Type", "text/html", false); + response.write("<body>This was the slow load. You should never see this.</body>"); + response.finish(); + }, DELAY_MS, Ci.nsITimer.TYPE_ONE_SHOT); +} diff --git a/browser/base/content/test/urlbar/test_wyciwyg_copying.html b/browser/base/content/test/urlbar/test_wyciwyg_copying.html new file mode 100644 index 000000000..3a8c3a150 --- /dev/null +++ b/browser/base/content/test/urlbar/test_wyciwyg_copying.html @@ -0,0 +1,13 @@ +<html> +<body> +<script> + function go() { + var w = window.open(); + w.document.open(); + w.document.write("<html><body>test document</body></html>"); + w.document.close(); + } +</script> +<button id="btn" onclick="go();">test</button> +</body> +</html> |