mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 06:43:32 +00:00
Bug 1824040: Make the loading URL opened in _blank target to be the target of Session Restore r=mak,smaug,farre,whimboo
Differential Revision: https://phabricator.services.mozilla.com/D173790
This commit is contained in:
parent
9cb4b77faa
commit
73dc3820cc
@ -6651,7 +6651,7 @@
|
||||
isURL: true,
|
||||
});
|
||||
|
||||
this.mBrowser._initialURI = originalLocation;
|
||||
this.mBrowser.browsingContext.nonWebControlledBlankURI = originalLocation;
|
||||
if (this.mTab.selected && !gBrowser.userTypedValue) {
|
||||
gURLBar.setURI();
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ add_task(async function normal_page__foreground__abort() {
|
||||
finalState: {
|
||||
tab: WAIT_A_BIT_LOADING_TITLE,
|
||||
urlbar: WAIT_A_BIT_URL,
|
||||
history: [],
|
||||
history: [WAIT_A_BIT_URL],
|
||||
},
|
||||
});
|
||||
});
|
||||
@ -95,6 +95,16 @@ add_task(async function normal_page__foreground__timeout() {
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function normal_page__foreground__session_restore() {
|
||||
await doSessionRestoreTest({
|
||||
link: "wait-a-bit--blank-target",
|
||||
openBy: OPEN_BY.CLICK,
|
||||
openAs: OPEN_AS.FOREGROUND,
|
||||
expectedSessionHistory: [WAIT_A_BIT_URL],
|
||||
expectedSessionRestored: true,
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function normal_page__background__click() {
|
||||
await doTestInSameWindow({
|
||||
link: "wait-a-bit--blank-target",
|
||||
@ -178,3 +188,12 @@ add_task(async function normal_page__background__timeout() {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function normal_page__background__session_restore() {
|
||||
await doSessionRestoreTest({
|
||||
link: "wait-a-bit--blank-target",
|
||||
openBy: OPEN_BY.CLICK,
|
||||
openAs: OPEN_AS.BACKGROUND,
|
||||
expectedSessionRestored: false,
|
||||
});
|
||||
});
|
||||
|
@ -73,3 +73,12 @@ add_task(async function normal_page__by_script__timeout() {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function normal_page__by_script__session_restore() {
|
||||
await doSessionRestoreTest({
|
||||
link: "wait-a-bit--by-script",
|
||||
openBy: OPEN_BY.CLICK,
|
||||
openAs: OPEN_AS.FOREGROUND,
|
||||
expectedSessionRestored: false,
|
||||
});
|
||||
});
|
||||
|
@ -75,3 +75,12 @@ add_task(async function normal_page__no_target__timeout() {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function normal_page__no_target__session_restore() {
|
||||
await doSessionRestoreTest({
|
||||
link: "wait-a-bit--no-target",
|
||||
openBy: OPEN_BY.CLICK,
|
||||
openAs: OPEN_AS.FOREGROUND,
|
||||
expectedSessionRestored: false,
|
||||
});
|
||||
});
|
||||
|
@ -74,6 +74,15 @@ add_task(async function normal_page__other_target__foreground__timeout() {
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function normal_page__foreground__session_restore() {
|
||||
await doSessionRestoreTest({
|
||||
link: "wait-a-bit--other-target",
|
||||
openBy: OPEN_BY.CLICK,
|
||||
openAs: OPEN_AS.FOREGROUND,
|
||||
expectedSessionRestored: false,
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function normal_page__other_target__background() {
|
||||
await doTestInSameWindow({
|
||||
link: "wait-a-bit--other-target",
|
||||
@ -136,3 +145,12 @@ add_task(async function normal_page__other_target__background__timeout() {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function normal_page__foreground__session_restore() {
|
||||
await doSessionRestoreTest({
|
||||
link: "wait-a-bit--other-target",
|
||||
openBy: OPEN_BY.CLICK,
|
||||
openAs: OPEN_AS.BACKGROUND,
|
||||
expectedSessionRestored: false,
|
||||
});
|
||||
});
|
||||
|
@ -54,22 +54,12 @@ async function doTestInSameWindow({
|
||||
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 onNewTabCreated = waitForNewTabWithLoadRequest();
|
||||
const href = await openLink(browser, link, openBy, openAs);
|
||||
|
||||
info("Wait until starting to load in the target tab");
|
||||
const target = await onLoadStarted;
|
||||
const target = await onNewTabCreated;
|
||||
Assert.equal(target.selected, openAs === OPEN_AS.FOREGROUND);
|
||||
Assert.equal(gURLBar.value, loadingState.urlbar);
|
||||
Assert.equal(target.textLabel.textContent, loadingState.tab);
|
||||
@ -119,7 +109,10 @@ async function doTestWithNewWindow({ link, expectedSetURICalled }) {
|
||||
});
|
||||
let isSetURIWhileLoading = false;
|
||||
sandbox.stub(win.gURLBar, "setURI").callsFake(uri => {
|
||||
if (!uri && win.gBrowser.selectedBrowser._initialURI) {
|
||||
if (
|
||||
!uri &&
|
||||
win.gBrowser.selectedBrowser.browsingContext.nonWebControlledBlankURI
|
||||
) {
|
||||
isSetURIWhileLoading = true;
|
||||
}
|
||||
});
|
||||
@ -132,7 +125,7 @@ async function doTestWithNewWindow({ link, expectedSetURICalled }) {
|
||||
|
||||
Assert.equal(isSetURIWhileLoading, expectedSetURICalled);
|
||||
Assert.equal(
|
||||
!!win.gBrowser.selectedBrowser._initialURI,
|
||||
!!win.gBrowser.selectedBrowser.browsingContext.nonWebControlledBlankURI,
|
||||
expectedSetURICalled
|
||||
);
|
||||
|
||||
@ -142,6 +135,61 @@ async function doTestWithNewWindow({ link, expectedSetURICalled }) {
|
||||
await SpecialPowers.popPrefEnv();
|
||||
}
|
||||
|
||||
async function doSessionRestoreTest({
|
||||
link,
|
||||
openBy,
|
||||
openAs,
|
||||
expectedSessionHistory,
|
||||
expectedSessionRestored,
|
||||
}) {
|
||||
await BrowserTestUtils.withNewTab("about:blank", async browser => {
|
||||
BrowserTestUtils.loadURIString(browser, HOME_URL);
|
||||
await BrowserTestUtils.browserLoaded(
|
||||
gBrowser.selectedBrowser,
|
||||
false,
|
||||
HOME_URL
|
||||
);
|
||||
|
||||
info(`Open link for ${link} by ${openBy} as ${openAs}`);
|
||||
const onNewTabCreated = waitForNewTabWithLoadRequest();
|
||||
const href = await openLink(browser, link, openBy, openAs);
|
||||
const target = await onNewTabCreated;
|
||||
await BrowserTestUtils.waitForCondition(
|
||||
() =>
|
||||
target.linkedBrowser.browsingContext
|
||||
.mostRecentLoadingSessionHistoryEntry
|
||||
);
|
||||
|
||||
info("Close the session");
|
||||
const sessionPromise = BrowserTestUtils.waitForSessionStoreUpdate(target);
|
||||
BrowserTestUtils.removeTab(target);
|
||||
await sessionPromise;
|
||||
|
||||
info("Restore the session");
|
||||
const restoredTab = SessionStore.undoCloseTab(window, 0);
|
||||
await BrowserTestUtils.browserLoaded(restoredTab.linkedBrowser);
|
||||
|
||||
info("Check the loaded URL of restored tab");
|
||||
Assert.equal(
|
||||
restoredTab.linkedBrowser.currentURI.spec === href,
|
||||
expectedSessionRestored
|
||||
);
|
||||
|
||||
if (expectedSessionRestored) {
|
||||
info("Check the session history of restored tab");
|
||||
const sessionHistory = await new Promise(r =>
|
||||
SessionStore.getSessionHistory(restoredTab, r)
|
||||
);
|
||||
Assert.deepEqual(
|
||||
sessionHistory.entries.map(e => e.url),
|
||||
expectedSessionHistory
|
||||
);
|
||||
}
|
||||
|
||||
BrowserTestUtils.removeTab(restoredTab);
|
||||
});
|
||||
}
|
||||
|
||||
async function openLink(browser, link, openBy, openAs) {
|
||||
let href;
|
||||
const openAsBackground = openAs === OPEN_AS.BACKGROUND;
|
||||
@ -195,3 +243,16 @@ async function synthesizeMouse(browser, link, event) {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function waitForNewTabWithLoadRequest() {
|
||||
return new Promise(resolve =>
|
||||
gBrowser.addTabsProgressListener({
|
||||
onStateChange(aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
|
||||
if (aStateFlags & Ci.nsIWebProgressListener.STATE_START) {
|
||||
gBrowser.removeTabsProgressListener(this);
|
||||
resolve(gBrowser.getTabForBrowser(aBrowser));
|
||||
}
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -459,13 +459,15 @@ export var SessionStore = {
|
||||
aBrowser,
|
||||
aBrowsingContext,
|
||||
aPermanentKey,
|
||||
aData
|
||||
aData,
|
||||
aForStorage
|
||||
) {
|
||||
return SessionStoreInternal.updateSessionStoreFromTablistener(
|
||||
aBrowser,
|
||||
aBrowsingContext,
|
||||
aPermanentKey,
|
||||
aData
|
||||
aData,
|
||||
aForStorage
|
||||
);
|
||||
},
|
||||
|
||||
@ -1357,7 +1359,8 @@ var SessionStoreInternal = {
|
||||
browser,
|
||||
browsingContext,
|
||||
permanentKey,
|
||||
update
|
||||
update,
|
||||
forStorage = false
|
||||
) {
|
||||
permanentKey = browser?.permanentKey ?? permanentKey;
|
||||
if (!permanentKey) {
|
||||
@ -1380,10 +1383,18 @@ var SessionStoreInternal = {
|
||||
);
|
||||
|
||||
if (listener) {
|
||||
let historychange = listener.collect(permanentKey, browsingContext, {
|
||||
collectFull: !!update.sHistoryNeeded,
|
||||
writeToCache: false,
|
||||
});
|
||||
let historychange =
|
||||
// If it is not the scheduled update (tab closed, window closed etc),
|
||||
// try to store the loading non-web-controlled page opened in _blank
|
||||
// first.
|
||||
(forStorage &&
|
||||
lazy.SessionHistory.collectNonWebControlledBlankLoadingSession(
|
||||
browsingContext
|
||||
)) ||
|
||||
listener.collect(permanentKey, browsingContext, {
|
||||
collectFull: !!update.sHistoryNeeded,
|
||||
writeToCache: false,
|
||||
});
|
||||
|
||||
if (historychange) {
|
||||
update.data.historychange = historychange;
|
||||
|
@ -1,6 +1,7 @@
|
||||
[DEFAULT]
|
||||
tags = local
|
||||
|
||||
[test_restore_loading_tab.py]
|
||||
[test_restore_manually_with_pinned_tabs.py]
|
||||
[test_restore_windows_after_restart_and_quit.py]
|
||||
[test_restore_windows_after_windows_shutdown.py]
|
||||
|
@ -0,0 +1,69 @@
|
||||
# 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/.
|
||||
|
||||
from urllib.parse import quote
|
||||
|
||||
from marionette_harness import MarionetteTestCase, WindowManagerMixin
|
||||
|
||||
|
||||
def inline(doc):
|
||||
return "data:text/html;charset=utf-8,{}".format(quote(doc))
|
||||
|
||||
|
||||
class TestRestoreLoadingPage(WindowManagerMixin, MarionetteTestCase):
|
||||
def setUp(self):
|
||||
super(TestRestoreLoadingPage, self).setUp()
|
||||
self.delayed_page = self.marionette.absolute_url("slow")
|
||||
|
||||
def do_test(self, html, is_restoring_expected):
|
||||
self.marionette.navigate(inline(html.format(self.delayed_page)))
|
||||
link = self.marionette.find_element("id", "link")
|
||||
link.click()
|
||||
|
||||
self.marionette.restart(in_app=True)
|
||||
|
||||
with self.marionette.using_context("chrome"):
|
||||
urls = self.marionette.execute_script(
|
||||
"return gBrowser.tabs.map(t => t.linkedBrowser.currentURI.spec);"
|
||||
)
|
||||
|
||||
if is_restoring_expected:
|
||||
self.assertEqual(
|
||||
len(urls),
|
||||
2,
|
||||
msg="The tab opened should be restored",
|
||||
)
|
||||
self.assertEqual(
|
||||
urls[1],
|
||||
self.delayed_page,
|
||||
msg="The tab restored is correct",
|
||||
)
|
||||
else:
|
||||
self.assertEqual(
|
||||
len(urls),
|
||||
1,
|
||||
msg="The tab opened should not be restored",
|
||||
)
|
||||
|
||||
self.close_all_tabs()
|
||||
|
||||
def test_target_blank(self):
|
||||
self.do_test("<a id='link' href='{}' target='_blank'>click</a>", True)
|
||||
|
||||
def test_target_other(self):
|
||||
self.do_test("<a id='link' href='{}' target='other'>click</a>", False)
|
||||
|
||||
def test_by_script(self):
|
||||
self.do_test(
|
||||
"""
|
||||
<a id='link'>click</a>
|
||||
<script>
|
||||
document.getElementById("link").addEventListener(
|
||||
"click",
|
||||
() => window.open("{}", "_blank");
|
||||
)
|
||||
</script>
|
||||
""",
|
||||
False,
|
||||
)
|
@ -384,7 +384,8 @@ export class UrlbarInput {
|
||||
uri ||
|
||||
(this.window.gBrowser.selectedBrowser.browsingContext.sessionHistory
|
||||
?.count === 0 &&
|
||||
this.window.gBrowser.selectedBrowser._initialURI) ||
|
||||
this.window.gBrowser.selectedBrowser.browsingContext
|
||||
.nonWebControlledBlankURI) ||
|
||||
this.window.gBrowser.currentURI;
|
||||
// Strip off usernames and passwords for the location bar
|
||||
try {
|
||||
|
@ -3015,6 +3015,16 @@ void CanonicalBrowsingContext::StopApzAutoscroll(nsViewID aScrollId,
|
||||
widget->StopAsyncAutoscroll(guid);
|
||||
}
|
||||
|
||||
already_AddRefed<nsISHEntry>
|
||||
CanonicalBrowsingContext::GetMostRecentLoadingSessionHistoryEntry() {
|
||||
if (mLoadingEntries.IsEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<SessionHistoryEntry> entry = mLoadingEntries.LastElement().mEntry;
|
||||
return entry.forget();
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(CanonicalBrowsingContext)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(CanonicalBrowsingContext,
|
||||
|
@ -379,6 +379,8 @@ class CanonicalBrowsingContext final : public BrowsingContext {
|
||||
void SetForceAppWindowActive(bool, ErrorResult&);
|
||||
void RecomputeAppWindowVisibility();
|
||||
|
||||
already_AddRefed<nsISHEntry> GetMostRecentLoadingSessionHistoryEntry();
|
||||
|
||||
protected:
|
||||
// Called when the browsing context is being discarded.
|
||||
void CanonicalDiscard();
|
||||
|
@ -411,6 +411,8 @@ interface CanonicalBrowsingContext : BrowsingContext {
|
||||
*/
|
||||
undefined stopApzAutoscroll(unsigned long long aScrollId,
|
||||
unsigned long aPresShellId);
|
||||
|
||||
readonly attribute nsISHEntry? mostRecentLoadingSessionHistoryEntry;
|
||||
};
|
||||
|
||||
[Exposed=Window, ChromeOnly]
|
||||
|
@ -69,7 +69,7 @@ var SessionStoreFuncInternal = {
|
||||
);
|
||||
},
|
||||
|
||||
updateSessionStoreForStorage: function SSF_updateSessionStoreForWindow(
|
||||
updateSessionStoreForStorage: function SSF_updateSessionStoreForStorage(
|
||||
aBrowser,
|
||||
aBrowsingContext,
|
||||
aPermanentKey,
|
||||
@ -80,7 +80,8 @@ var SessionStoreFuncInternal = {
|
||||
aBrowser,
|
||||
aBrowsingContext,
|
||||
aPermanentKey,
|
||||
{ data: { storage: aData }, epoch: aEpoch }
|
||||
{ data: { storage: aData }, epoch: aEpoch },
|
||||
true
|
||||
);
|
||||
},
|
||||
};
|
||||
|
@ -36,6 +36,12 @@ export var SessionHistory = Object.freeze({
|
||||
);
|
||||
},
|
||||
|
||||
collectNonWebControlledBlankLoadingSession(browsingContext) {
|
||||
return SessionHistoryInternal.collectNonWebControlledBlankLoadingSession(
|
||||
browsingContext
|
||||
);
|
||||
},
|
||||
|
||||
restore(docShell, tabData) {
|
||||
if (Services.appinfo.sessionHistoryInParent) {
|
||||
throw new Error("Use SessionHistory.restoreFromParent instead");
|
||||
@ -151,6 +157,27 @@ var SessionHistoryInternal = {
|
||||
return data;
|
||||
},
|
||||
|
||||
collectNonWebControlledBlankLoadingSession(browsingContext) {
|
||||
if (
|
||||
browsingContext.sessionHistory?.count === 0 &&
|
||||
browsingContext.nonWebControlledBlankURI &&
|
||||
browsingContext.mostRecentLoadingSessionHistoryEntry
|
||||
) {
|
||||
return {
|
||||
entries: [
|
||||
this.serializeEntry(
|
||||
browsingContext.mostRecentLoadingSessionHistoryEntry
|
||||
),
|
||||
],
|
||||
index: 0,
|
||||
fromIdx: -1,
|
||||
requestedIndex: browsingContext.sessionHistory.requestedIndex + 1,
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get an object that is a serialized representation of a History entry.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user