Index: chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java |
index 6b1382a96b794b44c80ce3fb1eaf18be00dd0e06..6b356084f0ccbd1523cd89d06f87cebc9f0076b4 100644 |
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java |
@@ -19,6 +19,7 @@ import org.chromium.base.VisibleForTesting; |
import org.chromium.chrome.browser.TabState; |
import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager; |
import org.chromium.chrome.browser.tab.Tab; |
+import org.chromium.content_public.browser.LoadUrlParams; |
import java.io.BufferedInputStream; |
import java.io.ByteArrayOutputStream; |
@@ -37,10 +38,14 @@ import java.util.concurrent.ExecutionException; |
* This class handles saving and loading tab state from the persistent storage. |
*/ |
public class TabPersistentStore extends TabPersister { |
- private static final String TAG = "TabPersistentStore"; |
+ private static final String TAG = "tabmodel"; |
- /** The current version of the saved state file. */ |
- private static final int SAVED_STATE_VERSION = 4; |
+ /** |
+ * The current version of the saved state file. |
+ * Version 4: In addition to the tab's ID, save the tab's last URL. |
+ * Version 5: In addition to the total tab count, save the incognito tab count. |
+ */ |
+ private static final int SAVED_STATE_VERSION = 5; |
David Trainor- moved to gerrit
2015/10/17 05:58:41
at some point we should probably just go to protos
gone
2015/10/19 17:42:06
Yeah, was planning to but only after we had a stan
|
private static final String BASE_STATE_FOLDER = "tabs"; |
@@ -61,13 +66,15 @@ public class TabPersistentStore extends TabPersister { |
/** |
* To be called as the details about a persisted Tab are read from the TabModelSelector's |
* persisted data. |
- * @param index The index out of all tabs for the current tab read. |
- * @param id The id for the current tab read. |
- * @param url The url for the current tab read. |
- * @param isStandardActiveIndex Whether the current tab read is the normal active tab. |
+ * @param index The index out of all tabs for the current tab read. |
+ * @param id The id for the current tab read. |
+ * @param url The url for the current tab read. |
+ * @param incognitoCount Whether the Tab is definitely Incognito, or null if it |
David Trainor- moved to gerrit
2015/10/17 05:58:41
incognitoCount -> isIncognito? Also, when can't w
gone
2015/10/19 17:42:06
Ack; you seem to have figured this out later on.
|
+ * couldn't be determined because of a lack of information. |
+ * @param isStandardActiveIndex Whether the current tab read is the normal active tab. |
* @param isIncognitoActiveIndex Whether the current tab read is the incognito active tab. |
*/ |
- void onDetailsRead(int index, int id, String url, |
+ void onDetailsRead(int index, int id, String url, Boolean isIncognito, |
boolean isStandardActiveIndex, boolean isIncognitoActiveIndex); |
} |
@@ -348,32 +355,23 @@ public class TabPersistentStore extends TabPersister { |
loadNextTab(); |
} |
- /** TODO(tedchoc): Remove this after migrating all callers to restoreTabStateForUrl. */ |
- public boolean restoreTabState(String url) { |
- return restoreTabStateForUrl(url); |
- } |
- |
/** |
- * If a tab is being restored with the given url, then restore the tab |
- * in a frozen state synchronously. |
- * |
- * @return Whether the tab was restored. |
+ * If a tab is being restored with the given url, then restore the tab in a frozen state |
+ * synchronously. |
*/ |
- public boolean restoreTabStateForUrl(String url) { |
- return restoreTabStateInternal(url, Tab.INVALID_TAB_ID); |
+ public void restoreTabStateForUrl(String url) { |
+ restoreTabStateInternal(url, Tab.INVALID_TAB_ID); |
} |
/** |
- * If a tab is being restored with the given id, then restore the tab |
- * in a frozen state synchronously. |
- * |
- * @return Whether the tab was restored. |
+ * If a tab is being restored with the given id, then restore the tab in a frozen state |
+ * synchronously. |
*/ |
- public boolean restoreTabStateForId(int id) { |
- return restoreTabStateInternal(null, id); |
+ public void restoreTabStateForId(int id) { |
+ restoreTabStateInternal(null, id); |
} |
- private boolean restoreTabStateInternal(String url, int id) { |
+ private void restoreTabStateInternal(String url, int id) { |
TabRestoreDetails tabToRestore = null; |
if (mLoadTabTask != null) { |
if ((url == null && mLoadTabTask.mTabToRestore.id == id) |
@@ -395,26 +393,19 @@ public class TabPersistentStore extends TabPersister { |
if (tabToRestore != null) { |
mTabsToRestore.remove(tabToRestore); |
- return restoreTab(tabToRestore, false); |
- } else { |
- return false; |
+ restoreTab(tabToRestore, false); |
} |
} |
- private boolean restoreTab(TabRestoreDetails tabToRestore, boolean setAsActive) { |
+ private void restoreTab(TabRestoreDetails tabToRestore, boolean setAsActive) { |
// As we do this in startup, and restoring the active tab's state is critical, we permit |
// this read. |
// TODO(joth): An improved solution would be to pre-read the files on a background and |
// block here waiting for that task to complete only if needed. See http://b/5518170 |
- boolean tabRestored = false; |
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); |
try { |
TabState state = TabState.restoreTabState(getStateDirectory(), tabToRestore.id); |
- |
- if (state != null) { |
- restoreTab(tabToRestore, state, setAsActive); |
- tabRestored = true; |
- } |
+ restoreTab(tabToRestore, state, setAsActive); |
} catch (Exception e) { |
// Catch generic exception to prevent a corrupted state from crashing the app |
// at startup. |
@@ -422,14 +413,24 @@ public class TabPersistentStore extends TabPersister { |
} finally { |
StrictMode.setThreadPolicy(oldPolicy); |
} |
- return tabRestored; |
} |
private void restoreTab( |
TabRestoreDetails tabToRestore, TabState tabState, boolean setAsActive) { |
- TabModel model = mTabModelSelector.getModel(tabState.isIncognito()); |
- SparseIntArray restoredTabs = tabState.isIncognito() |
- ? mIncognitoTabsRestored : mNormalTabsRestored; |
+ // If we don't have enough information about the Tab, bail out. |
+ boolean isIncognito = isIncognitoTabBeingRestored(tabToRestore, tabState); |
+ if (tabState == null) { |
+ if (tabToRestore.isIncognito == null) { |
+ Log.w(TAG, "Failed to restore tab: not enough info about its type was available."); |
+ return; |
+ } else if (isIncognito) { |
+ Log.i(TAG, "Failed to restore Incognito tab: its TabState could not be restored."); |
+ return; |
+ } |
+ } |
+ |
+ TabModel model = mTabModelSelector.getModel(isIncognito); |
+ SparseIntArray restoredTabs = isIncognito ? mIncognitoTabsRestored : mNormalTabsRestored; |
int restoredIndex = 0; |
if (restoredTabs.size() > 0 |
&& tabToRestore.originalIndex > restoredTabs.keyAt(restoredTabs.size() - 1)) { |
@@ -445,8 +446,17 @@ public class TabPersistentStore extends TabPersister { |
} |
} |
} |
- mTabCreatorManager.getTabCreator(tabState.isIncognito()).createFrozenTab(tabState, |
- tabToRestore.id, restoredIndex); |
+ |
+ if (tabState != null) { |
+ mTabCreatorManager.getTabCreator(isIncognito).createFrozenTab( |
+ tabState, tabToRestore.id, restoredIndex); |
+ } else { |
+ Log.w(TAG, "Failed to restore TabState; creating Tab with last known URL."); |
+ Tab fallbackTab = mTabCreatorManager.getTabCreator(isIncognito).createNewTab( |
+ new LoadUrlParams(tabToRestore.url), TabModel.TabLaunchType.FROM_RESTORE, null); |
+ model.moveTab(fallbackTab.getId(), restoredIndex); |
+ } |
+ |
if (setAsActive) { |
TabModelUtils.setIndex(model, TabModelUtils.getTabIndexById(model, tabToRestore.id)); |
} |
@@ -566,8 +576,9 @@ public class TabPersistentStore extends TabPersister { |
/** |
* Serializes {@code selector} to a byte array, copying out the data pertaining to tab ordering |
* and selected indices. |
- * @param selector The {@link TabModelSelector} to serialize. |
- * @return A {@code byte[]} containing the serialized state of {@code selector}. |
+ * @param selector The {@link TabModelSelector} to serialize. |
+ * @param tabsToRestore Tabs that are in the process of being restored. |
+ * @return {@code byte[]} containing the serialized state of {@code selector}. |
*/ |
@VisibleForTesting |
public static byte[] serializeTabModelSelector(TabModelSelector selector, |
@@ -587,10 +598,12 @@ public class TabPersistentStore extends TabPersister { |
DataOutputStream stream = new DataOutputStream(output); |
stream.writeInt(SAVED_STATE_VERSION); |
stream.writeInt(numTabsTotal); |
+ stream.writeInt(incognitoList.getCount()); |
stream.writeInt(incognitoList.index()); |
stream.writeInt(standardList.index() + incognitoList.getCount()); |
- Log.d(TAG, "Serializing tab lists; counts: " |
- + standardList.getCount() + "," + incognitoList.getCount()); |
+ Log.d(TAG, "Serializing tab lists; counts: " + standardList.getCount() |
+ + ", " + incognitoList.getCount() |
+ + ", " + (tabsToRestore == null ? 0 : tabsToRestore.size())); |
// Save incognito state first, so when we load, if the incognito files are unreadable |
// we can fall back easily onto the standard selected tab. |
@@ -656,7 +669,7 @@ public class TabPersistentStore extends TabPersister { |
// avoid reading in all of the possible app_tabs subdirectories. |
int curId = readSavedStateFile(folder, new OnTabStateReadCallback() { |
@Override |
- public void onDetailsRead(int index, int id, String url, |
+ public void onDetailsRead(int index, int id, String url, Boolean isIncognito, |
boolean isStandardActiveIndex, boolean isIncognitoActiveIndex) { |
// If we're not trying to build the restore list skip the build part. |
// We've already read all the state for this entry from the input stream. |
@@ -665,12 +678,15 @@ public class TabPersistentStore extends TabPersister { |
// Note that incognito tab may not load properly so we may need to use |
// the current tab from the standard model. |
// This logic only works because we store the incognito indices first. |
+ TabRestoreDetails details = |
+ new TabRestoreDetails(id, index, isIncognito, url); |
+ |
if ((isIncognitoActiveIndex && isIncognitoSelected) |
|| (isStandardActiveIndex && !isIncognitoSelected)) { |
// Active tab gets loaded first |
- restoreList.addFirst(new TabRestoreDetails(id, index, url)); |
+ restoreList.addFirst(details); |
} else { |
- restoreList.addLast(new TabRestoreDetails(id, index, url)); |
+ restoreList.addLast(details); |
} |
if (mObserver != null) { |
@@ -703,17 +719,19 @@ public class TabPersistentStore extends TabPersister { |
int nextId = 0; |
boolean skipUrlRead = false; |
+ boolean skipIncognitoCount = false; |
final int version = stream.readInt(); |
if (version != SAVED_STATE_VERSION) { |
- if (version == 3 && SAVED_STATE_VERSION == 4) { |
- // Can transition from version 3 to version 4 by skipping URL reads. |
- skipUrlRead = true; |
- } else { |
- return 0; |
- } |
+ // We don't support restoring Tab data from before M18. |
+ if (version < 3) return 0; |
+ |
+ // Older versions are missing newer data. |
+ if (version < 5) skipIncognitoCount = true; |
+ if (version < 4) skipUrlRead = true; |
} |
final int count = stream.readInt(); |
+ final int incognitoCount = skipIncognitoCount ? -1 : stream.readInt(); |
final int incognitoActiveIndex = stream.readInt(); |
final int standardActiveIndex = stream.readInt(); |
if (count < 0 || incognitoActiveIndex >= count || standardActiveIndex >= count) { |
@@ -725,8 +743,9 @@ public class TabPersistentStore extends TabPersister { |
String tabUrl = skipUrlRead ? "" : stream.readUTF(); |
if (id >= nextId) nextId = id + 1; |
- callback.onDetailsRead( |
- i, id, tabUrl, i == standardActiveIndex, i == incognitoActiveIndex); |
+ Boolean isIncognito = (incognitoCount < 0) ? null : i < incognitoCount; |
David Trainor- moved to gerrit
2015/10/17 05:58:41
Ahh so we don't know if we don't know how many tab
gone
2015/10/19 17:42:06
Acknowledged.
|
+ callback.onDetailsRead(i, id, tabUrl, isIncognito, |
+ i == standardActiveIndex, i == incognitoActiveIndex); |
} |
return nextId; |
} finally { |
@@ -916,10 +935,11 @@ public class TabPersistentStore extends TabPersister { |
protected void onPostExecute(TabState tabState) { |
if (mDestroyed || isCancelled()) return; |
- if (tabState != null && ((tabState.isIncognito() && !mCancelIncognitoTabLoads) |
- || (!tabState.isIncognito() && !mCancelNormalTabLoads))) { |
- restoreTab(mTabToRestore, tabState, false); |
- } |
+ boolean isIncognito = isIncognitoTabBeingRestored(mTabToRestore, tabState); |
+ boolean isLoadCancelled = (isIncognito && mCancelIncognitoTabLoads) |
+ || (!isIncognito && mCancelNormalTabLoads); |
+ if (!isLoadCancelled) restoreTab(mTabToRestore, tabState, false); |
+ |
loadNextTab(); |
} |
} |
@@ -929,11 +949,13 @@ public class TabPersistentStore extends TabPersister { |
public final int id; |
public final int originalIndex; |
public final String url; |
+ public final Boolean isIncognito; |
- public TabRestoreDetails(int id, int originalIndex, String url) { |
+ public TabRestoreDetails(int id, int originalIndex, Boolean isIncognito, String url) { |
this.id = id; |
this.originalIndex = originalIndex; |
this.url = url; |
+ this.isIncognito = isIncognito; |
} |
} |
@@ -972,4 +994,26 @@ public class TabPersistentStore extends TabPersister { |
String url = tab.getUrl(); |
return url != null && url.startsWith("content"); |
} |
+ |
+ /** |
+ * Determines if a Tab being restored is definitely an Incognito Tab. |
+ * |
+ * This function can fail to determine if a Tab is incognito if not enough data about the Tab |
+ * was successfully saved out. |
+ * |
+ * @return True if the tab is definitely Incognito, false if it's not or if it's undecideable. |
+ */ |
+ private static boolean isIncognitoTabBeingRestored( |
+ TabRestoreDetails tabDetails, TabState tabState) { |
+ if (tabState != null) { |
+ // The Tab's previous state was completely restored. |
+ return tabState.isIncognito(); |
+ } else if (tabDetails.isIncognito != null) { |
+ // The TabState couldn't be restored, but we have some information about the tab. |
+ return tabDetails.isIncognito; |
+ } else { |
+ // The tab's type is undecideable. |
+ return false; |
+ } |
+ } |
} |