OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 } |
OLD | NEW |