Bug 1351808 - Part 2 - Exclude non-standard tab types from session store. r=sebastian

Restoring anything other than normal browsing tabs (e.g. custom tabs, web apps) is more involved because those tabs
- don't appear in our normal tabs UI
- are opened in separate activities
- when we're starting up, Android's task switcher might or might not still have available task entries corresponding to such tabs from the last session

Therefore, for now, the session store will simply exclude those kinds of tabs from being saved in the session store data.

Instead of a real restore, if the corresponding tab has been closed or Gecko stopped running, we just recreate the custom tab/web app based on the stored Activity intent data we have available (bug 1352997).
Tab zombification while Gecko is running however remains fully supported, as we continue collecting session history data for all tab types, even if we don't necessarily save it to disk.

Because custom tabs/web apps currently still share a common Gecko browser window with normal tabs, we also have to modify our selected tab tracking logic accordingly, so that selecting one of these special tab types doesn't overwrite the last selected normal browsing tab.

To that effect, we now track the selected tab *ID* in memory and only convert that to a tab index when writing the data to disk. As the ID remains stable while Gecko is running, this makes tracking changes for a sub-group of tabs only easier, as we don't have to watch out for closing tabs of *any* kind affecting the tab index of everything behind them.

Bug 1346008#c3 has some preliminary ideas on how session restoring for custom tabs/web apps could be made to work.

MozReview-Commit-ID: 1q5Jtv0DKrE

--HG--
extra : rebase_source : 150e61f2a205e6bc6ea6cf346de0ba42b1935d13
This commit is contained in:
Jan Henning 2017-04-08 13:43:09 +02:00
Родитель 4f5955815a
Коммит 823bb8f884
3 изменённых файлов: 52 добавлений и 12 удалений

Просмотреть файл

