summaryrefslogtreecommitdiffstats
path: root/browser/components/urlbar/tests/browser/browser_view_selectionByMouse.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/urlbar/tests/browser/browser_view_selectionByMouse.js')
-rw-r--r--browser/components/urlbar/tests/browser/browser_view_selectionByMouse.js607
1 files changed, 607 insertions, 0 deletions
diff --git a/browser/components/urlbar/tests/browser/browser_view_selectionByMouse.js b/browser/components/urlbar/tests/browser/browser_view_selectionByMouse.js
new file mode 100644
index 0000000000..2de8439b58
--- /dev/null
+++ b/browser/components/urlbar/tests/browser/browser_view_selectionByMouse.js
@@ -0,0 +1,607 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test selection on result view by mouse.
+
+ChromeUtils.defineESModuleGetters(this, {
+ UrlbarProviderQuickActions:
+ "resource:///modules/UrlbarProviderQuickActions.sys.mjs",
+});
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.urlbar.quickactions.enabled", true],
+ ["browser.urlbar.suggest.quickactions", true],
+ ["browser.urlbar.shortcuts.quickactions", true],
+ ],
+ });
+
+ UrlbarTestUtils.disableResultMenuAutohide(window);
+
+ await SearchTestUtils.installSearchExtension({}, { setAsDefault: true });
+
+ UrlbarProviderQuickActions.addAction("test-addons", {
+ commands: ["test-addons"],
+ label: "quickactions-addons",
+ onPick: () =>
+ BrowserTestUtils.loadURIString(gBrowser.selectedBrowser, "about:about"),
+ });
+ UrlbarProviderQuickActions.addAction("test-downloads", {
+ commands: ["test-downloads"],
+ label: "quickactions-downloads2",
+ onPick: () =>
+ BrowserTestUtils.loadURIString(
+ gBrowser.selectedBrowser,
+ "about:downloads"
+ ),
+ });
+
+ registerCleanupFunction(function () {
+ UrlbarProviderQuickActions.removeAction("test-addons");
+ UrlbarProviderQuickActions.removeAction("test-downloads");
+ });
+});
+
+add_task(async function basic() {
+ const testData = [
+ {
+ description: "Normal result to quick action button",
+ mousedown: ".urlbarView-row:nth-child(1) > .urlbarView-row-inner",
+ mouseup: ".urlbarView-quickaction-row[data-key=test-downloads]",
+ expected: "about:downloads",
+ },
+ {
+ description: "Normal result to out of result",
+ mousedown: ".urlbarView-row:nth-child(1) > .urlbarView-row-inner",
+ mouseup: "body",
+ expected: false,
+ },
+ {
+ description: "Quick action button to normal result",
+ mousedown: ".urlbarView-quickaction-row[data-key=test-addons]",
+ mouseup: ".urlbarView-row:nth-child(1)",
+ expected: "https://example.com/?q=test",
+ },
+ {
+ description: "Quick action button to quick action button",
+ mousedown: ".urlbarView-quickaction-row[data-key=test-addons]",
+ mouseup: ".urlbarView-quickaction-row[data-key=test-downloads]",
+ expected: "about:downloads",
+ },
+ {
+ description: "Quick action button to out of result",
+ mousedown: ".urlbarView-quickaction-row[data-key=test-downloads]",
+ mouseup: "body",
+ expected: false,
+ },
+ ];
+
+ for (const { description, mousedown, mouseup, expected } of testData) {
+ info(description);
+
+ await BrowserTestUtils.withNewTab("about:blank", async () => {
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ value: "test",
+ window,
+ });
+ let [downElement, upElement] = await waitForElements([
+ mousedown,
+ mouseup,
+ ]);
+
+ EventUtils.synthesizeMouseAtCenter(downElement, {
+ type: "mousedown",
+ });
+ Assert.ok(
+ downElement.hasAttribute("selected"),
+ "Mousedown element should be selected after mousedown"
+ );
+
+ EventUtils.synthesizeMouseAtCenter(upElement, { type: "mouseup" });
+ Assert.ok(
+ !downElement.hasAttribute("selected"),
+ "Mousedown element should not be selected after mouseup"
+ );
+ Assert.ok(
+ !upElement.hasAttribute("selected"),
+ "Mouseup element should not be selected after mouseup"
+ );
+
+ if (expected) {
+ await BrowserTestUtils.browserLoaded(
+ gBrowser.selectedBrowser,
+ false,
+ expected
+ );
+ Assert.ok(true, "Expected page is opened");
+ }
+ });
+ }
+});
+
+add_task(async function outOfBrowser() {
+ const testData = [
+ {
+ description: "Normal result to out of browser",
+ mousedown: ".urlbarView-row:nth-child(1) > .urlbarView-row-inner",
+ },
+ {
+ description: "Normal result to out of result",
+ mousedown: ".urlbarView-row:nth-child(1) > .urlbarView-row-inner",
+ expected: false,
+ },
+ {
+ description: "Quick action button to out of browser",
+ mousedown: ".urlbarView-quickaction-row[data-key=test-addons]",
+ },
+ ];
+
+ for (const { description, mousedown } of testData) {
+ info(description);
+
+ await BrowserTestUtils.withNewTab("about:blank", async () => {
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ value: "test",
+ window,
+ });
+ let [downElement] = await waitForElements([mousedown]);
+
+ EventUtils.synthesizeMouseAtCenter(downElement, {
+ type: "mousedown",
+ });
+ Assert.ok(
+ downElement.hasAttribute("selected"),
+ "Mousedown element should be selected after mousedown"
+ );
+
+ // Mouseup at out of browser.
+ EventUtils.synthesizeMouse(document.documentElement, -1, -1, {
+ type: "mouseup",
+ });
+
+ Assert.ok(
+ !downElement.hasAttribute("selected"),
+ "Mousedown element should not be selected after mouseup"
+ );
+ });
+ }
+});
+
+add_task(async function withSelectionByKeyboard() {
+ const testData = [
+ {
+ description: "Select normal result, then click on out of result",
+ mousedown: "body",
+ mouseup: "body",
+ expected: {
+ selectedElementByKey:
+ "#urlbar-results .urlbarView-row > .urlbarView-row-inner[selected]",
+ selectedElementAfterMouseDown:
+ "#urlbar-results .urlbarView-row > .urlbarView-row-inner[selected]",
+ actionedPage: false,
+ },
+ },
+ {
+ description: "Select quick action button, then click on out of result",
+ arrowDown: 1,
+ mousedown: "body",
+ mouseup: "body",
+ expected: {
+ selectedElementByKey:
+ "#urlbar-results .urlbarView-quickaction-row[selected]",
+ selectedElementAfterMouseDown:
+ "#urlbar-results .urlbarView-quickaction-row[selected]",
+ actionedPage: false,
+ },
+ },
+ {
+ description: "Select normal result, then click on about:downloads",
+ mousedown: ".urlbarView-quickaction-row[data-key=test-downloads]",
+ mouseup: ".urlbarView-quickaction-row[data-key=test-downloads]",
+ expected: {
+ selectedElementByKey:
+ "#urlbar-results .urlbarView-row > .urlbarView-row-inner[selected]",
+ selectedElementAfterMouseDown:
+ ".urlbarView-quickaction-row[data-key=test-downloads]",
+ actionedPage: "about:downloads",
+ },
+ },
+ ];
+
+ for (const {
+ description,
+ arrowDown,
+ mousedown,
+ mouseup,
+ expected,
+ } of testData) {
+ info(description);
+
+ await BrowserTestUtils.withNewTab("about:blank", async () => {
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ value: "test",
+ window,
+ });
+ let [downElement, upElement] = await waitForElements([
+ mousedown,
+ mouseup,
+ ]);
+
+ if (arrowDown) {
+ EventUtils.synthesizeKey(
+ "KEY_ArrowDown",
+ { repeat: arrowDown },
+ window
+ );
+ }
+
+ let [selectedElementByKey] = await waitForElements([
+ expected.selectedElementByKey,
+ ]);
+ Assert.ok(
+ selectedElementByKey.hasAttribute("selected"),
+ "selectedElementByKey should be selected after arrow down"
+ );
+
+ EventUtils.synthesizeMouseAtCenter(downElement, {
+ type: "mousedown",
+ });
+
+ if (
+ expected.selectedElementByKey !== expected.selectedElementAfterMouseDown
+ ) {
+ let [selectedElementAfterMouseDown] = await waitForElements([
+ expected.selectedElementAfterMouseDown,
+ ]);
+ Assert.ok(
+ selectedElementAfterMouseDown.hasAttribute("selected"),
+ "selectedElementAfterMouseDown should be selected after mousedown"
+ );
+ Assert.ok(
+ !selectedElementByKey.hasAttribute("selected"),
+ "selectedElementByKey should not be selected after mousedown"
+ );
+ }
+
+ EventUtils.synthesizeMouseAtCenter(upElement, {
+ type: "mouseup",
+ });
+
+ if (expected.actionedPage) {
+ Assert.ok(
+ !selectedElementByKey.hasAttribute("selected"),
+ "selectedElementByKey should not be selected after page starts load"
+ );
+ await BrowserTestUtils.browserLoaded(
+ gBrowser.selectedBrowser,
+ false,
+ expected.actionedPage
+ );
+ Assert.ok(true, "Expected page is opened");
+ } else {
+ Assert.ok(
+ selectedElementByKey.hasAttribute("selected"),
+ "selectedElementByKey should remain selected"
+ );
+ }
+ });
+ }
+});
+
+add_task(async function withDnsFirstForSingleWordsPref() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.fixup.dns_first_for_single_words", true]],
+ });
+
+ await PlacesUtils.bookmarks.insert({
+ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+ url: "https://example.org/",
+ title: "example",
+ });
+ await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
+
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ value: "ex",
+ window,
+ });
+
+ const details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
+ const target = details.element.action;
+ EventUtils.synthesizeMouseAtCenter(target, { type: "mousedown" });
+ const onLoaded = BrowserTestUtils.browserLoaded(
+ gBrowser.selectedBrowser,
+ false,
+ "https://example.org/"
+ );
+ EventUtils.synthesizeMouseAtCenter(target, { type: "mouseup" });
+ await onLoaded;
+ Assert.ok(true, "Expected page is opened");
+
+ await PlacesUtils.bookmarks.eraseEverything();
+ await SpecialPowers.popPrefEnv();
+});
+
+add_task(async function buttons() {
+ let initialTabUrl = "https://example.com/initial";
+ let mainResultUrl = "https://example.com/main";
+ let mainResultHelpUrl = "https://example.com/help";
+ let otherResultUrl = "https://example.com/other";
+
+ let searchString = "test";
+
+ // Add a provider with two results: The first has buttons and the second has a
+ // URL that should or shouldn't become the input's value when the block button
+ // in the first result is clicked, depending on the test.
+ let provider = new UrlbarTestUtils.TestProvider({
+ priority: Infinity,
+ results: [
+ new UrlbarResult(
+ UrlbarUtils.RESULT_TYPE.URL,
+ UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
+ {
+ url: mainResultUrl,
+ helpUrl: mainResultHelpUrl,
+ helpL10n: {
+ id: UrlbarPrefs.get("resultMenu")
+ ? "urlbar-result-menu-learn-more-about-firefox-suggest"
+ : "firefox-suggest-urlbar-learn-more",
+ },
+ isBlockable: true,
+ blockL10n: {
+ id: UrlbarPrefs.get("resultMenu")
+ ? "urlbar-result-menu-dismiss-firefox-suggest"
+ : "firefox-suggest-urlbar-block",
+ },
+ }
+ ),
+ new UrlbarResult(
+ UrlbarUtils.RESULT_TYPE.URL,
+ UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
+ {
+ url: otherResultUrl,
+ }
+ ),
+ ],
+ });
+
+ // Implement the provider's `onEngagement()` so it removes the result.
+ let onEngagementCallCount = 0;
+ provider.onEngagement = (isPrivate, state, queryContext, details) => {
+ onEngagementCallCount++;
+ queryContext.view.controller.removeResult(details.result);
+ };
+
+ UrlbarProvidersManager.registerProvider(provider);
+
+ let assertBlockResultCalled = () => {
+ Assert.equal(
+ onEngagementCallCount,
+ 1,
+ "blockResult() should have been called once"
+ );
+ onEngagementCallCount = 0;
+
+ let rowUrls = [];
+ let rows = UrlbarTestUtils.getResultsContainer(window).children;
+ for (let row of rows) {
+ rowUrls.push(row.result.payload.url);
+ }
+ Assert.ok(
+ !rowUrls.includes(mainResultUrl),
+ "The main result should not be in the view after blocking it: " +
+ JSON.stringify(rowUrls)
+ );
+ };
+ let assertResultMenuOpen = () => {
+ Assert.equal(
+ gURLBar.view.resultMenu.state,
+ "showing",
+ "Result menu is showing"
+ );
+ EventUtils.synthesizeKey("KEY_Escape");
+ };
+
+ let testData = [
+ {
+ description: UrlbarPrefs.get("resultMenu")
+ ? "Menu button to menu button"
+ : "Block button to block button",
+ mousedown: UrlbarPrefs.get("resultMenu")
+ ? ".urlbarView-row:nth-child(1) .urlbarView-button-menu"
+ : ".urlbarView-row:nth-child(1) .urlbarView-button-block",
+ afterMouseupCallback: UrlbarPrefs.get("resultMenu")
+ ? assertResultMenuOpen
+ : assertBlockResultCalled,
+ expected: {
+ mousedownSelected: false,
+ topSites: {
+ pageProxyState: "valid",
+ value: initialTabUrl,
+ },
+ searchString: {
+ pageProxyState: "invalid",
+ value: searchString,
+ },
+ },
+ },
+ {
+ skip: UrlbarPrefs.get("resultMenu"),
+ description: "Help button to help button",
+ mousedown: ".urlbarView-row:nth-child(1) .urlbarView-button-help",
+ expected: {
+ mousedownSelected: false,
+ url: mainResultHelpUrl,
+ newTab: true,
+ },
+ },
+ {
+ description: UrlbarPrefs.get("resultMenu")
+ ? "Row-inner to menu button"
+ : "Row-inner to block button",
+ mousedown: ".urlbarView-row:nth-child(1) > .urlbarView-row-inner",
+ mouseup: UrlbarPrefs.get("resultMenu")
+ ? ".urlbarView-row:nth-child(1) .urlbarView-button-menu"
+ : ".urlbarView-row:nth-child(1) .urlbarView-button-block",
+ afterMouseupCallback: UrlbarPrefs.get("resultMenu")
+ ? assertResultMenuOpen
+ : assertBlockResultCalled,
+ expected: {
+ mousedownSelected: true,
+ topSites: {
+ pageProxyState: "invalid",
+ value: UrlbarPrefs.get("resultMenu") ? initialTabUrl : otherResultUrl,
+ },
+ searchString: {
+ pageProxyState: "invalid",
+ value: UrlbarPrefs.get("resultMenu") ? searchString : otherResultUrl,
+ },
+ },
+ },
+ {
+ description: UrlbarPrefs.get("resultMenu")
+ ? "Menu button to row-inner"
+ : "Block button to row-inner",
+ mousedown: UrlbarPrefs.get("resultMenu")
+ ? ".urlbarView-row:nth-child(1) .urlbarView-button-menu"
+ : ".urlbarView-row:nth-child(1) .urlbarView-button-block",
+ mouseup: ".urlbarView-row:nth-child(1) > .urlbarView-row-inner",
+ expected: {
+ mousedownSelected: false,
+ url: mainResultUrl,
+ newTab: false,
+ },
+ },
+ ];
+
+ for (let showTopSites of [true, false]) {
+ for (let {
+ description,
+ mousedown,
+ mouseup,
+ expected,
+ afterMouseupCallback = null,
+ skip = false,
+ } of testData) {
+ if (skip) {
+ info(
+ `Skipping test with showTopSites = ${showTopSites}: ${description}`
+ );
+ continue;
+ }
+ info(`Running test with showTopSites = ${showTopSites}: ${description}`);
+ mouseup ||= mousedown;
+
+ await BrowserTestUtils.withNewTab(initialTabUrl, async () => {
+ Assert.equal(
+ gURLBar.getAttribute("pageproxystate"),
+ "valid",
+ "Sanity check: pageproxystate should be valid initially"
+ );
+ Assert.equal(
+ gURLBar.value,
+ initialTabUrl,
+ "Sanity check: input.value should be the initial URL initially"
+ );
+
+ if (showTopSites) {
+ // Open the view and show top sites by performing the accel+L command.
+ await SimpleTest.promiseFocus(window);
+ let searchPromise = UrlbarTestUtils.promiseSearchComplete(window);
+ document.getElementById("Browser:OpenLocation").doCommand();
+ await searchPromise;
+ } else {
+ // Do a search.
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: searchString,
+ });
+ }
+
+ let [downElement, upElement] = await waitForElements([
+ mousedown,
+ mouseup,
+ ]);
+
+ // Mousedown and check the selection.
+ EventUtils.synthesizeMouseAtCenter(downElement, {
+ type: "mousedown",
+ });
+ if (expected.mousedownSelected) {
+ Assert.ok(
+ downElement.hasAttribute("selected"),
+ "Mousedown element should be selected after mousedown"
+ );
+ } else {
+ Assert.ok(
+ !downElement.hasAttribute("selected"),
+ "Mousedown element should not be selected after mousedown"
+ );
+ }
+
+ let loadPromise;
+ if (expected.url) {
+ loadPromise = expected.newTab
+ ? BrowserTestUtils.waitForNewTab(gBrowser, expected.url)
+ : BrowserTestUtils.browserLoaded(
+ gBrowser.selectedBrowser,
+ null,
+ expected.url
+ );
+ }
+
+ // Mouseup and check the selection.
+ EventUtils.synthesizeMouseAtCenter(upElement, { type: "mouseup" });
+ Assert.ok(
+ !downElement.hasAttribute("selected"),
+ "Mousedown element should not be selected after mouseup"
+ );
+ Assert.ok(
+ !upElement.hasAttribute("selected"),
+ "Mouseup element should not be selected after mouseup"
+ );
+
+ // If we expect a URL to load, we're done since the view will close and
+ // the input value will be set to the URL.
+ if (loadPromise) {
+ info("Waiting for URL to load: " + expected.url);
+ let tab = await loadPromise;
+ Assert.ok(true, "Expected URL loaded");
+ if (expected.newTab) {
+ BrowserTestUtils.removeTab(tab);
+ }
+ return;
+ }
+
+ if (afterMouseupCallback) {
+ await afterMouseupCallback();
+ }
+
+ let state = showTopSites ? expected.topSites : expected.searchString;
+ Assert.equal(
+ gURLBar.getAttribute("pageproxystate"),
+ state.pageProxyState,
+ "pageproxystate should be as expected"
+ );
+ Assert.equal(
+ gURLBar.value,
+ state.value,
+ "input.value should be as expected"
+ );
+ });
+ }
+ }
+
+ UrlbarProvidersManager.unregisterProvider(provider);
+});
+
+async function waitForElements(selectors) {
+ let elements;
+ await BrowserTestUtils.waitForCondition(() => {
+ elements = selectors.map(s => document.querySelector(s));
+ return elements.every(e => e && BrowserTestUtils.is_visible(e));
+ }, "Waiting for elements to become visible: " + JSON.stringify(selectors));
+ return elements;
+}