Bug 1422588 fix discard if tab sessionstate is not ready, r=mikedeboer draft
authorShane Caraveo <scaraveo@mozilla.com>
Tue, 19 Jun 2018 10:47:25 -0400
changeset 808435 53192258129c669cf48854ab506c677d3fae49b9
parent 808416 257c191e7903523a1132e04460a0b2460d950809
push id113383
push usermixedpuppy@gmail.com
push dateTue, 19 Jun 2018 14:48:41 +0000
reviewersmikedeboer
bugs1422588
milestone62.0a1
Bug 1422588 fix discard if tab sessionstate is not ready, r=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
browser/components/extensions/test/browser/browser_ext_tabs_discarded.js
browser/components/sessionstore/SessionStore.jsm
--- a/browser/components/extensions/test/browser/browser_ext_tabs_discarded.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_discarded.js
@@ -62,8 +62,46 @@ add_task(async function test_discarded()
 
   await extension.awaitFinish("test-finished");
   await extension.unload();
 
   BrowserTestUtils.removeTab(tab1);
   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();
+});
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -2025,21 +2025,35 @@ var SessionStoreInternal = {
     this.cleanUpRemovedBrowser(aTab);
 
     aTab.setAttribute("pending", "true");
 
     this._lastKnownFrameLoader.delete(browser.permanentKey);
     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,
     });
   },
 
   /**
    * When a tab is removed or suspended, remove listeners and reset restoring state.
    * @param aBrowser
    *        Browser reference
    */