From c0f21b484cf4a4a86056f653a891201edea90144 Mon Sep 17 00:00:00 2001 From: Alex Kontos Date: Tue, 5 Aug 2025 15:35:25 +0100 Subject: [PATCH] feat: add double-click select all behavior to urlbar --- .../components/search/content/searchbar.js | 10 ++++- browser/components/urlbar/UrlbarInput.sys.mjs | 19 +++++--- browser/components/urlbar/UrlbarPrefs.sys.mjs | 10 +++++ .../browser/browser_doubleClickSelectsAll.js | 45 +++++++++++++++++++ waterfox/browser/app/profile/00-general.js | 19 ++++++++ 5 files changed, 96 insertions(+), 7 deletions(-) create mode 100644 browser/components/urlbar/tests/browser/browser_doubleClickSelectsAll.js diff --git a/browser/components/search/content/searchbar.js b/browser/components/search/content/searchbar.js index e0db48bf0be8..0c44298e992b 100644 --- a/browser/components/search/content/searchbar.js +++ b/browser/components/search/content/searchbar.js @@ -532,15 +532,16 @@ /** * Determines if we should select all the text in the searchbar based on the - * searchbar state, and whether the selection is empty. + * clickSelectsAll pref, searchbar state, and whether the selection is empty. */ _maybeSelectAll() { if ( !this._preventClickSelectsAll && + UrlbarPrefs.get("clickSelectsAll") && document.activeElement == this._textbox && this._textbox.selectionStart == this._textbox.selectionEnd ) { - this.select(); + this._textbox.editor.selectAll(); } } @@ -647,6 +648,11 @@ // is text in the textbox. this.openSuggestionsPanel(true); } + + if (event.detail == 2 && UrlbarPrefs.get("doubleClickSelectsAll")) { + this._textbox.editor.selectAll(); + event.preventDefault(); + } }); } diff --git a/browser/components/urlbar/UrlbarInput.sys.mjs b/browser/components/urlbar/UrlbarInput.sys.mjs index cbe8cc6d94e9..483cef4324eb 100644 --- a/browser/components/urlbar/UrlbarInput.sys.mjs +++ b/browser/components/urlbar/UrlbarInput.sys.mjs @@ -4077,11 +4077,15 @@ export class UrlbarInput { /** * Determines if we should select all the text in the Urlbar based on the - * Urlbar state, and whether the selection is empty. + * clickSelectsAll pref, Urlbar state, and whether the selection is empty. + * + * @param {boolean} [ignoreClickSelectsAllPref] + * If true, the browser.urlbar.clickSelectsAll pref will be ignored. */ - _maybeSelectAll() { + _maybeSelectAll(ignoreClickSelectsAllPref = false) { if ( !this._preventClickSelectsAll && + (ignoreClickSelectsAllPref || lazy.UrlbarPrefs.get("clickSelectsAll")) && this._compositionState != lazy.UrlbarUtils.COMPOSITION.COMPOSING && this.focused && this.inputField.selectionStart == this.inputField.selectionEnd @@ -4214,7 +4218,9 @@ export class UrlbarInput { return; } - this._maybeSelectAll(); + // If the user right clicks, we select all regardless of the value of + // the browser.urlbar.clickSelectsAll pref. + this._maybeSelectAll(/* ignoreClickSelectsAllPref */ event.button == 2); } _on_focus(event) { @@ -4263,7 +4269,7 @@ export class UrlbarInput { } if (this.inputField.hasAttribute("refocused-by-panel")) { - this._maybeSelectAll(); + this._maybeSelectAll(true); } } @@ -4325,7 +4331,10 @@ export class UrlbarInput { this.inputField.setSelectionRange(0, 0); } - if (event.target.classList.contains(SEARCH_BUTTON_CLASS)) { + if (event.detail == 2 && lazy.UrlbarPrefs.get("doubleClickSelectsAll")) { + this.editor.selectAll(); + event.preventDefault(); + } else if (event.target.classList.contains(SEARCH_BUTTON_CLASS)) { this._preventClickSelectsAll = true; this.search(lazy.UrlbarTokenizer.RESTRICT.SEARCH); } else { diff --git a/browser/components/urlbar/UrlbarPrefs.sys.mjs b/browser/components/urlbar/UrlbarPrefs.sys.mjs index 1c47a5000bbc..95d56e6c5637 100644 --- a/browser/components/urlbar/UrlbarPrefs.sys.mjs +++ b/browser/components/urlbar/UrlbarPrefs.sys.mjs @@ -75,6 +75,11 @@ const PREF_URLBAR_DEFAULTS = new Map([ // active view if the the view utilizes OpenSearch. ["contextualSearch.enabled", true], + // If true, this optimizes for replacing the full URL rather than editing + // part of it. This also copies the urlbar value to the selection clipboard + // on systems that support it. + ["clickSelectsAll", false], + // Whether using `ctrl` when hitting return/enter in the URL bar // (or clicking 'go') should prefix 'www.' and suffix // browser.fixup.alternate.suffix to the URL bar value prior to @@ -90,6 +95,11 @@ const PREF_URLBAR_DEFAULTS = new Map([ // "heuristic" result). We fetch it as fast as possible. ["delay", 50], + // If true, this optimizes for replacing the full URL rather than selecting a + // portion of it. This also copies the urlbar value to the selection + // clipboard on systems that support it. + ["doubleClickSelectsAll", false], + // Ensure we use trailing dots for DNS lookups for single words that could // be hosts. ["dnsResolveFullyQualifiedNames", true], diff --git a/browser/components/urlbar/tests/browser/browser_doubleClickSelectsAll.js b/browser/components/urlbar/tests/browser/browser_doubleClickSelectsAll.js new file mode 100644 index 000000000000..2cd2e7802d61 --- /dev/null +++ b/browser/components/urlbar/tests/browser/browser_doubleClickSelectsAll.js @@ -0,0 +1,45 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + function doubleClick(target) { + let promise = BrowserTestUtils.waitForEvent(target, "dblclick"); + EventUtils.synthesizeMouseAtCenter( + target, + { clickCount: 1 }, + target.ownerGlobal + ); + EventUtils.synthesizeMouseAtCenter( + target, + { clickCount: 2 }, + target.ownerGlobal + ); + return promise; + } + + add_task(async function() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.urlbar.clickSelectsAll", false], + ["browser.urlbar.doubleClickSelectsAll", true], + ], + }); + + let url = "about:mozilla"; + let win = await BrowserTestUtils.openNewBrowserWindow(); + await BrowserTestUtils.openNewForegroundTab({ gBrowser: win.gBrowser, url }); + + await doubleClick(win.gURLBar.inputField); + is( + win.gURLBar.selectionStart, + 0, + "Selection should start at the beginning of the urlbar value" + ); + is( + win.gURLBar.selectionEnd, + url.length, + "Selection should end at the end of the urlbar value" + ); + + win.close(); + }); \ No newline at end of file diff --git a/waterfox/browser/app/profile/00-general.js b/waterfox/browser/app/profile/00-general.js index 2a83578d0d6f..cae7efa38370 100644 --- a/waterfox/browser/app/profile/00-general.js +++ b/waterfox/browser/app/profile/00-general.js @@ -40,6 +40,25 @@ pref("browser.uiCustomization.state", "{\"placements\":{\"widget-overflow-fixed- // Alternative smooth scroll physics. ("MSD" = Mass-Spring-Damper) pref("general.smoothScroll.msdPhysics.enabled", true); +// --- URL Bar Behavior --- +// Platform-specific settings for how clicks interact with the URL bar. +#ifdef UNIX_BUT_NOT_MAC +// On Linux (excluding macOS), a single click does not select all text in the URL bar. +pref("browser.urlbar.clickSelectsAll", false); +// On Linux (excluding macOS), a double click selects all text. +pref("browser.urlbar.doubleClickSelectsAll", true); +#else +// On other operating systems (Windows, macOS), a single click selects all text. +pref("browser.urlbar.clickSelectsAll", true); +// On other operating systems, double click behavior might be different or not specifically set to select all. +pref("browser.urlbar.doubleClickSelectsAll", false); +#endif + +#ifdef XP_MACOSX +// Whether to disable treating ctrl click as right click +pref("dom.event.treat_ctrl_click_as_right_click.disabled", true); +#endif + // --- Top Sites and Partner Integrations --- // Settings related to "Top Sites" on the New Tab Page and partner integrations. pref("browser.partnerlink.attributionURL", "", locked); // URL for partner attribution (locked).