From 0df4ab0a93726eeb2782549182b7888b6857d7c2 Mon Sep 17 00:00:00 2001 From: Shane Caraveo Date: Tue, 19 Jun 2018 10:47:25 -0400 Subject: [PATCH] Bug 1422588 fix discard if tab sessionstate is not ready, r=Gijs,mikedeboer If discard is used immediately after creating a tab, restoring the tab resulted in an unusable tab. This was due to the sessionstate not being ready. This adds enough sessionstate to restore when this occurs. MozReview-Commit-ID: 6PIc71BS8VU --HG-- extra : rebase_source : 1bb9627eee561e9bf924e9eb2a34a5071cae3742 --- .../browser/browser_ext_tabs_discarded.js | 38 +++++++++++++++++++ .../components/sessionstore/SessionStore.jsm | 18 ++++++++- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_discarded.js b/browser/components/extensions/test/browser/browser_ext_tabs_discarded.js index 0bd5c5c21b2f..dbc3d30948bf 100644 --- a/browser/components/extensions/test/browser/browser_ext_tabs_discarded.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_discarded.js @@ -67,3 +67,41 @@ add_task(async function test_discarded() { BrowserTestUtils.removeTab(tab2); }); +// If discard is called immediately after creating a new tab, the new tab may not have loaded, +// and the sessionstore for that tab is not ready for discarding. The result was a corrupted +// sessionstore for the tab, which when the tab was activated, resulted in a tab with partial +// state. +add_task(async function test_create_then_discard() { + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + "permissions": ["tabs", "webNavigation"], + }, + + background: async function() { + let createdTab; + + browser.tabs.onUpdated.addListener((tabId, updatedInfo) => { + if (!updatedInfo.discarded) { + return; + } + + browser.webNavigation.onCompleted.addListener(async (details) => { + browser.test.assertEq(createdTab.id, details.tabId, "created tab navigation is completed"); + let activeTab = await browser.tabs.get(details.tabId); + browser.test.assertEq("http://example.com/", details.url, "created tab url is correct"); + browser.test.assertEq("http://example.com/", activeTab.url, "created tab url is correct"); + browser.tabs.remove(details.tabId); + browser.test.notifyPass("test-finished"); + }, {url: [{hostContains: "example.com"}]}); + + browser.tabs.update(tabId, {active: true}); + }); + + createdTab = await browser.tabs.create({url: "http://example.com/", active: false}); + browser.tabs.discard(createdTab.id); + }, + }); + await extension.startup(); + await extension.awaitFinish("test-finished"); + await extension.unload(); +}); diff --git a/browser/components/sessionstore/SessionStore.jsm b/browser/components/sessionstore/SessionStore.jsm index 81461499db44..9ca7819abafc 100644 --- a/browser/components/sessionstore/SessionStore.jsm +++ b/browser/components/sessionstore/SessionStore.jsm @@ -2030,11 +2030,25 @@ var SessionStoreInternal = { this._crashedBrowsers.delete(browser.permanentKey); aTab.removeAttribute("crashed"); + let {userTypedValue = "", userTypedClear = 0} = browser; + + let cacheState = TabStateCache.get(browser); + if (cacheState === undefined && userTypedValue) { + // Discard was likely called before state can be cached. Update + // the persistent tab state cache with browser information so a + // restore will be successful. This information is necessary for + // restoreTabContent in ContentRestore.jsm to work properly. + TabStateCache.update(browser, { + userTypedValue, + userTypedClear: 1, + }); + } + TAB_LAZY_STATES.set(aTab, { url: browser.currentURI.spec, title: aTab.label, - userTypedValue: browser.userTypedValue || "", - userTypedClear: browser.userTypedClear || 0 + userTypedValue, + userTypedClear, }); },