Bug 1044556 - Part 1 - Notify the session store about tab zombifications. r=sebastian draft
authorJan Henning <jh+bugzilla@buttercookie.de>
Fri, 18 Mar 2016 11:40:26 +0100
changeset 346266 7dbbd6bad9f6b80e7df60a26a0593e1d727bf1c5
parent 346007 e14db462d31d566570e3bece66d5380f7b1ad400
child 346267 915ddf51b7d0af0d7b0f43b813a6a41bff7be266
push id14320
push usermozilla@buttercookie.de
push dateThu, 31 Mar 2016 17:56:36 +0000
reviewerssebastian
bugs1044556
milestone48.0a1
Bug 1044556 - Part 1 - Notify the session store about tab zombifications. r=sebastian The session store relies on a few event listeners to track the history and life cycle of a tab. Under memory pressure, background tabs are zombified in order to reduce our memory usage. This involves destroying the original tab object and then recreating it as a delay loaded tab. As the session store is never told about this, it will keep the event listeners for the old tab objects - which have now been destroyed - alive and won't receive any future events for the new tab objects. This means that once a zombification has been triggered, the session history for those tabs will become effectively frozen, so after the next zombification or a session restore, the tab will reload the wrong page. Therefore this patch introduces two new events which are sent during the tab zombification process and allow the session store to detach its event listeners from the old tab object before it is going to be destroyed and subsequently reattach its listeners to the new tab object. MozReview-Commit-ID: 6xZtsCNZbQY
mobile/android/chrome/content/MemoryObserver.js
mobile/android/components/SessionStore.js
--- a/mobile/android/chrome/content/MemoryObserver.js
+++ b/mobile/android/chrome/content/MemoryObserver.js
@@ -49,31 +49,41 @@ var MemoryObserver = {
     if (tab.playingAudio) {
       Messaging.sendRequest({
         type: "Tab:AudioPlayingChange",
         tabID: tab.id,
         isAudioPlaying: false
       });
     }
 
+    // Notify the session store that the original tab object is going to be destroyed
+    let evt = document.createEvent("UIEvents");
+    evt.initUIEvent("TabPreZombify", true, false, window, null);
+    browser.dispatchEvent(evt);
+
     // We need this data to correctly create and position the new browser
     // If this browser is already a zombie, fallback to the session data
     let currentURL = browser.__SS_restore ? data.entries[0].url : browser.currentURI.spec;
     let sibling = browser.nextSibling;
     let isPrivate = PrivateBrowsingUtils.isBrowserPrivate(browser);
 
     tab.destroy();
     tab.create(currentURL, { sibling: sibling, zombifying: true, delayLoad: true, isPrivate: isPrivate });
 
     // Reattach session store data and flag this browser so it is restored on select
     browser = tab.browser;
     browser.__SS_data = data;
     browser.__SS_extdata = extra;
     browser.__SS_restore = true;
     browser.setAttribute("pending", "true");
+
+    // Notify the session store to reattach its listeners to the new tab object
+    evt = document.createEvent("UIEvents");
+    evt.initUIEvent("TabPostZombify", true, false, window, null);
+    browser.dispatchEvent(evt);
   },
 
   gc: function() {
     window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).garbageCollect();
     Cu.forceGC();
   },
 
   dumpMemoryStats: function(aLabel) {
--- a/mobile/android/components/SessionStore.js
+++ b/mobile/android/components/SessionStore.js
@@ -208,16 +208,26 @@ SessionStore.prototype = {
         break;
       }
       case "TabClose": {
         let browser = aEvent.target;
         this.onTabClose(window, browser, aEvent.detail);
         this.onTabRemove(window, browser);
         break;
       }
+      case "TabPreZombify": {
+        let browser = aEvent.target;
+        this.onTabRemove(window, browser, true);
+        break;
+      }
+      case "TabPostZombify": {
+        let browser = aEvent.target;
+        this.onTabAdd(window, browser, true);
+        break;
+      }
       case "TabSelect": {
         let browser = aEvent.target;
         this.onTabSelect(window, browser);
         break;
       }
       case "DOMTitleChanged": {
         let browser = aEvent.currentTarget;
 
@@ -272,32 +282,36 @@ SessionStore.prototype = {
       this._lastSaveTime = Date.now();
     }
 
     // Add tab change listeners to all already existing tabs
     let tabs = aWindow.BrowserApp.tabs;
     for (let i = 0; i < tabs.length; i++)
       this.onTabAdd(aWindow, tabs[i].browser, true);
 
-    // Notification of tab add/remove/selection
+    // Notification of tab add/remove/selection/zombification
     let browsers = aWindow.document.getElementById("browsers");
     browsers.addEventListener("TabOpen", this, true);
     browsers.addEventListener("TabClose", this, true);
     browsers.addEventListener("TabSelect", this, true);
+    browsers.addEventListener("TabPreZombify", this, true);
+    browsers.addEventListener("TabPostZombify", this, true);
   },
 
   onWindowClose: function ss_onWindowClose(aWindow) {
     // Ignore windows not tracked by SessionStore
     if (!aWindow.__SSID || !this._windows[aWindow.__SSID])
       return;
 
     let browsers = aWindow.document.getElementById("browsers");
     browsers.removeEventListener("TabOpen", this, true);
     browsers.removeEventListener("TabClose", this, true);
     browsers.removeEventListener("TabSelect", this, true);
+    browsers.removeEventListener("TabPreZombify", this, true);
+    browsers.removeEventListener("TabPostZombify", this, true);
 
     if (this._loadState == STATE_RUNNING) {
       // Update all window data for a last time
       this._collectWindowData(aWindow);
 
       // Clear this window from the list
       delete this._windows[aWindow.__SSID];