Commit c8387541 authored by Henrique Nakashima's avatar Henrique Nakashima Committed by Commit Bot

Reland "Use IncognitoTabModelObserver to break IncognitoTabModel->.incognito"

Fixed version with integration test to guard against the bug that
required the revert.


Original message:

Use IncognitoTabModelObserver to break IncognitoTabModel->.incognito

Two new classes IncognitoNotificationPresenceController and
IncognitoProfileDestroyer now bind the TabModelSelector to the
.incognito package, controlling respectively the incognito notification
and the incognito Profile destruction.

Since TabModelSelectorBase creates the TabModels only after native load,
it acts as a relay for the events. It is changed to recognize that there
are two TabModels, one normal and one incognito, so it can observe the
incognito one.

Splitting IncognitoTabModel into interface and impl keeps tests from
having to depend on IncognitoTabModelImpl.

Bug: 1109013
Change-Id: I04421e2f58e7ab498fbab31657175ee5e2c310b5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2378733Reviewed-by: default avatarDavid Trainor <dtrainor@chromium.org>
Commit-Queue: Henrique Nakashima <hnakashima@chromium.org>
Cr-Commit-Position: refs/heads/master@{#804332}
parent 858aea82
...@@ -803,7 +803,9 @@ chrome_java_sources = [ ...@@ -803,7 +803,9 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/identity/UuidBasedUniqueIdentificationGenerator.java", "java/src/org/chromium/chrome/browser/identity/UuidBasedUniqueIdentificationGenerator.java",
"java/src/org/chromium/chrome/browser/identity_disc/IdentityDiscController.java", "java/src/org/chromium/chrome/browser/identity_disc/IdentityDiscController.java",
"java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationManager.java", "java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationManager.java",
"java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationPresenceController.java",
"java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationService.java", "java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationService.java",
"java/src/org/chromium/chrome/browser/incognito/IncognitoProfileDestroyer.java",
"java/src/org/chromium/chrome/browser/incognito/IncognitoTabLauncher.java", "java/src/org/chromium/chrome/browser/incognito/IncognitoTabLauncher.java",
"java/src/org/chromium/chrome/browser/incognito/IncognitoTabSnapshotController.java", "java/src/org/chromium/chrome/browser/incognito/IncognitoTabSnapshotController.java",
"java/src/org/chromium/chrome/browser/incognito/IncognitoUtils.java", "java/src/org/chromium/chrome/browser/incognito/IncognitoUtils.java",
...@@ -1517,7 +1519,7 @@ chrome_java_sources = [ ...@@ -1517,7 +1519,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/tabbed_mode/TabbedSystemUiCoordinator.java", "java/src/org/chromium/chrome/browser/tabbed_mode/TabbedSystemUiCoordinator.java",
"java/src/org/chromium/chrome/browser/tabmodel/AsyncTabParamsManager.java", "java/src/org/chromium/chrome/browser/tabmodel/AsyncTabParamsManager.java",
"java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java", "java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java",
"java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModel.java", "java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelImpl.java",
"java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelImplCreator.java", "java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelImplCreator.java",
"java/src/org/chromium/chrome/browser/tabmodel/TabModelDelegate.java", "java/src/org/chromium/chrome/browser/tabmodel/TabModelDelegate.java",
"java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java", "java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java",
......
...@@ -228,6 +228,7 @@ chrome_test_java_sources = [ ...@@ -228,6 +228,7 @@ chrome_test_java_sources = [
"javatests/src/org/chromium/chrome/browser/incognito/IncognitoHistoryLeakageTest.java", "javatests/src/org/chromium/chrome/browser/incognito/IncognitoHistoryLeakageTest.java",
"javatests/src/org/chromium/chrome/browser/incognito/IncognitoNotificationServiceTest.java", "javatests/src/org/chromium/chrome/browser/incognito/IncognitoNotificationServiceTest.java",
"javatests/src/org/chromium/chrome/browser/incognito/IncognitoPermissionLeakageTest.java", "javatests/src/org/chromium/chrome/browser/incognito/IncognitoPermissionLeakageTest.java",
"javatests/src/org/chromium/chrome/browser/incognito/IncognitoProfileDestroyerIntegrationTest.java",
"javatests/src/org/chromium/chrome/browser/incognito/IncognitoStorageLeakageTest.java", "javatests/src/org/chromium/chrome/browser/incognito/IncognitoStorageLeakageTest.java",
"javatests/src/org/chromium/chrome/browser/incognito/IncognitoTabLauncherTest.java", "javatests/src/org/chromium/chrome/browser/incognito/IncognitoTabLauncherTest.java",
"javatests/src/org/chromium/chrome/browser/infobar/InfoBarAppearanceTest.java", "javatests/src/org/chromium/chrome/browser/infobar/InfoBarAppearanceTest.java",
......
...@@ -101,6 +101,8 @@ import org.chromium.chrome.browser.gsa.GSAAccountChangeListener; ...@@ -101,6 +101,8 @@ import org.chromium.chrome.browser.gsa.GSAAccountChangeListener;
import org.chromium.chrome.browser.gsa.GSAState; import org.chromium.chrome.browser.gsa.GSAState;
import org.chromium.chrome.browser.help.HelpAndFeedback; import org.chromium.chrome.browser.help.HelpAndFeedback;
import org.chromium.chrome.browser.history.HistoryManagerUtils; import org.chromium.chrome.browser.history.HistoryManagerUtils;
import org.chromium.chrome.browser.incognito.IncognitoNotificationPresenceController;
import org.chromium.chrome.browser.incognito.IncognitoProfileDestroyer;
import org.chromium.chrome.browser.init.AsyncInitializationActivity; import org.chromium.chrome.browser.init.AsyncInitializationActivity;
import org.chromium.chrome.browser.init.ProcessInitializationHandler; import org.chromium.chrome.browser.init.ProcessInitializationHandler;
import org.chromium.chrome.browser.init.StartupTabPreloader; import org.chromium.chrome.browser.init.StartupTabPreloader;
...@@ -619,6 +621,8 @@ public abstract class ChromeActivity<C extends ChromeActivityComponent> ...@@ -619,6 +621,8 @@ public abstract class ChromeActivity<C extends ChromeActivityComponent>
mIncognitoTabCreator = tabCreators.second; mIncognitoTabCreator = tabCreators.second;
OfflinePageUtils.observeTabModelSelector(this, mTabModelSelector); OfflinePageUtils.observeTabModelSelector(this, mTabModelSelector);
IncognitoProfileDestroyer.observeTabModelSelector(mTabModelSelector);
IncognitoNotificationPresenceController.observeTabModelSelector(mTabModelSelector);
if (mTabModelSelectorTabObserver != null) mTabModelSelectorTabObserver.destroy(); if (mTabModelSelectorTabObserver != null) mTabModelSelectorTabObserver.destroy();
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.incognito;
import org.chromium.chrome.browser.tabmodel.IncognitoTabModelObserver;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
/**
* Controls the presence incognito notification through {@link IncognitoNotificationManager}.
*
* Reacts to the presence or absence of incognito tabs.
*/
public class IncognitoNotificationPresenceController implements IncognitoTabModelObserver {
/**
* Creates an {@link IncognitoNotificationPresenceController} that reacts to incognito tabs in a
* given |tabModelSelector|.
* @param tabModelSelector The {@link TabModelSelector} to observe
*/
public static void observeTabModelSelector(TabModelSelector tabModelSelector) {
tabModelSelector.addIncognitoTabModelObserver(
new IncognitoNotificationPresenceController());
}
IncognitoNotificationPresenceController() {}
@Override
public void wasFirstTabCreated() {
IncognitoNotificationManager.showIncognitoNotification();
}
@Override
public void didBecomeEmpty() {
if (!IncognitoUtils.doIncognitoTabsExist()) {
IncognitoNotificationManager.dismissIncognitoNotification();
}
}
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.incognito;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tabmodel.IncognitoTabModelObserver;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
/**
* Destroys incognito {@link Profile}s when the last incognito tab is destroyed.
*
* Reacts to the presence or absence of incognito tabs.
*/
public class IncognitoProfileDestroyer implements IncognitoTabModelObserver {
private final TabModelSelector mTabModelSelector;
/**
* Creates an {@link IncognitoProfileDestroyer} that reacts to incognito tabs in a
* given |tabModelSelector|.
* @param tabModelSelector The {@link TabModelSelector} to observe
*/
public static void observeTabModelSelector(TabModelSelector tabModelSelector) {
tabModelSelector.addIncognitoTabModelObserver(
new IncognitoProfileDestroyer(tabModelSelector));
}
IncognitoProfileDestroyer(TabModelSelector tabModelSelector) {
mTabModelSelector = tabModelSelector;
}
@Override
public void didBecomeEmpty() {
if (!IncognitoUtils.doIncognitoTabsExist()) {
// Only delete the incognito profile if there are no incognito tabs open in any tab
// model selector as the profile is shared between them.
Profile profile = mTabModelSelector.getModel(true).getProfile();
if (profile != null) {
profile.destroyWhenAppropriate();
}
}
}
}
...@@ -6,7 +6,6 @@ package org.chromium.chrome.browser.tabmodel; ...@@ -6,7 +6,6 @@ package org.chromium.chrome.browser.tabmodel;
import org.chromium.base.ObserverList; import org.chromium.base.ObserverList;
import org.chromium.base.ThreadUtils; import org.chromium.base.ThreadUtils;
import org.chromium.chrome.browser.incognito.IncognitoNotificationManager;
import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabCreationState; import org.chromium.chrome.browser.tab.TabCreationState;
...@@ -27,15 +26,12 @@ import java.util.List; ...@@ -27,15 +26,12 @@ import java.util.List;
* no Tabs remain, the native model will be destroyed and only rebuilt when a new incognito Tab * no Tabs remain, the native model will be destroyed and only rebuilt when a new incognito Tab
* is created. * is created.
*/ */
public class IncognitoTabModel implements TabModel { public class IncognitoTabModelImpl implements IncognitoTabModel {
/** Creates TabModels for use in IncognitoModel. */ /** Creates TabModels for use in IncognitoModel. */
public interface IncognitoTabModelDelegate { public interface IncognitoTabModelDelegate {
/** Creates a fully working TabModel to delegate calls to. */ /** Creates a fully working TabModel to delegate calls to. */
TabModel createTabModel(); TabModel createTabModel();
/** @return Whether Incognito Tabs exist. */
boolean doIncognitoTabsExist();
/** /**
* @param model {@link TabModel} to act on. * @param model {@link TabModel} to act on.
* @return Whether the provided {@link TabModel} is currently selected in the corresponding * @return Whether the provided {@link TabModel} is currently selected in the corresponding
...@@ -45,14 +41,16 @@ public class IncognitoTabModel implements TabModel { ...@@ -45,14 +41,16 @@ public class IncognitoTabModel implements TabModel {
} }
private final IncognitoTabModelDelegate mDelegate; private final IncognitoTabModelDelegate mDelegate;
private final ObserverList<TabModelObserver> mObservers = new ObserverList<TabModelObserver>(); private final ObserverList<TabModelObserver> mObservers = new ObserverList<>();
private final ObserverList<IncognitoTabModelObserver> mIncognitoObservers =
new ObserverList<>();
private TabModel mDelegateModel; private TabModel mDelegateModel;
private boolean mIsAddingTab; private boolean mIsAddingTab;
/** /**
* Constructor for IncognitoTabModel. * Constructor for IncognitoTabModel.
*/ */
public IncognitoTabModel(IncognitoTabModelDelegate tabModelCreator) { public IncognitoTabModelImpl(IncognitoTabModelDelegate tabModelCreator) {
mDelegate = tabModelCreator; mDelegate = tabModelCreator;
mDelegateModel = EmptyTabModel.getInstance(); mDelegateModel = EmptyTabModel.getInstance();
} }
...@@ -64,23 +62,18 @@ public class IncognitoTabModel implements TabModel { ...@@ -64,23 +62,18 @@ public class IncognitoTabModel implements TabModel {
ThreadUtils.assertOnUiThread(); ThreadUtils.assertOnUiThread();
if (!(mDelegateModel instanceof EmptyTabModel)) return; if (!(mDelegateModel instanceof EmptyTabModel)) return;
IncognitoNotificationManager.showIncognitoNotification();
mDelegateModel = mDelegate.createTabModel(); mDelegateModel = mDelegate.createTabModel();
for (TabModelObserver observer : mObservers) { for (TabModelObserver observer : mObservers) {
mDelegateModel.addObserver(observer); mDelegateModel.addObserver(observer);
} }
for (IncognitoTabModelObserver observer : mIncognitoObservers) {
observer.wasFirstTabCreated();
}
} }
/** /**
* @return The TabModel that this {@link IncognitoTabModel} is delegating calls to. * Resets the delegate TabModel to be a stub EmptyTabModel and notifies
*/ * {@link IncognitoTabModelObserver}s.
protected TabModel getDelegateModel() {
return mDelegateModel;
}
/**
* Destroys the Incognito profile when all Incognito tabs have been closed. Also resets the
* delegate TabModel to be a stub EmptyTabModel.
*/ */
protected void destroyIncognitoIfNecessary() { protected void destroyIncognitoIfNecessary() {
ThreadUtils.assertOnUiThread(); ThreadUtils.assertOnUiThread();
...@@ -88,17 +81,12 @@ public class IncognitoTabModel implements TabModel { ...@@ -88,17 +81,12 @@ public class IncognitoTabModel implements TabModel {
return; return;
} }
Profile profile = getProfile(); for (IncognitoTabModelObserver observer : mIncognitoObservers) {
mDelegateModel.destroy(); observer.didBecomeEmpty();
// Only delete the incognito profile if there are no incognito tabs open in any tab
// model selector as the profile is shared between them.
if (profile != null && !mDelegate.doIncognitoTabsExist()) {
IncognitoNotificationManager.dismissIncognitoNotification();
profile.destroyWhenAppropriate();
} }
mDelegateModel.destroy();
mDelegateModel = EmptyTabModel.getInstance(); mDelegateModel = EmptyTabModel.getInstance();
} }
...@@ -262,6 +250,16 @@ public class IncognitoTabModel implements TabModel { ...@@ -262,6 +250,16 @@ public class IncognitoTabModel implements TabModel {
mDelegateModel.removeObserver(observer); mDelegateModel.removeObserver(observer);
} }
@Override
public void addIncognitoObserver(IncognitoTabModelObserver observer) {
mIncognitoObservers.addObserver(observer);
}
@Override
public void removeIncognitoObserver(IncognitoTabModelObserver observer) {
mIncognitoObservers.removeObserver(observer);
}
@Override @Override
public void removeTab(Tab tab) { public void removeTab(Tab tab) {
mDelegateModel.removeTab(tab); mDelegateModel.removeTab(tab);
...@@ -271,6 +269,5 @@ public class IncognitoTabModel implements TabModel { ...@@ -271,6 +269,5 @@ public class IncognitoTabModel implements TabModel {
} }
@Override @Override
public void openMostRecentlyClosedTab() { public void openMostRecentlyClosedTab() {}
}
} }
...@@ -5,8 +5,7 @@ ...@@ -5,8 +5,7 @@
package org.chromium.chrome.browser.tabmodel; package org.chromium.chrome.browser.tabmodel;
import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager; import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
import org.chromium.chrome.browser.incognito.IncognitoUtils; import org.chromium.chrome.browser.tabmodel.IncognitoTabModelImpl.IncognitoTabModelDelegate;
import org.chromium.chrome.browser.tabmodel.IncognitoTabModel.IncognitoTabModelDelegate;
import org.chromium.chrome.browser.tabmodel.NextTabPolicy.NextTabPolicySupplier; import org.chromium.chrome.browser.tabmodel.NextTabPolicy.NextTabPolicySupplier;
/** /**
...@@ -24,10 +23,10 @@ class IncognitoTabModelImplCreator implements IncognitoTabModelDelegate { ...@@ -24,10 +23,10 @@ class IncognitoTabModelImplCreator implements IncognitoTabModelDelegate {
private final TabModelDelegate mModelDelegate; private final TabModelDelegate mModelDelegate;
/** /**
* Constructor for an IncognitoTabModelImplCreator, used by {@link IncognitoTabModel}. * Constructor for an IncognitoTabModelImplCreator, used by {@link IncognitoTabModelImpl}.
* *
* Creating an instance of this class does not create the Incognito TabModelImpl immediately. * Creating an instance of this class does not create the Incognito TabModelImpl immediately.
* The {@link IncognitoTabModel} will use this class to create the real TabModelImpl when it * The {@link IncognitoTabModelImpl} will use this class to create the real TabModelImpl when it
* will actually be used. * will actually be used.
* @param regularTabCreator Creates regular tabs. * @param regularTabCreator Creates regular tabs.
* @param incognitoTabCreator Creates incognito tabs. * @param incognitoTabCreator Creates incognito tabs.
...@@ -62,11 +61,6 @@ class IncognitoTabModelImplCreator implements IncognitoTabModelDelegate { ...@@ -62,11 +61,6 @@ class IncognitoTabModelImplCreator implements IncognitoTabModelDelegate {
mAsyncTabParamsManager, mModelDelegate, false); mAsyncTabParamsManager, mModelDelegate, false);
} }
@Override
public boolean doIncognitoTabsExist() {
return IncognitoUtils.doIncognitoTabsExist();
}
@Override @Override
public boolean isCurrentModel(TabModel model) { public boolean isCurrentModel(TabModel model) {
return mModelDelegate.isCurrentModel(model); return mModelDelegate.isCurrentModel(model);
......
...@@ -12,18 +12,18 @@ import org.chromium.chrome.browser.tab.TabSelectionType; ...@@ -12,18 +12,18 @@ import org.chromium.chrome.browser.tab.TabSelectionType;
import org.chromium.content_public.browser.LoadUrlParams; import org.chromium.content_public.browser.LoadUrlParams;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
/** /**
* Implement methods shared across the different model implementations. * Implement methods shared across the different model implementations.
*/ */
public abstract class TabModelSelectorBase implements TabModelSelector { public abstract class TabModelSelectorBase implements TabModelSelector, IncognitoTabModelObserver {
private static final int MODEL_NOT_FOUND = -1; private static final int MODEL_NOT_FOUND = -1;
private static TabModelSelectorObserver sObserver; private static TabModelSelectorObserver sObserver;
private List<TabModel> mTabModels = new ArrayList<>(); private List<TabModel> mTabModels = new ArrayList<>();
private IncognitoTabModel mIncognitoTabModel;
/** /**
* This is a dummy implementation intended to stub out TabModelFilterProvider before native is * This is a dummy implementation intended to stub out TabModelFilterProvider before native is
...@@ -34,6 +34,8 @@ public abstract class TabModelSelectorBase implements TabModelSelector { ...@@ -34,6 +34,8 @@ public abstract class TabModelSelectorBase implements TabModelSelector {
private final TabModelFilterFactory mTabModelFilterFactory; private final TabModelFilterFactory mTabModelFilterFactory;
private int mActiveModelIndex; private int mActiveModelIndex;
private final ObserverList<TabModelSelectorObserver> mObservers = new ObserverList<>(); private final ObserverList<TabModelSelectorObserver> mObservers = new ObserverList<>();
private final ObserverList<IncognitoTabModelObserver> mIncognitoObservers =
new ObserverList<>();
private boolean mTabStateInitialized; private boolean mTabStateInitialized;
private boolean mStartIncognito; private boolean mStartIncognito;
private boolean mReparentingInProgress; private boolean mReparentingInProgress;
...@@ -47,12 +49,13 @@ public abstract class TabModelSelectorBase implements TabModelSelector { ...@@ -47,12 +49,13 @@ public abstract class TabModelSelectorBase implements TabModelSelector {
mStartIncognito = startIncognito; mStartIncognito = startIncognito;
} }
protected final void initialize(TabModel... models) { protected final void initialize(TabModel normalModel, IncognitoTabModel incognitoModel) {
// Only normal and incognito supported for now. // Only normal and incognito supported for now.
assert mTabModels.isEmpty(); assert mTabModels.isEmpty();
assert models.length > 0;
Collections.addAll(mTabModels, models); mTabModels.add(normalModel);
mTabModels.add(incognitoModel);
mIncognitoTabModel = incognitoModel;
mActiveModelIndex = getModelIndex(mStartIncognito); mActiveModelIndex = getModelIndex(mStartIncognito);
assert mActiveModelIndex != MODEL_NOT_FOUND; assert mActiveModelIndex != MODEL_NOT_FOUND;
mTabModelFilterProvider = new TabModelFilterProvider(mTabModelFilterFactory, mTabModels); mTabModelFilterProvider = new TabModelFilterProvider(mTabModelFilterFactory, mTabModels);
...@@ -83,6 +86,8 @@ public abstract class TabModelSelectorBase implements TabModelSelector { ...@@ -83,6 +86,8 @@ public abstract class TabModelSelectorBase implements TabModelSelector {
addObserver(sObserver); addObserver(sObserver);
} }
mIncognitoTabModel.addIncognitoObserver(this);
notifyChanged(); notifyChanged();
} }
...@@ -261,6 +266,10 @@ public abstract class TabModelSelectorBase implements TabModelSelector { ...@@ -261,6 +266,10 @@ public abstract class TabModelSelectorBase implements TabModelSelector {
public void destroy() { public void destroy() {
removeObserver(mTabModelFilterProvider); removeObserver(mTabModelFilterProvider);
mTabModelFilterProvider.destroy(); mTabModelFilterProvider.destroy();
if (mIncognitoTabModel != null) {
mIncognitoTabModel.removeIncognitoObserver(this);
}
for (int i = 0; i < getModels().size(); i++) mTabModels.get(i).destroy(); for (int i = 0; i < getModels().size(); i++) mTabModels.get(i).destroy();
mTabModels.clear(); mTabModels.clear();
} }
...@@ -278,7 +287,7 @@ public abstract class TabModelSelectorBase implements TabModelSelector { ...@@ -278,7 +287,7 @@ public abstract class TabModelSelectorBase implements TabModelSelector {
/** /**
* Notifies all the listeners that a new tab has been created. * Notifies all the listeners that a new tab has been created.
* @param tab The tab that has been created. * @param tab The tab that has been created.
* @param creationSTate How the tab was created. * @param creationState How the tab was created.
*/ */
private void notifyNewTabCreated(Tab tab, @TabCreationState int creationState) { private void notifyNewTabCreated(Tab tab, @TabCreationState int creationState) {
for (TabModelSelectorObserver listener : mObservers) { for (TabModelSelectorObserver listener : mObservers) {
...@@ -299,4 +308,23 @@ public abstract class TabModelSelectorBase implements TabModelSelector { ...@@ -299,4 +308,23 @@ public abstract class TabModelSelectorBase implements TabModelSelector {
public boolean isReparentingInProgress() { public boolean isReparentingInProgress() {
return mReparentingInProgress; return mReparentingInProgress;
} }
@Override
public void addIncognitoTabModelObserver(IncognitoTabModelObserver incognitoObserver) {
mIncognitoObservers.addObserver(incognitoObserver);
}
@Override
public void wasFirstTabCreated() {
for (IncognitoTabModelObserver observer : mIncognitoObservers) {
observer.wasFirstTabCreated();
}
}
@Override
public void didBecomeEmpty() {
for (IncognitoTabModelObserver observer : mIncognitoObservers) {
observer.didBecomeEmpty();
}
}
} }
...@@ -128,20 +128,21 @@ public class TabModelSelectorImpl extends TabModelSelectorBase implements TabMod ...@@ -128,20 +128,21 @@ public class TabModelSelectorImpl extends TabModelSelectorBase implements TabMod
(ChromeTabCreator) getTabCreatorManager().getTabCreator(false); (ChromeTabCreator) getTabCreatorManager().getTabCreator(false);
ChromeTabCreator incognitoTabCreator = ChromeTabCreator incognitoTabCreator =
(ChromeTabCreator) getTabCreatorManager().getTabCreator(true); (ChromeTabCreator) getTabCreatorManager().getTabCreator(true);
TabModelImpl normalModel = new TabModelImpl(false, mIsTabbedActivityForSync, TabModel normalModel = new TabModelImpl(false, mIsTabbedActivityForSync, regularTabCreator,
regularTabCreator, incognitoTabCreator, mUma, mOrderController, mTabContentManager, incognitoTabCreator, mUma, mOrderController, mTabContentManager, mTabSaver,
mTabSaver, mNextTabPolicySupplier, mAsyncTabParamsManager, this, mIsUndoSupported); mNextTabPolicySupplier, mAsyncTabParamsManager, this, mIsUndoSupported);
TabModel incognitoModel = new IncognitoTabModel(new IncognitoTabModelImplCreator( IncognitoTabModel incognitoModel =
regularTabCreator, incognitoTabCreator, mUma, mOrderController, mTabContentManager, new IncognitoTabModelImpl(new IncognitoTabModelImplCreator(regularTabCreator,
mTabSaver, mNextTabPolicySupplier, mAsyncTabParamsManager, this)); incognitoTabCreator, mUma, mOrderController, mTabContentManager, mTabSaver,
mNextTabPolicySupplier, mAsyncTabParamsManager, this));
regularTabCreator.setTabModel(normalModel, mOrderController); regularTabCreator.setTabModel(normalModel, mOrderController);
incognitoTabCreator.setTabModel(incognitoModel, mOrderController); incognitoTabCreator.setTabModel(incognitoModel, mOrderController);
onNativeLibraryReadyInternal(tabContentProvider, normalModel, incognitoModel); onNativeLibraryReadyInternal(tabContentProvider, normalModel, incognitoModel);
} }
@VisibleForTesting @VisibleForTesting
void onNativeLibraryReadyInternal( void onNativeLibraryReadyInternal(TabContentManager tabContentProvider, TabModel normalModel,
TabContentManager tabContentProvider, TabModel normalModel, TabModel incognitoModel) { IncognitoTabModel incognitoModel) {
mTabContentManager = tabContentProvider; mTabContentManager = tabContentProvider;
initialize(normalModel, incognitoModel); initialize(normalModel, incognitoModel);
mTabSaver.setTabContentManager(mTabContentManager); mTabSaver.setTabContentManager(mTabContentManager);
...@@ -226,7 +227,7 @@ public class TabModelSelectorImpl extends TabModelSelectorBase implements TabMod ...@@ -226,7 +227,7 @@ public class TabModelSelectorImpl extends TabModelSelectorBase implements TabMod
* @param incognitoModel The incognito tab model. * @param incognitoModel The incognito tab model.
*/ */
@VisibleForTesting @VisibleForTesting
public void initializeForTesting(TabModel normalModel, TabModel incognitoModel) { public void initializeForTesting(TabModel normalModel, IncognitoTabModel incognitoModel) {
initialize(normalModel, incognitoModel); initialize(normalModel, incognitoModel);
} }
......
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.incognito;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import androidx.test.filters.MediumTest;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.JniMocker;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.profiles.ProfileManager;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
import java.util.concurrent.ExecutionException;
/**
* Integration tests for {@link IncognitoProfileDestroyer}.
*/
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class IncognitoProfileDestroyerIntegrationTest {
@Rule
public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
@Rule
public JniMocker jniMocker = new JniMocker();
private TabModel mIncognitoTabModel;
@Mock
ProfileManager.Observer mMockProfileManagerObserver;
@Before
public void setUp() throws InterruptedException {
MockitoAnnotations.initMocks(this);
mActivityTestRule.startMainActivityOnBlankPage();
ProfileManager.addObserver(mMockProfileManagerObserver);
mIncognitoTabModel = mActivityTestRule.getActivity().getTabModelSelector().getModel(true);
}
@Test
@MediumTest
@Feature({"OffTheRecord"})
public void test_closeOnlyTab_profileDestroyed() throws ExecutionException {
// Open a single incognito tab
Tab onlyTab = mActivityTestRule.newIncognitoTabFromMenu();
// Verify the tab is opened and the TabModel now has an incognito Profile
assertEquals(1, mIncognitoTabModel.getCount());
Profile incognitoProfile =
TestThreadUtils.runOnUiThreadBlocking(() -> mIncognitoTabModel.getProfile());
assertNotNull(incognitoProfile);
verify(mMockProfileManagerObserver, never()).onProfileDestroyed(any());
// Close the incognito tab
TestThreadUtils.runOnUiThreadBlocking(() -> mIncognitoTabModel.closeTab(onlyTab));
// Verify the incognito Profile was destroyed
verify(mMockProfileManagerObserver).onProfileDestroyed(eq(incognitoProfile));
incognitoProfile =
TestThreadUtils.runOnUiThreadBlocking(() -> mIncognitoTabModel.getProfile());
assertNull(incognitoProfile);
}
@Test
@MediumTest
@Feature({"OffTheRecord"})
public void test_closeOneOfTwoTabs_profileNotDestroyed() throws ExecutionException {
// Open two incognito tabs
Tab firstTab = mActivityTestRule.newIncognitoTabFromMenu();
mActivityTestRule.newIncognitoTabFromMenu();
// Verify the tabs are opened and the TabModel now has an incognito Profile
assertEquals(2, mIncognitoTabModel.getCount());
Profile incognitoProfile =
TestThreadUtils.runOnUiThreadBlocking(() -> mIncognitoTabModel.getProfile());
assertNotNull(incognitoProfile);
verify(mMockProfileManagerObserver, never()).onProfileDestroyed(any());
// Close one incognito tab
TestThreadUtils.runOnUiThreadBlocking(() -> mIncognitoTabModel.closeTab(firstTab));
// Verify the incognito Profile was not destroyed
verify(mMockProfileManagerObserver, never()).onProfileDestroyed(any());
incognitoProfile =
TestThreadUtils.runOnUiThreadBlocking(() -> mIncognitoTabModel.getProfile());
assertNotNull(incognitoProfile);
}
}
...@@ -28,7 +28,7 @@ import java.util.Set; ...@@ -28,7 +28,7 @@ import java.util.Set;
public class TabModelSelectorObserverTestRule extends ChromeBrowserTestRule { public class TabModelSelectorObserverTestRule extends ChromeBrowserTestRule {
private TabModelSelectorBase mSelector; private TabModelSelectorBase mSelector;
private TabModelSelectorTestTabModel mNormalTabModel; private TabModelSelectorTestTabModel mNormalTabModel;
private TabModelSelectorTestTabModel mIncognitoTabModel; private TabModelSelectorTestIncognitoTabModel mIncognitoTabModel;
public TabModelSelectorBase getSelector() { public TabModelSelectorBase getSelector() {
return mSelector; return mSelector;
...@@ -38,7 +38,7 @@ public class TabModelSelectorObserverTestRule extends ChromeBrowserTestRule { ...@@ -38,7 +38,7 @@ public class TabModelSelectorObserverTestRule extends ChromeBrowserTestRule {
return mNormalTabModel; return mNormalTabModel;
} }
public TabModelSelectorTestTabModel getIncognitoTabModel() { public TabModelSelectorTestIncognitoTabModel getIncognitoTabModel() {
return mIncognitoTabModel; return mIncognitoTabModel;
} }
...@@ -121,7 +121,7 @@ public class TabModelSelectorObserverTestRule extends ChromeBrowserTestRule { ...@@ -121,7 +121,7 @@ public class TabModelSelectorObserverTestRule extends ChromeBrowserTestRule {
tabPersistentStore, nextTabPolicySupplier, asyncTabParamsManager, delegate); tabPersistentStore, nextTabPolicySupplier, asyncTabParamsManager, delegate);
mIncognitoTabModel = mIncognitoTabModel =
new TabModelSelectorTestTabModel(true, orderController, tabContentManager, new TabModelSelectorTestIncognitoTabModel(orderController, tabContentManager,
tabPersistentStore, nextTabPolicySupplier, asyncTabParamsManager, delegate); tabPersistentStore, nextTabPolicySupplier, asyncTabParamsManager, delegate);
mSelector.initialize(mNormalTabModel, mIncognitoTabModel); mSelector.initialize(mNormalTabModel, mIncognitoTabModel);
...@@ -158,4 +158,24 @@ public class TabModelSelectorObserverTestRule extends ChromeBrowserTestRule { ...@@ -158,4 +158,24 @@ public class TabModelSelectorObserverTestRule extends ChromeBrowserTestRule {
return mObserverSet; return mObserverSet;
} }
} }
/**
* Test IncognitoTabModel that exposes the needed capabilities for testing.
*/
public static class TabModelSelectorTestIncognitoTabModel
extends TabModelSelectorTestTabModel implements IncognitoTabModel {
public TabModelSelectorTestIncognitoTabModel(TabModelOrderController orderController,
TabContentManager tabContentManager, TabPersistentStore tabPersistentStore,
NextTabPolicySupplier nextTabPolicySupplier,
AsyncTabParamsManager asyncTabParamsManager, TabModelDelegate modelDelegate) {
super(true, orderController, tabContentManager, tabPersistentStore,
nextTabPolicySupplier, asyncTabParamsManager, modelDelegate);
}
@Override
public void addIncognitoObserver(IncognitoTabModelObserver observer) {}
@Override
public void removeIncognitoObserver(IncognitoTabModelObserver observer) {}
}
} }
...@@ -123,12 +123,14 @@ public class TabPersistentStoreTest { ...@@ -123,12 +123,14 @@ public class TabPersistentStoreTest {
AsyncTabParamsManager.getInstance(), TestTabModelSelector.this, true); AsyncTabParamsManager.getInstance(), TestTabModelSelector.this, true);
} }
}; };
TabModelImpl regularTabModel = TestThreadUtils.runOnUiThreadBlocking(callable); TabModel regularTabModel = TestThreadUtils.runOnUiThreadBlocking(callable);
TabModel incognitoTabModel = new IncognitoTabModel(new IncognitoTabModelImplCreator( IncognitoTabModel incognitoTabModel = new IncognitoTabModelImpl(
getTabCreatorManager().getTabCreator(false), new IncognitoTabModelImplCreator(getTabCreatorManager().getTabCreator(false),
getTabCreatorManager().getTabCreator(true), null, mTabModelOrderController, getTabCreatorManager().getTabCreator(true), null,
null, mTabPersistentStore, mTabModelOrderController, null, mTabPersistentStore,
() -> NextTabPolicy.HIERARCHICAL, AsyncTabParamsManager.getInstance(), this)); ()
-> NextTabPolicy.HIERARCHICAL,
AsyncTabParamsManager.getInstance(), this));
initialize(regularTabModel, incognitoTabModel); initialize(regularTabModel, incognitoTabModel);
} }
......
...@@ -14,6 +14,8 @@ android_library("java") { ...@@ -14,6 +14,8 @@ android_library("java") {
"android/java/src/org/chromium/chrome/browser/tabmodel/EmptyTabModelSelectorObserver.java", "android/java/src/org/chromium/chrome/browser/tabmodel/EmptyTabModelSelectorObserver.java",
"android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabHost.java", "android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabHost.java",
"android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabHostRegistry.java", "android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabHostRegistry.java",
"android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModel.java",
"android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelObserver.java",
"android/java/src/org/chromium/chrome/browser/tabmodel/NextTabPolicy.java", "android/java/src/org/chromium/chrome/browser/tabmodel/NextTabPolicy.java",
"android/java/src/org/chromium/chrome/browser/tabmodel/TabCreator.java", "android/java/src/org/chromium/chrome/browser/tabmodel/TabCreator.java",
"android/java/src/org/chromium/chrome/browser/tabmodel/TabCreatorManager.java", "android/java/src/org/chromium/chrome/browser/tabmodel/TabCreatorManager.java",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.tabmodel;
/**
* A {@link TabModel} which also emits events relevant to incognito tabs.
*/
public interface IncognitoTabModel extends TabModel {
/**
* Subscribes an {@link IncognitoTabModelObserver} to be notified about incognito events.
* @param observer The observer to be subscribed.
*/
void addIncognitoObserver(IncognitoTabModelObserver observer);
/**
* Unsubscribes an {@link IncognitoTabModelObserver}.
* @param observer The observer to be unsubscribed.
*/
void removeIncognitoObserver(IncognitoTabModelObserver observer);
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.tabmodel;
/**
* An observer of {@link IncognitoTabModel} that receives events relevant to incognito tabs.
*/
public interface IncognitoTabModelObserver {
/**
* Called when the first tab of the {@link IncognitoTabModel} is created.
*/
default void wasFirstTabCreated() {}
/**
* Called when the last tab of the {@link IncognitoTabModel} is closed.
*/
default void didBecomeEmpty() {}
}
...@@ -179,6 +179,14 @@ public interface TabModelSelector { ...@@ -179,6 +179,14 @@ public interface TabModelSelector {
/** Returns whether reparenting is in progress. */ /** Returns whether reparenting is in progress. */
boolean isReparentingInProgress(); boolean isReparentingInProgress();
/**
* Subscribe an {@link IncognitoTabModelObserver} to events that the {@link IncognitoTabModel}
* in this selector emits. The model could be observed directly, but observing the
* selector allows an observer to subscribe itself before the model is created.
* @param incognitoObserver The observer to subscribe.
*/
void addIncognitoTabModelObserver(IncognitoTabModelObserver incognitoObserver);
/** /**
* Destroy all owned {@link TabModel}s and {@link Tab}s referenced by this selector. * Destroy all owned {@link TabModel}s and {@link Tab}s referenced by this selector.
*/ */
......
...@@ -468,9 +468,7 @@ public class ChromeActivityTestRule<T extends ChromeActivity> extends ActivityTe ...@@ -468,9 +468,7 @@ public class ChromeActivityTestRule<T extends ChromeActivity> extends ActivityTe
* *
* TODO(yolandyan): split this into the seperate test rule, this only applies to tabbed mode * TODO(yolandyan): split this into the seperate test rule, this only applies to tabbed mode
*/ */
public void newIncognitoTabFromMenu() { public Tab newIncognitoTabFromMenu() {
Tab tab = null;
final CallbackHelper createdCallback = new CallbackHelper(); final CallbackHelper createdCallback = new CallbackHelper();
final CallbackHelper selectedCallback = new CallbackHelper(); final CallbackHelper selectedCallback = new CallbackHelper();
...@@ -504,12 +502,13 @@ public class ChromeActivityTestRule<T extends ChromeActivity> extends ActivityTe ...@@ -504,12 +502,13 @@ public class ChromeActivityTestRule<T extends ChromeActivity> extends ActivityTe
} }
incognitoTabModel.removeObserver(observer); incognitoTabModel.removeObserver(observer);
tab = getActivity().getActivityTab(); Tab tab = getActivity().getActivityTab();
ChromeTabUtils.waitForTabPageLoaded(tab, (String) null); ChromeTabUtils.waitForTabPageLoaded(tab, (String) null);
NewTabPageTestUtils.waitForNtpLoaded(tab); NewTabPageTestUtils.waitForNtpLoaded(tab);
InstrumentationRegistry.getInstrumentation().waitForIdleSync(); InstrumentationRegistry.getInstrumentation().waitForIdleSync();
Log.d(TAG, "newIncognitoTabFromMenu <<"); Log.d(TAG, "newIncognitoTabFromMenu <<");
return tab;
} }
/** /**
......
...@@ -11,6 +11,8 @@ import org.chromium.chrome.browser.tab.TabCreationState; ...@@ -11,6 +11,8 @@ import org.chromium.chrome.browser.tab.TabCreationState;
import org.chromium.chrome.browser.tab.TabLaunchType; import org.chromium.chrome.browser.tab.TabLaunchType;
import org.chromium.chrome.browser.tab.TabSelectionType; import org.chromium.chrome.browser.tab.TabSelectionType;
import org.chromium.chrome.browser.tabmodel.EmptyTabModel; import org.chromium.chrome.browser.tabmodel.EmptyTabModel;
import org.chromium.chrome.browser.tabmodel.IncognitoTabModel;
import org.chromium.chrome.browser.tabmodel.IncognitoTabModelObserver;
import org.chromium.chrome.browser.tabmodel.TabModel; import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.tabmodel.TabModelObserver; import org.chromium.chrome.browser.tabmodel.TabModelObserver;
...@@ -19,7 +21,7 @@ import java.util.ArrayList; ...@@ -19,7 +21,7 @@ import java.util.ArrayList;
/** /**
* Almost empty implementation to mock a TabModel. It only handles tab creation and queries. * Almost empty implementation to mock a TabModel. It only handles tab creation and queries.
*/ */
public class MockTabModel extends EmptyTabModel { public class MockTabModel extends EmptyTabModel implements IncognitoTabModel {
/** /**
* Used to create different kinds of Tabs. If a MockTabModelDelegate is not provided, regular * Used to create different kinds of Tabs. If a MockTabModelDelegate is not provided, regular
* Tabs are produced. * Tabs are produced.
...@@ -114,4 +116,10 @@ public class MockTabModel extends EmptyTabModel { ...@@ -114,4 +116,10 @@ public class MockTabModel extends EmptyTabModel {
public void removeObserver(TabModelObserver observer) { public void removeObserver(TabModelObserver observer) {
mObservers.removeObserver(observer); mObservers.removeObserver(observer);
} }
@Override
public void addIncognitoObserver(IncognitoTabModelObserver observer) {}
@Override
public void removeIncognitoObserver(IncognitoTabModelObserver observer) {}
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment