From f0eef2cdca7f67a6a2c8112684df3cee94c4afe1 Mon Sep 17 00:00:00 2001 From: Daisuke Akatsuka Date: Thu, 23 Mar 2023 00:44:47 +0000 Subject: [PATCH] Bug 610357: Show pending URL in location bar and tab while loading. r=mak,Gijs Differential Revision: https://phabricator.services.mozilla.com/D170045 --- browser/base/content/tabbrowser.js | 22 + browser/base/content/test/tabs/browser.ini | 5 + ...ser_link_in_tab_title_and_url_prefilled.js | 787 ++++++++++++++++++ .../link_in_tab_title_and_url_prefilled.html | 30 + .../content/test/tabs/request-timeout.sjs | 8 + browser/base/content/test/tabs/wait-a-bit.sjs | 23 + browser/components/urlbar/UrlbarInput.sys.mjs | 3 + 7 files changed, 878 insertions(+) create mode 100644 browser/base/content/test/tabs/browser_link_in_tab_title_and_url_prefilled.js create mode 100644 browser/base/content/test/tabs/link_in_tab_title_and_url_prefilled.html create mode 100644 browser/base/content/test/tabs/request-timeout.sjs create mode 100644 browser/base/content/test/tabs/wait-a-bit.sjs diff --git a/browser/base/content/tabbrowser.js b/browser/base/content/tabbrowser.js index d705b48567fd..ef197085e349 100644 --- a/browser/base/content/tabbrowser.js +++ b/browser/base/content/tabbrowser.js @@ -6635,6 +6635,28 @@ // created or re-created browser, e.g. because it just switched // remoteness or is a new tab/window). this.mBrowser.urlbarChangeTracker.startedLoad(); + + // To improve the user experience and perceived performance when + // opening links in new tabs, we show the url and tab title sooner, + // but only if it's safe (from a phishing point of view) to do so, + // thus there's no session history and the load starts from a + // non-web-controlled blank page. + if ( + !this.mBrowser.browsingContext.sessionHistory.count && + BrowserUIUtils.checkEmptyPageOrigin( + this.mBrowser, + originalLocation + ) + ) { + gBrowser.setInitialTabTitle(this.mTab, originalLocation.spec, { + isURL: true, + }); + + this.mBrowser._initialURI = originalLocation; + if (this.mTab.selected && !gBrowser.userTypedValue) { + gURLBar.setURI(); + } + } } delete this.mBrowser.initialPageLoadedFromUserAction; // If the browser is loading it must not be crashed anymore diff --git a/browser/base/content/test/tabs/browser.ini b/browser/base/content/test/tabs/browser.ini index 986f577a2b96..ee267c01b7e5 100644 --- a/browser/base/content/test/tabs/browser.ini +++ b/browser/base/content/test/tabs/browser.ini @@ -48,6 +48,11 @@ skip-if = debug # Bug 1444565, Bug 1457887 support-files = tab_that_closes.html [browser_hiddentab_contextmenu.js] [browser_lazy_tab_browser_events.js] +[browser_link_in_tab_title_and_url_prefilled.js] +support-files = + link_in_tab_title_and_url_prefilled.html + request-timeout.sjs + wait-a-bit.sjs [browser_long_data_url_label_truncation.js] [browser_multiselect_tabs_active_tab_selected_by_default.js] [browser_multiselect_tabs_bookmark.js] diff --git a/browser/base/content/test/tabs/browser_link_in_tab_title_and_url_prefilled.js b/browser/base/content/test/tabs/browser_link_in_tab_title_and_url_prefilled.js new file mode 100644 index 000000000000..3647edd68749 --- /dev/null +++ b/browser/base/content/test/tabs/browser_link_in_tab_title_and_url_prefilled.js @@ -0,0 +1,787 @@ +/* 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/. */ + +const { sinon } = ChromeUtils.import("resource://testing-common/Sinon.jsm"); + +const TEST_ROOT = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" +); +const HOME_URL = `${TEST_ROOT}link_in_tab_title_and_url_prefilled.html`; +const HOME_TITLE = HOME_URL.substring("https://".length); +const WAIT_A_BIT_URL = `${TEST_ROOT}wait-a-bit.sjs`; +const WAIT_A_BIT_LOADING_TITLE = WAIT_A_BIT_URL.substring("https://".length); +const WAIT_A_BIT_PAGE_TITLE = "wait a bit"; +const REQUEST_TIMEOUT_URL = `${TEST_ROOT}request-timeout.sjs`; +const REQUEST_TIMEOUT_LOADING_TITLE = REQUEST_TIMEOUT_URL.substring( + "https://".length +); +const BLANK_URL = "about:blank"; +const BLANK_TITLE = "New Tab"; + +const OPEN_BY = { + CLICK: "click", + CONTEXT_MENU: "context_menu", +}; + +const OPEN_AS = { + FOREGROUND: "foreground", + BACKGROUND: "background", +}; + +add_task(async function normal_page__blank_target__foreground__click() { + await doTestInSameWindow({ + link: "wait-a-bit--blank-target", + openBy: OPEN_BY.CLICK, + openAs: OPEN_AS.FOREGROUND, + loadingState: { + tab: WAIT_A_BIT_LOADING_TITLE, + urlbar: WAIT_A_BIT_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Wait until loading the link target"); + await onTabLoaded; + }, + finalState: { + tab: WAIT_A_BIT_PAGE_TITLE, + urlbar: WAIT_A_BIT_URL, + history: [WAIT_A_BIT_URL], + }, + }); +}); + +add_task(async function normal_page__blank_target__foreground__contextmenu() { + await doTestInSameWindow({ + link: "wait-a-bit--blank-target", + openBy: OPEN_BY.CONTEXT_MENU, + openAs: OPEN_AS.FOREGROUND, + loadingState: { + tab: WAIT_A_BIT_LOADING_TITLE, + urlbar: WAIT_A_BIT_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Wait until loading the link target"); + await onTabLoaded; + }, + finalState: { + tab: WAIT_A_BIT_PAGE_TITLE, + urlbar: WAIT_A_BIT_URL, + history: [WAIT_A_BIT_URL], + }, + }); +}); + +add_task(async function normal_page__blank_target__foreground__abort() { + await doTestInSameWindow({ + link: "wait-a-bit--blank-target", + openBy: OPEN_BY.CLICK, + openAs: OPEN_AS.FOREGROUND, + loadingState: { + tab: WAIT_A_BIT_LOADING_TITLE, + urlbar: WAIT_A_BIT_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Abort loading"); + document.getElementById("stop-button").click(); + }, + finalState: { + tab: WAIT_A_BIT_LOADING_TITLE, + urlbar: WAIT_A_BIT_URL, + history: [], + }, + }); +}); + +add_task(async function normal_page__blank_target__foreground__timeout() { + await doTestInSameWindow({ + link: "request-timeout--blank-target", + openBy: OPEN_BY.CLICK, + openAs: OPEN_AS.FOREGROUND, + loadingState: { + tab: REQUEST_TIMEOUT_LOADING_TITLE, + urlbar: REQUEST_TIMEOUT_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Wait until loading the link target"); + await onTabLoaded; + }, + finalState: { + tab: REQUEST_TIMEOUT_LOADING_TITLE, + urlbar: REQUEST_TIMEOUT_URL, + history: [REQUEST_TIMEOUT_URL], + }, + }); +}); + +add_task(async function normal_page__blank_target__background__click() { + await doTestInSameWindow({ + link: "wait-a-bit--blank-target", + openBy: OPEN_BY.CLICK, + openAs: OPEN_AS.BACKGROUND, + loadingState: { + tab: WAIT_A_BIT_LOADING_TITLE, + urlbar: HOME_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Wait until loading the link target"); + await onTabLoaded; + }, + finalState: { + tab: WAIT_A_BIT_PAGE_TITLE, + urlbar: HOME_URL, + history: [WAIT_A_BIT_URL], + }, + }); +}); + +add_task(async function normal_page__blank_target__background__contextmenu() { + await doTestInSameWindow({ + link: "wait-a-bit--blank-target", + openBy: OPEN_BY.CONTEXT_MENU, + openAs: OPEN_AS.BACKGROUND, + loadingState: { + tab: WAIT_A_BIT_LOADING_TITLE, + urlbar: HOME_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Wait until loading the link target"); + await onTabLoaded; + }, + finalState: { + tab: WAIT_A_BIT_PAGE_TITLE, + urlbar: HOME_URL, + history: [WAIT_A_BIT_URL], + }, + }); +}); + +add_task(async function normal_page__blank_target__background__abort() { + await doTestInSameWindow({ + link: "wait-a-bit--blank-target", + openBy: OPEN_BY.CLICK, + openAs: OPEN_AS.BACKGROUND, + loadingState: { + tab: WAIT_A_BIT_LOADING_TITLE, + urlbar: HOME_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Abort loading"); + document.getElementById("stop-button").click(); + }, + finalState: { + tab: WAIT_A_BIT_LOADING_TITLE, + urlbar: HOME_URL, + history: [], + }, + }); +}); + +add_task(async function normal_page__blank_target__background__timeout() { + await doTestInSameWindow({ + link: "request-timeout--blank-target", + openBy: OPEN_BY.CLICK, + openAs: OPEN_AS.BACKGROUND, + loadingState: { + tab: REQUEST_TIMEOUT_LOADING_TITLE, + urlbar: HOME_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Wait until loading the link target"); + await onTabLoaded; + }, + finalState: { + tab: REQUEST_TIMEOUT_LOADING_TITLE, + urlbar: HOME_URL, + history: [REQUEST_TIMEOUT_URL], + }, + }); +}); + +add_task(async function normal_page__other_target__foreground() { + await doTestInSameWindow({ + link: "wait-a-bit--other-target", + openBy: OPEN_BY.CLICK, + openAs: OPEN_AS.FOREGROUND, + loadingState: { + tab: BLANK_TITLE, + urlbar: BLANK_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Wait until loading the link target"); + await onTabLoaded; + }, + finalState: { + tab: WAIT_A_BIT_PAGE_TITLE, + urlbar: WAIT_A_BIT_URL, + history: [WAIT_A_BIT_URL], + }, + }); +}); + +add_task(async function normal_page__other_target__foreground__abort() { + await doTestInSameWindow({ + link: "wait-a-bit--other-target", + openBy: OPEN_BY.CLICK, + openAs: OPEN_AS.FOREGROUND, + loadingState: { + tab: BLANK_TITLE, + urlbar: BLANK_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Abort loading"); + document.getElementById("stop-button").click(); + }, + finalState: { + tab: BLANK_TITLE, + urlbar: BLANK_URL, + history: [], + }, + }); +}); + +add_task(async function normal_page__other_target__foreground__timeout() { + await doTestInSameWindow({ + link: "request-timeout--other-target", + openBy: OPEN_BY.CLICK, + openAs: OPEN_AS.FOREGROUND, + loadingState: { + tab: BLANK_TITLE, + urlbar: BLANK_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Wait until loading the link target"); + await onTabLoaded; + }, + finalState: { + tab: REQUEST_TIMEOUT_LOADING_TITLE, + urlbar: REQUEST_TIMEOUT_URL, + history: [REQUEST_TIMEOUT_URL], + }, + }); +}); + +add_task(async function normal_page__other_target__background() { + await doTestInSameWindow({ + link: "wait-a-bit--other-target", + openBy: OPEN_BY.CLICK, + openAs: OPEN_AS.BACKGROUND, + loadingState: { + tab: WAIT_A_BIT_LOADING_TITLE, + urlbar: HOME_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Wait until loading the link target"); + await onTabLoaded; + }, + finalState: { + tab: WAIT_A_BIT_PAGE_TITLE, + urlbar: HOME_URL, + history: [WAIT_A_BIT_URL], + }, + }); +}); + +add_task(async function normal_page__other_target__background__abort() { + await doTestInSameWindow({ + link: "wait-a-bit--other-target", + openBy: OPEN_BY.CLICK, + openAs: OPEN_AS.BACKGROUND, + loadingState: { + tab: WAIT_A_BIT_LOADING_TITLE, + urlbar: HOME_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Abort loading"); + document.getElementById("stop-button").click(); + }, + finalState: { + tab: WAIT_A_BIT_LOADING_TITLE, + urlbar: HOME_URL, + history: [], + }, + }); +}); + +add_task(async function normal_page__other_target__background__timeout() { + await doTestInSameWindow({ + link: "request-timeout--other-target", + openBy: OPEN_BY.CLICK, + openAs: OPEN_AS.BACKGROUND, + loadingState: { + tab: REQUEST_TIMEOUT_LOADING_TITLE, + urlbar: HOME_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Wait until loading the link target"); + await onTabLoaded; + }, + finalState: { + tab: REQUEST_TIMEOUT_LOADING_TITLE, + urlbar: HOME_URL, + history: [REQUEST_TIMEOUT_URL], + }, + }); +}); + +add_task(async function normal_page__by_script() { + await doTestInSameWindow({ + link: "wait-a-bit--by-script", + openBy: OPEN_BY.CLICK, + openAs: OPEN_AS.FOREGROUND, + loadingState: { + tab: BLANK_TITLE, + urlbar: BLANK_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Wait until loading the link target"); + await onTabLoaded; + }, + finalState: { + tab: WAIT_A_BIT_PAGE_TITLE, + urlbar: WAIT_A_BIT_URL, + history: [WAIT_A_BIT_URL], + }, + }); +}); + +add_task(async function normal_page__by_script__abort() { + await doTestInSameWindow({ + link: "wait-a-bit--by-script", + openBy: OPEN_BY.CLICK, + openAs: OPEN_AS.FOREGROUND, + loadingState: { + tab: BLANK_TITLE, + urlbar: BLANK_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Abort loading"); + document.getElementById("stop-button").click(); + }, + finalState: { + tab: BLANK_TITLE, + urlbar: BLANK_URL, + history: [], + }, + }); +}); + +add_task(async function normal_page__by_script__timeout() { + await doTestInSameWindow({ + link: "request-timeout--by-script", + openBy: OPEN_BY.CLICK, + openAs: OPEN_AS.FOREGROUND, + loadingState: { + tab: BLANK_TITLE, + urlbar: BLANK_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Wait until loading the link target"); + await onTabLoaded; + }, + finalState: { + tab: REQUEST_TIMEOUT_LOADING_TITLE, + urlbar: REQUEST_TIMEOUT_URL, + history: [REQUEST_TIMEOUT_URL], + }, + }); +}); + +add_task(async function normal_page__no_target() { + await doTestInSameWindow({ + link: "wait-a-bit--no-target", + openBy: OPEN_BY.CLICK, + openAs: OPEN_AS.FOREGROUND, + loadingState: { + // Inherit the title and URL until finishing loading a new link when the + // link is opened in same tab. + tab: HOME_TITLE, + urlbar: HOME_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Wait until loading the link target"); + await onTabLoaded; + }, + finalState: { + tab: WAIT_A_BIT_PAGE_TITLE, + urlbar: WAIT_A_BIT_URL, + history: [HOME_URL, WAIT_A_BIT_URL], + }, + }); +}); + +add_task(async function normal_page__no_target__abort() { + await doTestInSameWindow({ + link: "wait-a-bit--no-target", + openBy: OPEN_BY.CLICK, + openAs: OPEN_AS.FOREGROUND, + loadingState: { + tab: HOME_TITLE, + urlbar: HOME_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Abort loading"); + document.getElementById("stop-button").click(); + }, + finalState: { + tab: HOME_TITLE, + urlbar: HOME_URL, + history: [HOME_URL], + }, + }); +}); + +add_task(async function normal_page__no_target__timeout() { + await doTestInSameWindow({ + link: "request-timeout--no-target", + openBy: OPEN_BY.CLICK, + openAs: OPEN_AS.FOREGROUND, + loadingState: { + tab: HOME_TITLE, + urlbar: HOME_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Wait until loading the link target"); + await onTabLoaded; + }, + finalState: { + tab: REQUEST_TIMEOUT_LOADING_TITLE, + urlbar: REQUEST_TIMEOUT_URL, + history: [HOME_URL, REQUEST_TIMEOUT_URL], + }, + }); +}); + +add_task(async function blank_page__blank_target__foreground() { + await doTestInSameWindow({ + link: "blank-page--blank-target", + openBy: OPEN_BY.CLICK, + openAs: OPEN_AS.FOREGROUND, + loadingState: { + tab: BLANK_TITLE, + urlbar: "", + }, + async actionWhileLoading(onTabLoaded) { + info("Wait until loading the link target"); + await onTabLoaded; + }, + finalState: { + tab: BLANK_TITLE, + urlbar: "", + history: [BLANK_URL], + }, + }); +}); + +add_task(async function blank_page__blank_target__background() { + await doTestInSameWindow({ + link: "blank-page--blank-target", + openBy: OPEN_BY.CLICK, + openAs: OPEN_AS.BACKGROUND, + loadingState: { + tab: BLANK_TITLE, + urlbar: HOME_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Wait until loading the link target"); + await onTabLoaded; + }, + finalState: { + tab: BLANK_TITLE, + urlbar: HOME_URL, + history: [BLANK_URL], + }, + }); +}); + +add_task(async function blank_page__other_target__foreground() { + await doTestInSameWindow({ + link: "blank-page--other-target", + openBy: OPEN_BY.CLICK, + openAs: OPEN_AS.FOREGROUND, + loadingState: { + tab: BLANK_TITLE, + urlbar: BLANK_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Wait until loading the link target"); + await onTabLoaded; + }, + finalState: { + tab: BLANK_TITLE, + urlbar: BLANK_URL, + history: [BLANK_URL], + }, + }); +}); + +add_task(async function blank_page__other_target__background() { + await doTestInSameWindow({ + link: "blank-page--other-target", + openBy: OPEN_BY.CLICK, + openAs: OPEN_AS.BACKGROUND, + loadingState: { + tab: BLANK_TITLE, + urlbar: HOME_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Wait until loading the link target"); + await onTabLoaded; + }, + finalState: { + tab: BLANK_TITLE, + urlbar: HOME_URL, + history: [BLANK_URL], + }, + }); +}); + +add_task(async function blank_page__by_script() { + await doTestInSameWindow({ + link: "blank-page--by-script", + openBy: OPEN_BY.CLICK, + openAs: OPEN_AS.FOREGROUND, + loadingState: { + tab: BLANK_TITLE, + urlbar: BLANK_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Wait until loading the link target"); + await onTabLoaded; + }, + finalState: { + tab: BLANK_TITLE, + urlbar: BLANK_URL, + history: [BLANK_URL], + }, + }); +}); + +add_task(async function blank_page__no_target() { + await doTestInSameWindow({ + link: "blank-page--no-target", + openBy: OPEN_BY.CLICK, + openAs: OPEN_AS.FOREGROUND, + loadingState: { + // Inherit the title and URL until finishing loading a new link when the + // link is opened in same tab. + tab: HOME_TITLE, + urlbar: HOME_URL, + }, + async actionWhileLoading(onTabLoaded) { + info("Wait until loading the link target"); + await onTabLoaded; + }, + finalState: { + tab: BLANK_TITLE, + urlbar: BLANK_URL, + history: [HOME_URL, BLANK_URL], + }, + }); +}); + +add_task(async function normal_page__blank_target__in_new_window() { + await doTestWithNewWindow({ + link: "wait-a-bit--blank-target", + expectedSetURICalled: true, + }); +}); + +add_task(async function normal_page__other_target__in_new_window() { + await doTestWithNewWindow({ + link: "wait-a-bit--other-target", + expectedSetURICalled: false, + }); +}); + +add_task(async function normal_page__by_script__in_new_window() { + await doTestWithNewWindow({ + link: "wait-a-bit--by-script", + expectedSetURICalled: false, + }); +}); + +add_task(async function blank_page__blank_target__in_new_window() { + await doTestWithNewWindow({ + link: "blank-page--blank-target", + expectedSetURICalled: false, + }); +}); + +add_task(async function blank_page__other_target__in_new_window() { + await doTestWithNewWindow({ + link: "blank-page--other-target", + expectedSetURICalled: false, + }); +}); + +add_task(async function blank_page__by_script__in_new_window() { + await doTestWithNewWindow({ + link: "blank-page--by-script", + expectedSetURICalled: false, + }); +}); + +async function doTestInSameWindow({ + link, + openBy, + openAs, + loadingState, + actionWhileLoading, + finalState, +}) { + await BrowserTestUtils.withNewTab("about:blank", async browser => { + // NOTE: The behavior after the click link + // (no target) is different when the URL is opened directly with + // BrowserTestUtils.withNewTab() and when it is loaded later. + // Specifically, if we load `about:blank`, expect to see `New Tab` as the + // title of the tab, but the former will continue to display the URL that + // was previously displayed. Therefore, use the latter way. + BrowserTestUtils.loadURIString(browser, HOME_URL); + await BrowserTestUtils.browserLoaded( + gBrowser.selectedBrowser, + false, + HOME_URL + ); + + const onLoadStarted = new Promise(resolve => + gBrowser.addTabsProgressListener({ + onStateChange(aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) { + if (aStateFlags & Ci.nsIWebProgressListener.STATE_START) { + gBrowser.removeTabsProgressListener(this); + resolve(gBrowser.getTabForBrowser(aBrowser)); + } + }, + }) + ); + + info(`Open link for ${link} by ${openBy} as ${openAs}`); + const href = await openLink(browser, link, openBy, openAs); + + info("Wait until starting to load in the target tab"); + const target = await onLoadStarted; + Assert.equal(target.selected, openAs === OPEN_AS.FOREGROUND); + Assert.equal(gURLBar.value, loadingState.urlbar); + Assert.equal(target.textLabel.textContent, loadingState.tab); + + await actionWhileLoading( + BrowserTestUtils.browserLoaded(target.linkedBrowser, false, href) + ); + + info("Check the final result"); + Assert.equal(gURLBar.value, finalState.urlbar); + Assert.equal(target.textLabel.textContent, finalState.tab); + const sessionHistory = await new Promise(r => + SessionStore.getSessionHistory(target, r) + ); + Assert.deepEqual( + sessionHistory.entries.map(e => e.url), + finalState.history + ); + + BrowserTestUtils.removeTab(target); + }); +} + +async function doTestWithNewWindow({ link, expectedSetURICalled }) { + await SpecialPowers.pushPrefEnv({ + set: [["browser.link.open_newwindow", 2]], + }); + + await BrowserTestUtils.withNewTab(HOME_URL, async browser => { + const onNewWindowOpened = BrowserTestUtils.domWindowOpenedAndLoaded(); + + info(`Open link for ${link}`); + const href = await openLink( + browser, + link, + OPEN_BY.CLICK, + OPEN_AS.FOREGROUND + ); + + info("Wait until opening a new window"); + const win = await onNewWindowOpened; + + info("Check whether gURLBar.setURI is called while loading the page"); + const sandbox = sinon.createSandbox(); + registerCleanupFunction(() => { + sandbox.restore(); + }); + let isSetURIWhileLoading = false; + sandbox.stub(win.gURLBar, "setURI").callsFake(uri => { + if (!uri && win.gBrowser.selectedBrowser._initialURI) { + isSetURIWhileLoading = true; + } + }); + await BrowserTestUtils.browserLoaded( + win.gBrowser.selectedBrowser, + false, + href + ); + sandbox.restore(); + + Assert.equal(isSetURIWhileLoading, expectedSetURICalled); + Assert.equal( + !!win.gBrowser.selectedBrowser._initialURI, + expectedSetURICalled + ); + + await BrowserTestUtils.closeWindow(win); + }); + + await SpecialPowers.popPrefEnv(); +} + +async function openLink(browser, link, openBy, openAs) { + let href; + const openAsBackground = openAs === OPEN_AS.BACKGROUND; + if (openBy === OPEN_BY.CLICK) { + href = await synthesizeMouse(browser, link, { + ctrlKey: openAsBackground, + metaKey: openAsBackground, + }); + } else if (openBy === OPEN_BY.CONTEXT_MENU) { + await SpecialPowers.pushPrefEnv({ + set: [["browser.tabs.loadInBackground", openAsBackground]], + }); + + const contextMenu = document.getElementById("contentAreaContextMenu"); + const onPopupShown = BrowserTestUtils.waitForEvent( + contextMenu, + "popupshown" + ); + + href = await synthesizeMouse(browser, link, { + type: "contextmenu", + button: 2, + }); + + await onPopupShown; + + const openLinkMenuItem = contextMenu.querySelector( + "#context-openlinkintab" + ); + contextMenu.activateItem(openLinkMenuItem); + + await SpecialPowers.popPrefEnv(); + } else { + throw new Error("Invalid openBy"); + } + + return href; +} + +async function synthesizeMouse(browser, link, event) { + return SpecialPowers.spawn( + browser, + [link, event], + (linkInContent, eventInContent) => { + const { EventUtils } = ChromeUtils.importESModule( + "resource://specialpowers/SpecialPowersEventUtils.sys.mjs" + ); + const target = content.document.getElementById(linkInContent); + EventUtils.synthesizeMouseAtCenter(target, eventInContent, content); + return target.href; + } + ); +} diff --git a/browser/base/content/test/tabs/link_in_tab_title_and_url_prefilled.html b/browser/base/content/test/tabs/link_in_tab_title_and_url_prefilled.html new file mode 100644 index 000000000000..a7561f40997d --- /dev/null +++ b/browser/base/content/test/tabs/link_in_tab_title_and_url_prefilled.html @@ -0,0 +1,30 @@ + + +wait-a-bit - _blank target +wait-a-bit - other target +wait-a-bit - script +wait-a-bit - no target + +request-timeout - _blank target +request-timeout - other target +request-timeout - script +request-timeout - no target + +about:blank - _blank target +about:blank - other target +blank - script +about:blank - no target + + diff --git a/browser/base/content/test/tabs/request-timeout.sjs b/browser/base/content/test/tabs/request-timeout.sjs new file mode 100644 index 000000000000..00e95ca4c06a --- /dev/null +++ b/browser/base/content/test/tabs/request-timeout.sjs @@ -0,0 +1,8 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +async function handleRequest(request, response) { + response.setStatusLine("1.1", 408, "Request Timeout"); +} diff --git a/browser/base/content/test/tabs/wait-a-bit.sjs b/browser/base/content/test/tabs/wait-a-bit.sjs new file mode 100644 index 000000000000..e90133d75289 --- /dev/null +++ b/browser/base/content/test/tabs/wait-a-bit.sjs @@ -0,0 +1,23 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// eslint-disable-next-line mozilla/no-redeclare-with-import-autofix +const { setTimeout } = ChromeUtils.importESModule( + "resource://gre/modules/Timer.sys.mjs" +); + +async function handleRequest(request, response) { + response.seizePower(); + + await new Promise(r => setTimeout(r, 2000)); + + response.write("HTTP/1.1 200 OK\r\n"); + const body = "wait a bitok"; + response.write("Content-Type: text/html\r\n"); + response.write(`Content-Length: ${body.length}\r\n`); + response.write("\r\n"); + response.write(body); + response.finish(); +} diff --git a/browser/components/urlbar/UrlbarInput.sys.mjs b/browser/components/urlbar/UrlbarInput.sys.mjs index 530a81f017ba..fe45457cea9a 100644 --- a/browser/components/urlbar/UrlbarInput.sys.mjs +++ b/browser/components/urlbar/UrlbarInput.sys.mjs @@ -378,6 +378,9 @@ export class UrlbarInput { uri = this.window.gBrowser.selectedBrowser.currentAuthPromptURI || uri || + (!this.window.gBrowser.selectedBrowser.browsingContext.sessionHistory + .count && + this.window.gBrowser.selectedBrowser._initialURI) || this.window.gBrowser.currentURI; // Strip off usernames and passwords for the location bar try {