Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(246)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java

Issue 1408853002: [Tabbed mode] Restore tabs even if TabState is missing (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Added test Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.chrome.browser.tabmodel; 5 package org.chromium.chrome.browser.tabmodel;
6 6
7 import android.content.Context; 7 import android.content.Context;
8 import android.os.AsyncTask; 8 import android.os.AsyncTask;
9 import android.os.StrictMode; 9 import android.os.StrictMode;
10 import android.text.TextUtils; 10 import android.text.TextUtils;
11 import android.util.Pair; 11 import android.util.Pair;
12 import android.util.SparseIntArray; 12 import android.util.SparseIntArray;
13 13
14 import org.chromium.base.ImportantFileWriterAndroid; 14 import org.chromium.base.ImportantFileWriterAndroid;
15 import org.chromium.base.Log; 15 import org.chromium.base.Log;
16 import org.chromium.base.StreamUtil; 16 import org.chromium.base.StreamUtil;
17 import org.chromium.base.ThreadUtils; 17 import org.chromium.base.ThreadUtils;
18 import org.chromium.base.VisibleForTesting; 18 import org.chromium.base.VisibleForTesting;
19 import org.chromium.chrome.browser.TabState; 19 import org.chromium.chrome.browser.TabState;
20 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager; 20 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
21 import org.chromium.chrome.browser.tab.Tab; 21 import org.chromium.chrome.browser.tab.Tab;
22 import org.chromium.content_public.browser.LoadUrlParams;
22 23
23 import java.io.BufferedInputStream; 24 import java.io.BufferedInputStream;
24 import java.io.ByteArrayOutputStream; 25 import java.io.ByteArrayOutputStream;
25 import java.io.DataInputStream; 26 import java.io.DataInputStream;
26 import java.io.DataOutputStream; 27 import java.io.DataOutputStream;
27 import java.io.File; 28 import java.io.File;
28 import java.io.FileInputStream; 29 import java.io.FileInputStream;
29 import java.io.IOException; 30 import java.io.IOException;
30 import java.util.ArrayDeque; 31 import java.util.ArrayDeque;
31 import java.util.ArrayList; 32 import java.util.ArrayList;
32 import java.util.Deque; 33 import java.util.Deque;
33 import java.util.List; 34 import java.util.List;
34 import java.util.concurrent.ExecutionException; 35 import java.util.concurrent.ExecutionException;
35 36
36 /** 37 /**
37 * This class handles saving and loading tab state from the persistent storage. 38 * This class handles saving and loading tab state from the persistent storage.
38 */ 39 */
39 public class TabPersistentStore extends TabPersister { 40 public class TabPersistentStore extends TabPersister {
40 private static final String TAG = "TabPersistentStore"; 41 private static final String TAG = "tabmodel";
41 42
42 /** The current version of the saved state file. */ 43 /**
43 private static final int SAVED_STATE_VERSION = 4; 44 * The current version of the saved state file.
45 * Version 4: In addition to the tab's ID, save the tab's last URL.
46 * Version 5: In addition to the total tab count, save the incognito tab cou nt.
47 */
48 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
44 49
45 private static final String BASE_STATE_FOLDER = "tabs"; 50 private static final String BASE_STATE_FOLDER = "tabs";
46 51
47 /** The name of the file where the state is saved. */ 52 /** The name of the file where the state is saved. */
48 @VisibleForTesting 53 @VisibleForTesting
49 public static final String SAVED_STATE_FILE = "tab_state"; 54 public static final String SAVED_STATE_FILE = "tab_state";
50 55
51 /** Prevents two copies of the Migration task from being created. */ 56 /** Prevents two copies of the Migration task from being created. */
52 private static final Object MIGRATION_LOCK = new Object(); 57 private static final Object MIGRATION_LOCK = new Object();
53 58
54 /** Prevents race conditions when setting the sBaseStateDirectory. */ 59 /** Prevents race conditions when setting the sBaseStateDirectory. */
55 private static final Object BASE_STATE_DIRECTORY_LOCK = new Object(); 60 private static final Object BASE_STATE_DIRECTORY_LOCK = new Object();
56 61
57 /** 62 /**
58 * Callback interface to use while reading the persisted TabModelSelector in fo from disk. 63 * Callback interface to use while reading the persisted TabModelSelector in fo from disk.
59 */ 64 */
60 public static interface OnTabStateReadCallback { 65 public static interface OnTabStateReadCallback {
61 /** 66 /**
62 * To be called as the details about a persisted Tab are read from the T abModelSelector's 67 * To be called as the details about a persisted Tab are read from the T abModelSelector's
63 * persisted data. 68 * persisted data.
64 * @param index The index out of all tabs for the current tab read. 69 * @param index The index out of all tabs for the curre nt tab read.
65 * @param id The id for the current tab read. 70 * @param id The id for the current tab read.
66 * @param url The url for the current tab read. 71 * @param url The url for the current tab read.
67 * @param isStandardActiveIndex Whether the current tab read is the norm al active tab. 72 * @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.
73 * couldn't be determined because of a lac k of information.
74 * @param isStandardActiveIndex Whether the current tab read is the nor mal active tab.
68 * @param isIncognitoActiveIndex Whether the current tab read is the inc ognito active tab. 75 * @param isIncognitoActiveIndex Whether the current tab read is the inc ognito active tab.
69 */ 76 */
70 void onDetailsRead(int index, int id, String url, 77 void onDetailsRead(int index, int id, String url, Boolean isIncognito,
71 boolean isStandardActiveIndex, boolean isIncognitoActiveIndex); 78 boolean isStandardActiveIndex, boolean isIncognitoActiveIndex);
72 } 79 }
73 80
74 /** 81 /**
75 * Alerted at various stages of initialization. 82 * Alerted at various stages of initialization.
76 */ 83 */
77 public static interface TabPersistentStoreObserver { 84 public static interface TabPersistentStoreObserver {
78 /** 85 /**
79 * To be called when the file containing the initial information about t he TabModels has 86 * To be called when the file containing the initial information about t he TabModels has
80 * been loaded. 87 * been loaded.
(...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after
341 while (!mTabsToRestore.isEmpty() 348 while (!mTabsToRestore.isEmpty()
342 && mNormalTabsRestored.size() == 0 349 && mNormalTabsRestored.size() == 0
343 && mIncognitoTabsRestored.size() == 0) { 350 && mIncognitoTabsRestored.size() == 0) {
344 TabRestoreDetails tabToRestore = mTabsToRestore.removeFirst(); 351 TabRestoreDetails tabToRestore = mTabsToRestore.removeFirst();
345 restoreTab(tabToRestore, true); 352 restoreTab(tabToRestore, true);
346 } 353 }
347 } 354 }
348 loadNextTab(); 355 loadNextTab();
349 } 356 }
350 357
351 /** TODO(tedchoc): Remove this after migrating all callers to restoreTabStat eForUrl. */ 358 /**
352 public boolean restoreTabState(String url) { 359 * If a tab is being restored with the given url, then restore the tab in a frozen state
353 return restoreTabStateForUrl(url); 360 * synchronously.
361 */
362 public void restoreTabStateForUrl(String url) {
363 restoreTabStateInternal(url, Tab.INVALID_TAB_ID);
354 } 364 }
355 365
356 /** 366 /**
357 * If a tab is being restored with the given url, then restore the tab 367 * If a tab is being restored with the given id, then restore the tab in a f rozen state
358 * in a frozen state synchronously. 368 * synchronously.
359 *
360 * @return Whether the tab was restored.
361 */ 369 */
362 public boolean restoreTabStateForUrl(String url) { 370 public void restoreTabStateForId(int id) {
363 return restoreTabStateInternal(url, Tab.INVALID_TAB_ID); 371 restoreTabStateInternal(null, id);
364 } 372 }
365 373
366 /** 374 private void restoreTabStateInternal(String url, int id) {
367 * If a tab is being restored with the given id, then restore the tab
368 * in a frozen state synchronously.
369 *
370 * @return Whether the tab was restored.
371 */
372 public boolean restoreTabStateForId(int id) {
373 return restoreTabStateInternal(null, id);
374 }
375
376 private boolean restoreTabStateInternal(String url, int id) {
377 TabRestoreDetails tabToRestore = null; 375 TabRestoreDetails tabToRestore = null;
378 if (mLoadTabTask != null) { 376 if (mLoadTabTask != null) {
379 if ((url == null && mLoadTabTask.mTabToRestore.id == id) 377 if ((url == null && mLoadTabTask.mTabToRestore.id == id)
380 || (url != null && TextUtils.equals(mLoadTabTask.mTabToResto re.url, url))) { 378 || (url != null && TextUtils.equals(mLoadTabTask.mTabToResto re.url, url))) {
381 // Steal the task of restoring the tab from the active load tab task. 379 // Steal the task of restoring the tab from the active load tab task.
382 mLoadTabTask.cancel(false); 380 mLoadTabTask.cancel(false);
383 tabToRestore = mLoadTabTask.mTabToRestore; 381 tabToRestore = mLoadTabTask.mTabToRestore;
384 loadNextTab(); // Queue up async task to load next tab after we 're done here. 382 loadNextTab(); // Queue up async task to load next tab after we 're done here.
385 } 383 }
386 } 384 }
387 385
388 if (tabToRestore == null) { 386 if (tabToRestore == null) {
389 if (url == null) { 387 if (url == null) {
390 tabToRestore = getTabToRestoreById(id); 388 tabToRestore = getTabToRestoreById(id);
391 } else { 389 } else {
392 tabToRestore = getTabToRestoreByUrl(url); 390 tabToRestore = getTabToRestoreByUrl(url);
393 } 391 }
394 } 392 }
395 393
396 if (tabToRestore != null) { 394 if (tabToRestore != null) {
397 mTabsToRestore.remove(tabToRestore); 395 mTabsToRestore.remove(tabToRestore);
398 return restoreTab(tabToRestore, false); 396 restoreTab(tabToRestore, false);
399 } else {
400 return false;
401 } 397 }
402 } 398 }
403 399
404 private boolean restoreTab(TabRestoreDetails tabToRestore, boolean setAsActi ve) { 400 private void restoreTab(TabRestoreDetails tabToRestore, boolean setAsActive) {
405 // As we do this in startup, and restoring the active tab's state is cri tical, we permit 401 // As we do this in startup, and restoring the active tab's state is cri tical, we permit
406 // this read. 402 // this read.
407 // TODO(joth): An improved solution would be to pre-read the files on a background and 403 // TODO(joth): An improved solution would be to pre-read the files on a background and
408 // block here waiting for that task to complete only if needed. See http ://b/5518170 404 // block here waiting for that task to complete only if needed. See http ://b/5518170
409 boolean tabRestored = false;
410 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); 405 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
411 try { 406 try {
412 TabState state = TabState.restoreTabState(getStateDirectory(), tabTo Restore.id); 407 TabState state = TabState.restoreTabState(getStateDirectory(), tabTo Restore.id);
413 408 restoreTab(tabToRestore, state, setAsActive);
414 if (state != null) {
415 restoreTab(tabToRestore, state, setAsActive);
416 tabRestored = true;
417 }
418 } catch (Exception e) { 409 } catch (Exception e) {
419 // Catch generic exception to prevent a corrupted state from crashin g the app 410 // Catch generic exception to prevent a corrupted state from crashin g the app
420 // at startup. 411 // at startup.
421 Log.d(TAG, "loadTabs exception: " + e.toString(), e); 412 Log.d(TAG, "loadTabs exception: " + e.toString(), e);
422 } finally { 413 } finally {
423 StrictMode.setThreadPolicy(oldPolicy); 414 StrictMode.setThreadPolicy(oldPolicy);
424 } 415 }
425 return tabRestored;
426 } 416 }
427 417
428 private void restoreTab( 418 private void restoreTab(
429 TabRestoreDetails tabToRestore, TabState tabState, boolean setAsActi ve) { 419 TabRestoreDetails tabToRestore, TabState tabState, boolean setAsActi ve) {
430 TabModel model = mTabModelSelector.getModel(tabState.isIncognito()); 420 // If we don't have enough information about the Tab, bail out.
431 SparseIntArray restoredTabs = tabState.isIncognito() 421 boolean isIncognito = isIncognitoTabBeingRestored(tabToRestore, tabState );
432 ? mIncognitoTabsRestored : mNormalTabsRestored; 422 if (tabState == null) {
423 if (tabToRestore.isIncognito == null) {
424 Log.w(TAG, "Failed to restore tab: not enough info about its typ e was available.");
425 return;
426 } else if (isIncognito) {
427 Log.i(TAG, "Failed to restore Incognito tab: its TabState could not be restored.");
428 return;
429 }
430 }
431
432 TabModel model = mTabModelSelector.getModel(isIncognito);
433 SparseIntArray restoredTabs = isIncognito ? mIncognitoTabsRestored : mNo rmalTabsRestored;
433 int restoredIndex = 0; 434 int restoredIndex = 0;
434 if (restoredTabs.size() > 0 435 if (restoredTabs.size() > 0
435 && tabToRestore.originalIndex > restoredTabs.keyAt(restoredTabs. size() - 1)) { 436 && tabToRestore.originalIndex > restoredTabs.keyAt(restoredTabs. size() - 1)) {
436 // Restore at end if our index is greater than all restored tabs. 437 // Restore at end if our index is greater than all restored tabs.
437 restoredIndex = restoredTabs.size(); 438 restoredIndex = restoredTabs.size();
438 } else { 439 } else {
439 // Otherwise try to find the tab we should restore before, if any. 440 // Otherwise try to find the tab we should restore before, if any.
440 for (int i = 0; i < restoredTabs.size(); i++) { 441 for (int i = 0; i < restoredTabs.size(); i++) {
441 if (restoredTabs.keyAt(i) > tabToRestore.originalIndex) { 442 if (restoredTabs.keyAt(i) > tabToRestore.originalIndex) {
442 Tab nextTabByIndex = TabModelUtils.getTabById(model, restore dTabs.valueAt(i)); 443 Tab nextTabByIndex = TabModelUtils.getTabById(model, restore dTabs.valueAt(i));
443 restoredIndex = nextTabByIndex != null ? model.indexOf(nextT abByIndex) : -1; 444 restoredIndex = nextTabByIndex != null ? model.indexOf(nextT abByIndex) : -1;
444 break; 445 break;
445 } 446 }
446 } 447 }
447 } 448 }
448 mTabCreatorManager.getTabCreator(tabState.isIncognito()).createFrozenTab (tabState, 449
449 tabToRestore.id, restoredIndex); 450 if (tabState != null) {
451 mTabCreatorManager.getTabCreator(isIncognito).createFrozenTab(
452 tabState, tabToRestore.id, restoredIndex);
453 } else {
454 Log.w(TAG, "Failed to restore TabState; creating Tab with last known URL.");
455 Tab fallbackTab = mTabCreatorManager.getTabCreator(isIncognito).crea teNewTab(
456 new LoadUrlParams(tabToRestore.url), TabModel.TabLaunchType. FROM_RESTORE, null);
457 model.moveTab(fallbackTab.getId(), restoredIndex);
458 }
459
450 if (setAsActive) { 460 if (setAsActive) {
451 TabModelUtils.setIndex(model, TabModelUtils.getTabIndexById(model, t abToRestore.id)); 461 TabModelUtils.setIndex(model, TabModelUtils.getTabIndexById(model, t abToRestore.id));
452 } 462 }
453 restoredTabs.put(tabToRestore.originalIndex, tabToRestore.id); 463 restoredTabs.put(tabToRestore.originalIndex, tabToRestore.id);
454 } 464 }
455 465
456 /** 466 /**
457 * @return Number of restored tabs on cold startup. 467 * @return Number of restored tabs on cold startup.
458 */ 468 */
459 public int getRestoredTabCount() { 469 public int getRestoredTabCount() {
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
559 for (TabRestoreDetails details : mTabsToRestore) { 569 for (TabRestoreDetails details : mTabsToRestore) {
560 tabsToRestore.add(details); 570 tabsToRestore.add(details);
561 } 571 }
562 572
563 return serializeTabModelSelector(mTabModelSelector, tabsToRestore); 573 return serializeTabModelSelector(mTabModelSelector, tabsToRestore);
564 } 574 }
565 575
566 /** 576 /**
567 * Serializes {@code selector} to a byte array, copying out the data pertain ing to tab ordering 577 * Serializes {@code selector} to a byte array, copying out the data pertain ing to tab ordering
568 * and selected indices. 578 * and selected indices.
569 * @param selector The {@link TabModelSelector} to serialize. 579 * @param selector The {@link TabModelSelector} to serialize.
570 * @return A {@code byte[]} containing the serialized state of {@cod e selector}. 580 * @param tabsToRestore Tabs that are in the process of being restored.
581 * @return {@code byte[]} containing the serialized state of {@ code selector}.
571 */ 582 */
572 @VisibleForTesting 583 @VisibleForTesting
573 public static byte[] serializeTabModelSelector(TabModelSelector selector, 584 public static byte[] serializeTabModelSelector(TabModelSelector selector,
574 List<TabRestoreDetails> tabsToRestore) throws IOException { 585 List<TabRestoreDetails> tabsToRestore) throws IOException {
575 ThreadUtils.assertOnUiThread(); 586 ThreadUtils.assertOnUiThread();
576 587
577 TabModel incognitoList = selector.getModel(true); 588 TabModel incognitoList = selector.getModel(true);
578 TabModel standardList = selector.getModel(false); 589 TabModel standardList = selector.getModel(false);
579 590
580 // Determine how many Tabs there are, including those not yet been added to the TabLists. 591 // Determine how many Tabs there are, including those not yet been added to the TabLists.
581 int numAlreadyLoaded = incognitoList.getCount() + standardList.getCount( ); 592 int numAlreadyLoaded = incognitoList.getCount() + standardList.getCount( );
582 int numStillBeingLoaded = tabsToRestore == null ? 0 : tabsToRestore.size (); 593 int numStillBeingLoaded = tabsToRestore == null ? 0 : tabsToRestore.size ();
583 int numTabsTotal = numStillBeingLoaded + numAlreadyLoaded; 594 int numTabsTotal = numStillBeingLoaded + numAlreadyLoaded;
584 595
585 // Save the index file containing the list of tabs to restore. 596 // Save the index file containing the list of tabs to restore.
586 ByteArrayOutputStream output = new ByteArrayOutputStream(); 597 ByteArrayOutputStream output = new ByteArrayOutputStream();
587 DataOutputStream stream = new DataOutputStream(output); 598 DataOutputStream stream = new DataOutputStream(output);
588 stream.writeInt(SAVED_STATE_VERSION); 599 stream.writeInt(SAVED_STATE_VERSION);
589 stream.writeInt(numTabsTotal); 600 stream.writeInt(numTabsTotal);
601 stream.writeInt(incognitoList.getCount());
590 stream.writeInt(incognitoList.index()); 602 stream.writeInt(incognitoList.index());
591 stream.writeInt(standardList.index() + incognitoList.getCount()); 603 stream.writeInt(standardList.index() + incognitoList.getCount());
592 Log.d(TAG, "Serializing tab lists; counts: " 604 Log.d(TAG, "Serializing tab lists; counts: " + standardList.getCount()
593 + standardList.getCount() + "," + incognitoList.getCount()); 605 + ", " + incognitoList.getCount()
606 + ", " + (tabsToRestore == null ? 0 : tabsToRestore.size()));
594 607
595 // Save incognito state first, so when we load, if the incognito files a re unreadable 608 // Save incognito state first, so when we load, if the incognito files a re unreadable
596 // we can fall back easily onto the standard selected tab. 609 // we can fall back easily onto the standard selected tab.
597 for (int i = 0; i < incognitoList.getCount(); i++) { 610 for (int i = 0; i < incognitoList.getCount(); i++) {
598 stream.writeInt(incognitoList.getTabAt(i).getId()); 611 stream.writeInt(incognitoList.getTabAt(i).getId());
599 stream.writeUTF(incognitoList.getTabAt(i).getUrl()); 612 stream.writeUTF(incognitoList.getTabAt(i).getUrl());
600 } 613 }
601 for (int i = 0; i < standardList.getCount(); i++) { 614 for (int i = 0; i < standardList.getCount(); i++) {
602 stream.writeInt(standardList.getTabAt(i).getId()); 615 stream.writeInt(standardList.getTabAt(i).getId());
603 stream.writeUTF(standardList.getTabAt(i).getUrl()); 616 stream.writeUTF(standardList.getTabAt(i).getUrl());
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
649 if (!folder.isDirectory()) continue; 662 if (!folder.isDirectory()) continue;
650 boolean readDir = folder.equals(stateFolder); 663 boolean readDir = folder.equals(stateFolder);
651 final Deque<TabRestoreDetails> restoreList = readDir ? mTabsToRe store : null; 664 final Deque<TabRestoreDetails> restoreList = readDir ? mTabsToRe store : null;
652 final boolean isIncognitoSelected = mTabModelSelector.isIncognit oSelected(); 665 final boolean isIncognitoSelected = mTabModelSelector.isIncognit oSelected();
653 666
654 // TODO(dfalcantara): Store the max tab ID in a shared preferenc e so that it can be 667 // TODO(dfalcantara): Store the max tab ID in a shared preferenc e so that it can be
655 // shared with all of the other modes that Ch rome runs in and to 668 // shared with all of the other modes that Ch rome runs in and to
656 // avoid reading in all of the possible app_t abs subdirectories. 669 // avoid reading in all of the possible app_t abs subdirectories.
657 int curId = readSavedStateFile(folder, new OnTabStateReadCallbac k() { 670 int curId = readSavedStateFile(folder, new OnTabStateReadCallbac k() {
658 @Override 671 @Override
659 public void onDetailsRead(int index, int id, String url, 672 public void onDetailsRead(int index, int id, String url, Boo lean isIncognito,
660 boolean isStandardActiveIndex, boolean isIncognitoAc tiveIndex) { 673 boolean isStandardActiveIndex, boolean isIncognitoAc tiveIndex) {
661 // If we're not trying to build the restore list skip th e build part. 674 // If we're not trying to build the restore list skip th e build part.
662 // We've already read all the state for this entry from the input stream. 675 // We've already read all the state for this entry from the input stream.
663 if (restoreList == null) return; 676 if (restoreList == null) return;
664 677
665 // Note that incognito tab may not load properly so we m ay need to use 678 // Note that incognito tab may not load properly so we m ay need to use
666 // the current tab from the standard model. 679 // the current tab from the standard model.
667 // This logic only works because we store the incognito indices first. 680 // This logic only works because we store the incognito indices first.
681 TabRestoreDetails details =
682 new TabRestoreDetails(id, index, isIncognito, ur l);
683
668 if ((isIncognitoActiveIndex && isIncognitoSelected) 684 if ((isIncognitoActiveIndex && isIncognitoSelected)
669 || (isStandardActiveIndex && !isIncognitoSelecte d)) { 685 || (isStandardActiveIndex && !isIncognitoSelecte d)) {
670 // Active tab gets loaded first 686 // Active tab gets loaded first
671 restoreList.addFirst(new TabRestoreDetails(id, index , url)); 687 restoreList.addFirst(details);
672 } else { 688 } else {
673 restoreList.addLast(new TabRestoreDetails(id, index, url)); 689 restoreList.addLast(details);
674 } 690 }
675 691
676 if (mObserver != null) { 692 if (mObserver != null) {
677 mObserver.onDetailsRead( 693 mObserver.onDetailsRead(
678 index, id, url, isStandardActiveIndex, isInc ognitoActiveIndex); 694 index, id, url, isStandardActiveIndex, isInc ognitoActiveIndex);
679 } 695 }
680 } 696 }
681 }); 697 });
682 maxId = Math.max(maxId, curId); 698 maxId = Math.max(maxId, curId);
683 } 699 }
(...skipping 12 matching lines...) Expand all
696 // block here waiting for that task to complete only if needed. See http ://b/5518170 712 // block here waiting for that task to complete only if needed. See http ://b/5518170
697 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); 713 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
698 try { 714 try {
699 File stateFile = new File(folder, SAVED_STATE_FILE); 715 File stateFile = new File(folder, SAVED_STATE_FILE);
700 if (!stateFile.exists()) return 0; 716 if (!stateFile.exists()) return 0;
701 717
702 stream = new DataInputStream(new BufferedInputStream(new FileInputSt ream(stateFile))); 718 stream = new DataInputStream(new BufferedInputStream(new FileInputSt ream(stateFile)));
703 719
704 int nextId = 0; 720 int nextId = 0;
705 boolean skipUrlRead = false; 721 boolean skipUrlRead = false;
722 boolean skipIncognitoCount = false;
706 final int version = stream.readInt(); 723 final int version = stream.readInt();
707 if (version != SAVED_STATE_VERSION) { 724 if (version != SAVED_STATE_VERSION) {
708 if (version == 3 && SAVED_STATE_VERSION == 4) { 725 // We don't support restoring Tab data from before M18.
709 // Can transition from version 3 to version 4 by skipping UR L reads. 726 if (version < 3) return 0;
710 skipUrlRead = true; 727
711 } else { 728 // Older versions are missing newer data.
712 return 0; 729 if (version < 5) skipIncognitoCount = true;
713 } 730 if (version < 4) skipUrlRead = true;
714 } 731 }
715 732
716 final int count = stream.readInt(); 733 final int count = stream.readInt();
734 final int incognitoCount = skipIncognitoCount ? -1 : stream.readInt( );
717 final int incognitoActiveIndex = stream.readInt(); 735 final int incognitoActiveIndex = stream.readInt();
718 final int standardActiveIndex = stream.readInt(); 736 final int standardActiveIndex = stream.readInt();
719 if (count < 0 || incognitoActiveIndex >= count || standardActiveInde x >= count) { 737 if (count < 0 || incognitoActiveIndex >= count || standardActiveInde x >= count) {
720 throw new IOException(); 738 throw new IOException();
721 } 739 }
722 740
723 for (int i = 0; i < count; i++) { 741 for (int i = 0; i < count; i++) {
724 int id = stream.readInt(); 742 int id = stream.readInt();
725 String tabUrl = skipUrlRead ? "" : stream.readUTF(); 743 String tabUrl = skipUrlRead ? "" : stream.readUTF();
726 if (id >= nextId) nextId = id + 1; 744 if (id >= nextId) nextId = id + 1;
727 745
728 callback.onDetailsRead( 746 Boolean isIncognito = (incognitoCount < 0) ? null : i < incognit oCount;
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.
729 i, id, tabUrl, i == standardActiveIndex, i == incognitoA ctiveIndex); 747 callback.onDetailsRead(i, id, tabUrl, isIncognito,
748 i == standardActiveIndex, i == incognitoActiveIndex);
730 } 749 }
731 return nextId; 750 return nextId;
732 } finally { 751 } finally {
733 StreamUtil.closeQuietly(stream); 752 StreamUtil.closeQuietly(stream);
734 StrictMode.setThreadPolicy(oldPolicy); 753 StrictMode.setThreadPolicy(oldPolicy);
735 } 754 }
736 } 755 }
737 756
738 private void saveNextTab() { 757 private void saveNextTab() {
739 if (mSaveTabTask != null) return; 758 if (mSaveTabTask != null) return;
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after
909 } catch (Exception e) { 928 } catch (Exception e) {
910 Log.w(TAG, "Unable to read state: " + e); 929 Log.w(TAG, "Unable to read state: " + e);
911 return null; 930 return null;
912 } 931 }
913 } 932 }
914 933
915 @Override 934 @Override
916 protected void onPostExecute(TabState tabState) { 935 protected void onPostExecute(TabState tabState) {
917 if (mDestroyed || isCancelled()) return; 936 if (mDestroyed || isCancelled()) return;
918 937
919 if (tabState != null && ((tabState.isIncognito() && !mCancelIncognit oTabLoads) 938 boolean isIncognito = isIncognitoTabBeingRestored(mTabToRestore, tab State);
920 || (!tabState.isIncognito() && !mCancelNormalTabLoads))) { 939 boolean isLoadCancelled = (isIncognito && mCancelIncognitoTabLoads)
921 restoreTab(mTabToRestore, tabState, false); 940 || (!isIncognito && mCancelNormalTabLoads);
922 } 941 if (!isLoadCancelled) restoreTab(mTabToRestore, tabState, false);
942
923 loadNextTab(); 943 loadNextTab();
924 } 944 }
925 } 945 }
926 946
927 private static final class TabRestoreDetails { 947 private static final class TabRestoreDetails {
928 948
929 public final int id; 949 public final int id;
930 public final int originalIndex; 950 public final int originalIndex;
931 public final String url; 951 public final String url;
952 public final Boolean isIncognito;
932 953
933 public TabRestoreDetails(int id, int originalIndex, String url) { 954 public TabRestoreDetails(int id, int originalIndex, Boolean isIncognito, String url) {
934 this.id = id; 955 this.id = id;
935 this.originalIndex = originalIndex; 956 this.originalIndex = originalIndex;
936 this.url = url; 957 this.url = url;
958 this.isIncognito = isIncognito;
937 } 959 }
938 } 960 }
939 961
940 private class FileMigrationTask extends AsyncTask<Void, Void, Void> { 962 private class FileMigrationTask extends AsyncTask<Void, Void, Void> {
941 @Override 963 @Override
942 protected Void doInBackground(Void... params) { 964 protected Void doInBackground(Void... params) {
943 File oldFolder = mContext.getFilesDir(); 965 File oldFolder = mContext.getFilesDir();
944 File newFolder = getStateDirectory(); 966 File newFolder = getStateDirectory();
945 // If we already have files here just return. 967 // If we already have files here just return.
946 File[] newFiles = newFolder.listFiles(); 968 File[] newFiles = newFolder.listFiles();
(...skipping 18 matching lines...) Expand all
965 } 987 }
966 988
967 return null; 989 return null;
968 } 990 }
969 } 991 }
970 992
971 private boolean isTabUrlContentScheme(Tab tab) { 993 private boolean isTabUrlContentScheme(Tab tab) {
972 String url = tab.getUrl(); 994 String url = tab.getUrl();
973 return url != null && url.startsWith("content"); 995 return url != null && url.startsWith("content");
974 } 996 }
997
998 /**
999 * Determines if a Tab being restored is definitely an Incognito Tab.
1000 *
1001 * This function can fail to determine if a Tab is incognito if not enough d ata about the Tab
1002 * was successfully saved out.
1003 *
1004 * @return True if the tab is definitely Incognito, false if it's not or if it's undecideable.
1005 */
1006 private static boolean isIncognitoTabBeingRestored(
1007 TabRestoreDetails tabDetails, TabState tabState) {
1008 if (tabState != null) {
1009 // The Tab's previous state was completely restored.
1010 return tabState.isIncognito();
1011 } else if (tabDetails.isIncognito != null) {
1012 // The TabState couldn't be restored, but we have some information a bout the tab.
1013 return tabDetails.isIncognito;
1014 } else {
1015 // The tab's type is undecideable.
1016 return false;
1017 }
1018 }
975 } 1019 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698