/**
   * Test that migration skips if it already has files in the new folder.
   *
   * @throws IOException
   * @throws InterruptedException
   * @throws ExecutionException
   */
  @SuppressWarnings("unused")
  @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE")
  @SmallTest
  public void testSkipMigrateData() throws IOException, InterruptedException, ExecutionException {
    ApplicationData.clearAppData(getInstrumentation().getTargetContext());

    // Write old state files.
    File filesDir = getInstrumentation().getTargetContext().getFilesDir();
    File stateFile = new File(filesDir, TabPersistentStore.SAVED_STATE_FILE);
    File tab0 = new File(filesDir, TabState.SAVED_TAB_STATE_FILE_PREFIX + "0");
    File tab1 = new File(filesDir, TabState.SAVED_TAB_STATE_FILE_PREFIX + "1");
    File tab2 = new File(filesDir, TabState.SAVED_TAB_STATE_FILE_PREFIX_INCOGNITO + "2");
    File tab3 = new File(filesDir, TabState.SAVED_TAB_STATE_FILE_PREFIX_INCOGNITO + "3");

    assertTrue("Could not create state file", stateFile.createNewFile());
    assertTrue("Could not create tab 0 file", tab0.createNewFile());
    assertTrue("Could not create tab 1 file", tab1.createNewFile());
    assertTrue("Could not create tab 2 file", tab2.createNewFile());
    assertTrue("Could not create tab 3 file", tab3.createNewFile());

    // Write new state files
    File newDir = TabPersistentStore.getStateDirectory(getInstrumentation().getTargetContext(), 0);
    File newStateFile = new File(newDir, TabPersistentStore.SAVED_STATE_FILE);
    File newTab4 = new File(newDir, TabState.SAVED_TAB_STATE_FILE_PREFIX + "4");

    assertTrue("Could not create new tab 4 file", newTab4.createNewFile());
    assertTrue("Could not create new state file", newStateFile.createNewFile());

    // Build the TabPersistentStore which will try to move the files.
    MockTabModelSelector selector = new MockTabModelSelector(0, 0, null);
    TabPersistentStore store =
        new TabPersistentStore(selector, 0, getInstrumentation().getTargetContext(), null, null);
    TabPersistentStore.waitForMigrationToFinish();

    assertTrue("Could not find new state file", newStateFile.exists());
    assertTrue("Could not find new tab 4 file", newTab4.exists());

    // Make sure the old files did not move
    File newTab0 = new File(newDir, TabState.SAVED_TAB_STATE_FILE_PREFIX + "0");
    File newTab1 = new File(newDir, TabState.SAVED_TAB_STATE_FILE_PREFIX + "1");
    File newTab2 = new File(newDir, TabState.SAVED_TAB_STATE_FILE_PREFIX_INCOGNITO + "2");
    File newTab3 = new File(newDir, TabState.SAVED_TAB_STATE_FILE_PREFIX_INCOGNITO + "3");

    assertFalse("Could find new tab 0 file", newTab0.exists());
    assertFalse("Could find new tab 1 file", newTab1.exists());
    assertFalse("Could find new tab 2 file", newTab2.exists());
    assertFalse("Could find new tab 3 file", newTab3.exists());

    ApplicationData.clearAppData(getInstrumentation().getTargetContext());
  }
  /**
   * Tests that the max id returned is the max of all of the tab models.
   *
   * @throws IOException
   */
  @SmallTest
  public void testFindsMaxIdProperly() throws IOException {
    TabModelSelector selector0 = new MockTabModelSelector(1, 1, null);
    TabModelSelector selector1 = new MockTabModelSelector(1, 1, null);

    writeStateFile(selector0, 0);
    writeStateFile(selector1, 1);

    TabModelSelector selectorIn = new MockTabModelSelector(0, 0, null);
    TabPersistentStore storeIn =
        new TabPersistentStore(selectorIn, 0, getInstrumentation().getTargetContext(), null, null);

    int maxId = Math.max(getMaxId(selector0), getMaxId(selector1));
    disableReporting();
    assertEquals("Invalid next id", maxId + 1, storeIn.loadStateInternal());
  }
 @Override
 public void destroy() {
   mTabSaver.destroy();
   mUma.destroy();
   super.destroy();
   mActiveState = false;
 }
  /**
   * Test that the state file migration skips unrelated files.
   *
   * @throws IOException
   * @throws InterruptedException
   * @throws ExecutionException
   */
  @SuppressWarnings("unused")
  @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE")
  @SmallTest
  public void testMigrationLeavesOtherFilesAlone()
      throws IOException, InterruptedException, ExecutionException {
    ApplicationData.clearAppData(getInstrumentation().getTargetContext());

    // Write old state files.
    File filesDir = getInstrumentation().getTargetContext().getFilesDir();
    File stateFile = new File(filesDir, TabPersistentStore.SAVED_STATE_FILE);
    File tab0 = new File(filesDir, TabState.SAVED_TAB_STATE_FILE_PREFIX + "0");
    File otherFile = new File(filesDir, "other.file");

    assertTrue("Could not create state file", stateFile.createNewFile());
    assertTrue("Could not create tab 0 file", tab0.createNewFile());
    assertTrue("Could not create other file", otherFile.createNewFile());

    // Build the TabPersistentStore which will try to move the files.
    MockTabModelSelector selector = new MockTabModelSelector(0, 0, null);
    TabPersistentStore store =
        new TabPersistentStore(selector, 0, getInstrumentation().getTargetContext(), null, null);
    TabPersistentStore.waitForMigrationToFinish();

    assertFalse("Could still find old state file", stateFile.exists());
    assertFalse("Could still find old tab 0 file", tab0.exists());
    assertTrue("Could not find other file", otherFile.exists());

    // Check that the files were moved.
    File newDir = TabPersistentStore.getStateDirectory(getInstrumentation().getTargetContext(), 0);
    File newStateFile = new File(newDir, TabPersistentStore.SAVED_STATE_FILE);
    File newTab0 = new File(newDir, TabState.SAVED_TAB_STATE_FILE_PREFIX + "0");
    File newOtherFile = new File(newDir, "other.file");

    assertTrue("Could not find new state file", newStateFile.exists());
    assertTrue("Could not find new tab 0 file", newTab0.exists());
    assertFalse("Could find new other file", newOtherFile.exists());

    ApplicationData.clearAppData(getInstrumentation().getTargetContext());
  }
  @Override
  public void requestToShowTab(Tab tab, TabSelectionType type) {
    boolean isFromExternalApp =
        tab != null && tab.getLaunchType() == TabLaunchType.FROM_EXTERNAL_APP;

    if (mVisibleTab != tab && tab != null && !tab.isNativePage()) {
      TabModelBase.startTabSwitchLatencyTiming(type);
    }
    if (mVisibleTab != null && mVisibleTab != tab && !mVisibleTab.needsReload()) {
      if (mVisibleTab.isInitialized()) {
        // TODO(dtrainor): Once we figure out why we can't grab a snapshot from the current
        // tab when we have other tabs loading from external apps remove the checks for
        // FROM_EXTERNAL_APP/FROM_NEW.
        if (!mVisibleTab.isClosing() && (!isFromExternalApp || type != TabSelectionType.FROM_NEW)) {
          cacheTabBitmap(mVisibleTab);
        }
        mVisibleTab.hide();
        mVisibleTab.setFullscreenManager(null);
        mTabSaver.addTabToSaveQueue(mVisibleTab);
      }
      mVisibleTab = null;
    }

    if (tab == null) {
      notifyChanged();
      return;
    }

    // We hit this case when the user enters tab switcher and comes back to the current tab
    // without actual tab switch.
    if (mVisibleTab == tab && !mVisibleTab.isHidden()) {
      // The current tab might have been killed by the os while in tab switcher.
      tab.loadIfNeeded();
      return;
    }

    tab.setFullscreenManager(mActivity.getFullscreenManager());
    mVisibleTab = tab;

    // Don't execute the tab display part if Chrome has just been sent to background. This
    // avoids uneccessary work (tab restore) and prevents pollution of tab display metrics - see
    // http://crbug.com/316166.
    if (type != TabSelectionType.FROM_EXIT) {
      tab.show(type);
      mUma.onShowTab(tab.getId(), tab.isBeingRestored());
    }
  }
 private void writeStateFile(final TabModelSelector selector, int index) throws IOException {
   byte[] data =
       ThreadUtils.runOnUiThreadBlockingNoException(
           new Callable<byte[]>() {
             @Override
             public byte[] call() throws Exception {
               return TabPersistentStore.serializeTabModelSelector(selector, null);
             }
           });
   File f = TabPersistentStore.getStateDirectory(getInstrumentation().getTargetContext(), index);
   FileOutputStream fos = null;
   try {
     fos = new FileOutputStream(new File(f, TabPersistentStore.SAVED_STATE_FILE));
     fos.write(data);
   } finally {
     StreamUtil.closeQuietly(fos);
   }
 }
  /**
   * Tests that each model loads the subset of tabs it is responsible for. In this case, just check
   * that the model has the expected number of tabs to load. Since each model is loading a different
   * number of tabs we can tell if they are each attempting to load their specific set.
   *
   * @throws IOException
   */
  @SmallTest
  public void testOnlyLoadsSingleModel() throws IOException {
    TabModelSelector selector0 = new MockTabModelSelector(3, 3, null);
    TabModelSelector selector1 = new MockTabModelSelector(2, 1, null);

    writeStateFile(selector0, 0);
    writeStateFile(selector1, 1);

    TabModelSelector selectorIn0 = new MockTabModelSelector(0, 0, null);
    TabModelSelector selectorIn1 = new MockTabModelSelector(0, 0, null);

    TabPersistentStore storeIn0 =
        new TabPersistentStore(selectorIn0, 0, getInstrumentation().getTargetContext(), null, null);
    TabPersistentStore storeIn1 =
        new TabPersistentStore(selectorIn1, 1, getInstrumentation().getTargetContext(), null, null);

    disableReporting();
    storeIn0.loadStateInternal();
    storeIn1.loadStateInternal();

    assertEquals("Unexpected number of tabs to load", 6, storeIn0.getRestoredTabCount());
    assertEquals("Unexpected number of tabst o load", 3, storeIn1.getRestoredTabCount());
  }
 /** @return Number of restored tabs on cold startup. */
 public int getRestoredTabCount() {
   return mTabSaver.getRestoredTabCount();
 }
  /**
   * Should be called once the native library is loaded so that the actual internals of this class
   * can be initialized.
   *
   * @param tabContentProvider A {@link TabContentManager} instance.
   */
  public void onNativeLibraryReady(TabContentManager tabContentProvider) {
    assert !mActiveState : "onNativeLibraryReady called twice!";
    mTabContentManager = tabContentProvider;

    TabModel normalModel =
        new TabModelImpl(
            false, mActivity, mUma, mOrderController, mTabContentManager, mTabSaver, this);
    TabModel incognitoModel =
        new OffTheRecordTabModel(
            new TabModelImplCreator(
                mActivity, mUma, mOrderController, mTabContentManager, mTabSaver, this));
    initialize(isIncognitoSelected(), normalModel, incognitoModel);
    mRegularTabCreator.setTabModel(normalModel, mTabContentManager);
    mIncognitoTabCreator.setTabModel(incognitoModel, mTabContentManager);

    mTabSaver.setTabContentManager(tabContentProvider);

    addObserver(
        new EmptyTabModelSelectorObserver() {
          @Override
          public void onNewTabCreated(Tab tab) {
            // Only invalidate if the tab exists in the currently selected model.
            if (TabModelUtils.getTabById(getCurrentModel(), tab.getId()) != null) {
              mTabContentManager.invalidateIfChanged(tab.getId(), tab.getUrl());
            }
          }
        });

    mActiveState = true;

    new TabModelSelectorTabObserver(this) {
      @Override
      public void onUrlUpdated(Tab tab) {
        TabModel model = getModelForTabId(tab.getId());
        if (model == getCurrentModel()) {
          mTabContentManager.invalidateIfChanged(tab.getId(), tab.getUrl());
        }
      }

      @Override
      public void onLoadStopped(Tab tab) {
        handleOnPageLoadStopped(tab);
      }

      @Override
      public void onPageLoadStarted(Tab tab, String url) {
        String previousUrl = tab.getUrl();
        if (NativePageFactory.isNativePageUrl(previousUrl, tab.isIncognito())) {
          mTabContentManager.invalidateTabThumbnail(tab.getId(), previousUrl);
        } else {
          mTabContentManager.removeTabThumbnail(tab.getId());
        }
      }

      @Override
      public void onPageLoadFinished(Tab tab) {
        mUma.onPageLoadFinished(tab.getId());
      }

      @Override
      public void onPageLoadFailed(Tab tab, int errorCode) {
        mUma.onPageLoadFailed(tab.getId());
      }

      @Override
      public void onCrash(Tab tab, boolean sadTabShown) {
        if (sadTabShown) {
          mTabContentManager.removeTabThumbnail(tab.getId());
        }
        mUma.onTabCrashed(tab.getId());
      }
    };
  }
 public void clearEncryptedState() {
   mTabSaver.clearEncryptedState();
 }
 public void clearState() {
   mTabSaver.clearState();
 }
 /**
  * If there is an asynchronous session restore in-progress, try to synchronously restore the state
  * of a tab with the given id as a frozen tab. This method has no effect if there isn't a tab
  * being restored with this id, or the tab has already been restored.
  *
  * @return true if there exists a tab with the id.
  */
 public boolean tryToRestoreTabStateForId(int id) {
   if (!isSessionRestoreInProgress()) return false;
   return mTabSaver.restoreTabStateForId(id);
 }
 /**
  * If there is an asynchronous session restore in-progress, try to synchronously restore the state
  * of a tab with the given url as a frozen tab. This method has no effect if there isn't a tab
  * being restored with this url, or the tab has already been restored.
  *
  * @return true if there exists a tab with the url.
  */
 public boolean tryToRestoreTabStateForUrl(String url) {
   if (!isSessionRestoreInProgress()) return false;
   return mTabSaver.restoreTabStateForUrl(url);
 }
 /**
  * Restore the saved tabs which were loaded by {@link #loadState}.
  *
  * @param setActiveTab If true, synchronously load saved active tab and set it as the current
  *     active tab.
  */
 public void restoreTabs(boolean setActiveTab) {
   mTabSaver.restoreTabs(setActiveTab);
 }
 /**
  * Load the saved tab state. This should be called before any new tabs are created. The saved tabs
  * shall not be restored until {@link #restoreTabs} is called.
  */
 public void loadState() {
   int nextId = mTabSaver.loadState();
   if (nextId >= 0) Tab.incrementIdCounterTo(nextId);
 }
 public void saveState() {
   commitAllTabClosures();
   mTabSaver.saveState();
 }
  /**
   * Creates a new tab and posts to UI.
   *
   * @param loadUrlParams parameters of the url load.
   * @param type Information about how the tab was launched.
   * @param parent the parent tab, if present.
   * @param position the requested position (index in the tab model)
   * @param intent the source of the url if it isn't null.
   * @return The new tab.
   */
  public ChromeTab createNewTab(
      LoadUrlParams loadUrlParams,
      TabModel.TabLaunchType type,
      Tab parent,
      int position,
      Intent intent) {
    try {
      TraceEvent.begin("ChromeTabCreator.createNewTab");
      int parentId = parent != null ? parent.getId() : Tab.INVALID_TAB_ID;
      WebContents webContents = IntentHandler.getWebContentsFromIntent(intent);
      boolean isWebContentsPaused = false;
      if (webContents != null) {
        // The WebContents comes with additional data, but it shouldn't be used if the
        // WebContents itself couldn't be parsed out.
        parentId =
            IntentUtils.safeGetIntExtra(
                intent, IntentHandler.EXTRA_PARENT_TAB_ID, Tab.INVALID_TAB_ID);
        isWebContentsPaused =
            IntentUtils.safeGetBooleanExtra(intent, IntentHandler.EXTRA_WEB_CONTENTS_PAUSED, false);
      }

      // Sanitize the url.
      loadUrlParams.setUrl(UrlUtilities.fixupUrl(loadUrlParams.getUrl()));
      loadUrlParams.setTransitionType(getTransitionType(type));

      boolean openInForeground =
          mOrderController.willOpenInForeground(type, mIncognito) || webContents != null;
      ChromeTab tab;
      if (webContents != null) {
        tab =
            ChromeTab.createLiveTab(
                Tab.INVALID_TAB_ID,
                mActivity,
                mIncognito,
                mNativeWindow,
                type,
                parentId,
                !openInForeground);
        tab.initialize(webContents, mTabContentManager, !openInForeground);
        tab.getTabRedirectHandler().updateIntent(intent);

        if (isWebContentsPaused) webContents.resumeLoadingCreatedWebContents();
      } else if (!openInForeground && SysUtils.isLowEndDevice()) {
        // On low memory devices the tabs opened in background are not loaded automatically
        // to preserve resources (cpu, memory, strong renderer binding) for the foreground
        // tab.
        tab =
            ChromeTab.createTabForLazyLoad(
                mActivity, mIncognito, mNativeWindow, type, parentId, loadUrlParams);
        tab.initialize(null, mTabContentManager, !openInForeground);
        mTabSaver.addTabToSaveQueue(tab);
        tab.getTabRedirectHandler().updateIntent(intent);
      } else {
        tab =
            ChromeTab.createLiveTab(
                Tab.INVALID_TAB_ID,
                mActivity,
                mIncognito,
                mNativeWindow,
                type,
                parentId,
                !openInForeground);

        webContents =
            WarmupManager.getInstance().hasPrerenderedUrl(loadUrlParams.getUrl())
                ? WarmupManager.getInstance().takePrerenderedWebContents()
                : null;
        tab.initialize(webContents, mTabContentManager, !openInForeground);
        tab.getTabRedirectHandler().updateIntent(intent);
        tab.loadUrl(loadUrlParams);
      }

      if (intent != null && intent.hasExtra(ServiceTabLauncher.LAUNCH_REQUEST_ID_EXTRA)) {
        ServiceTabLauncher.onWebContentsForRequestAvailable(
            intent.getIntExtra(ServiceTabLauncher.LAUNCH_REQUEST_ID_EXTRA, 0),
            tab.getWebContents());
      }

      mTabModel.addTab(tab, position, type);

      return tab;
    } finally {
      TraceEvent.end("ChromeTabCreator.createNewTab");
    }
  }
 private void handleOnPageLoadStopped(Tab tab) {
   if (tab != null) mTabSaver.addTabToSaveQueue(tab);
 }