@ -1941,6 +1941,11 @@ public abstract class GeckoApp
parser.updateParentId(closedTabs); parser.updateParentId(closedTabs);
windowObject.putOpt("closedTabs", closedTabs); windowObject.putOpt("closedTabs", closedTabs);
if (isExternalURL) {
// Pass on the tab we would have selected if we weren't going to open an
// external URL later on.
windowObject.put("selectedTabId", parser.getStoredSelectedTabId());
}
sessionString = new JSONObject().put( sessionString = new JSONObject().put(
"windows", new JSONArray().put(windowObject)).toString(); "windows", new JSONArray().put(windowObject)).toString();
} catch (final JSONException e) { } catch (final JSONException e) {

Просмотреть файл

@ -3660,7 +3660,8 @@ Tab.prototype = {
desktopMode: this.desktopMode, desktopMode: this.desktopMode,
isPrivate: isPrivate, isPrivate: isPrivate,
tabId: this.id, tabId: this.id,
parentId: this.parentId parentId: this.parentId,
type: this.type
}; };
if (aParams.delayLoad) { if (aParams.delayLoad) {

Просмотреть файл

@ -565,7 +565,7 @@ SessionStore.prototype = {
// Assign it a unique identifier (timestamp) and create its data object // Assign it a unique identifier (timestamp) and create its data object
aWindow.__SSID = "window" + Date.now(); aWindow.__SSID = "window" + Date.now();
this._windows[aWindow.__SSID] = { tabs: [], selected: 0, closedTabs: [] }; this._windows[aWindow.__SSID] = { tabs: [], selectedTabId: INVALID_TAB_ID, closedTabs: [] };
// Perform additional initialization when the first window is loading // Perform additional initialization when the first window is loading
if (this._loadState == STATE_STOPPED) { if (this._loadState == STATE_STOPPED) {
@ -683,8 +683,20 @@ SessionStore.prototype = {
}, },
onTabClose: function ss_onTabClose(aWindow, aBrowser, aTabIndex) { onTabClose: function ss_onTabClose(aWindow, aBrowser, aTabIndex) {
let data = aBrowser.__SS_data || {}; let data = aBrowser.__SS_data;
if (this._maxTabsUndo == 0 || this._sessionDataIsEmpty(data)) { let tab = aWindow.BrowserApp.getTabForId(data.tabId);
let windowData = this._windows[aWindow.__SSID];
if (windowData.selectedTabId == tab.id) {
// Normally, we will first select another tab anyway before closing the previous tab, which
// would make this logic moot. However
// - we only update the selected tab when selecting a normal BROWSING-type tab, and
// - in conjunction with switching between activities, the event order as we see it can
// become reversed.
windowData.selectedTabId = INVALID_TAB_ID;
}
if (this._maxTabsUndo == 0 || this._sessionDataIsEmpty(data) || tab.type != "BROWSING") {
this._lastClosedTabIndex = INVALID_TAB_INDEX; this._lastClosedTabIndex = INVALID_TAB_INDEX;
return; return;
} }
@ -706,7 +718,7 @@ SessionStore.prototype = {
this._sendClosedTabsToJava(aWindow); this._sendClosedTabsToJava(aWindow);
} }
log("onTabClose() ran for tab " + aWindow.BrowserApp.getTabForBrowser(aBrowser).id); log("onTabClose() ran for tab " + tab.id);
let evt = new Event("SSTabCloseProcessed", {"bubbles":true, "cancelable":false}); let evt = new Event("SSTabCloseProcessed", {"bubbles":true, "cancelable":false});
aBrowser.dispatchEvent(evt); aBrowser.dispatchEvent(evt);
} }
@ -799,12 +811,13 @@ SessionStore.prototype = {
return; return;
} }
let index = aWindow.BrowserApp.selectedTabIndex;
this._windows[aWindow.__SSID].selected = parseInt(index) + 1; // 1-based
let tab = aWindow.BrowserApp.getTabForBrowser(aBrowser); let tab = aWindow.BrowserApp.getTabForBrowser(aBrowser);
let tabId = tab.id; let tabId = tab.id;
if (tab.type == "BROWSING") {
this._windows[aWindow.__SSID].selectedTabId = tabId;
}
// Restore the resurrected browser // Restore the resurrected browser
if (tabId != this._keepAsZombieTabId) { if (tabId != this._keepAsZombieTabId) {
this.restoreZombieTab(tab); this.restoreZombieTab(tab);
@ -1052,6 +1065,9 @@ SessionStore.prototype = {
for (let prop in win) { for (let prop in win) {
normalWin[prop] = data[prop]; normalWin[prop] = data[prop];
} }
// This particular attribute will be converted to a tab index further down
// and stored in the appropriate (normal or private) window data.
delete normalWin.selectedTabId;
normalWin.tabs = []; normalWin.tabs = [];
// Save normal closed tabs. Forget about private closed tabs. // Save normal closed tabs. Forget about private closed tabs.
@ -1065,10 +1081,14 @@ SessionStore.prototype = {
// data will be sent to Java for Android to hold it in memory. // data will be sent to Java for Android to hold it in memory.
for (let i = 0; i < win.tabs.length; ++i) { for (let i = 0; i < win.tabs.length; ++i) {
let tab = win.tabs[i]; let tab = win.tabs[i];
if (tab.type != "BROWSING") {
continue;
}
let savedWin = tab.isPrivate ? privateData.windows[winIndex] : normalData.windows[winIndex]; let savedWin = tab.isPrivate ? privateData.windows[winIndex] : normalData.windows[winIndex];
savedWin.tabs.push(tab); savedWin.tabs.push(tab);
if (win.selected == i + 1) { if (win.selectedTabId === tab.tabId) {
savedWin.selected = savedWin.tabs.length; savedWin.selected = savedWin.tabs.length; // 1-based index
} }
} }
} }
@ -1137,8 +1157,11 @@ SessionStore.prototype = {
let winData = this._windows[aWindow.__SSID]; let winData = this._windows[aWindow.__SSID];
winData.tabs = []; winData.tabs = [];
let index = aWindow.BrowserApp.selectedTabIndex; let selectedTab = aWindow.BrowserApp.selectedTab;
winData.selected = parseInt(index) + 1; // 1-based
if (selectedTab != null && selectedTab.type == "BROWSING") {
winData.selectedTabId = selectedTab.id;
}
let tabs = aWindow.BrowserApp.tabs; let tabs = aWindow.BrowserApp.tabs;
for (let i = 0; i < tabs.length; i++) { for (let i = 0; i < tabs.length; i++) {
@ -1433,6 +1456,8 @@ SessionStore.prototype = {
delete tab.browser.__SS_restore; delete tab.browser.__SS_restore;
tab.browser.removeAttribute("pending"); tab.browser.removeAttribute("pending");
this._windows[window.__SSID].selectedTabId = tab.id;
} else { } else {
// Mark the browser for delay loading // Mark the browser for delay loading
tab.browser.__SS_restore = true; tab.browser.__SS_restore = true;
@ -1440,6 +1465,15 @@ SessionStore.prototype = {
} }
} }
if (state.windows[0].hasOwnProperty("selectedTabId") &&
this._windows[window.__SSID].selectedTabId == INVALID_TAB_ID) {
// If none of the restored tabs was the selected tab, we might be opening an URL from an
// external intent. If this new tab is a normal BROWSING tab, we'll catch its selection
// anyway, however if we've opened a custom tab/web app or anything like that we want to
// ignore it. So instead, we store the tab we would have selected from the session file.
this._windows[window.__SSID].selectedTabId = state.windows[0].selectedTabId;
}
// Restore the closed tabs array on the current window. // Restore the closed tabs array on the current window.
if (state.windows[0].closedTabs && this._maxTabsUndo > 0) { if (state.windows[0].closedTabs && this._maxTabsUndo > 0) {
this._windows[window.__SSID].closedTabs = state.windows[0].closedTabs; this._windows[window.__SSID].closedTabs = state.windows[0].closedTabs;