Commit 57c21ec7 authored by Pavel Shmakov's avatar Pavel Shmakov Committed by Commit Bot

Extract url navigation and observer management out of

CustomTabActivityTabController

Extracting CustomTabNavigationController will consist of (at least) two
steps:
- Extracting navigate() method
- Extracting back/close navigation

This CL is the first step. Currently the url navigation code is in
CustomTabActivityTabController#loadUrlInTab. In addition to exposing
this public method, the TabController also calls it itself to load the
initial url when the tab is ready. Both of these things are not good
from single responsibility perspective, and need a separate class each.

To achieve that it became necessary to expose the way the initial tab
was created and update the Observer interface correspondingly. To keep
things tidy, holding onto the Tab, the creation method, and observer
management was extracted into CustomTabActivityTabProvider (this is
something we were planning to do eventually anyway).

In this CL the code becomes structured as follows:

TabController - minds the tab creation and initialization business, no
longer navigates to any urls. Updates TabProvider (via package-local
methods).

TabProvider - hold the current tab and information about how the
initial tab was created. Dispatches changes to observers. Typically,
the features should only need to use TabProvider, but not
TabController.

NavigationController - navigates to urls. Will also contain back/close
navigation in future.

InitialPageLoader - observes TabController and commands
NavigationController to navigate to the initial url.

Bug: 916528
Change-Id: Ic26ef955ab8e3e0bd926461c71ef740361686f17
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1530884
Commit-Queue: Pavel Shmakov <pshmakov@chromium.org>
Reviewed-by: default avatarPeter Conn <peconn@chromium.org>
Reviewed-by: default avatarYusuf Ozuysal <yusufo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#645309}
parent 3a4d480c
...@@ -459,8 +459,12 @@ chrome_java_sources = [ ...@@ -459,8 +459,12 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/customtabs/SeparateTaskCustomTabActivity9.java", "java/src/org/chromium/chrome/browser/customtabs/SeparateTaskCustomTabActivity9.java",
"java/src/org/chromium/chrome/browser/customtabs/SeparateTaskManagedCustomTabActivity.java", "java/src/org/chromium/chrome/browser/customtabs/SeparateTaskManagedCustomTabActivity.java",
"java/src/org/chromium/chrome/browser/customtabs/TabObserverRegistrar.java", "java/src/org/chromium/chrome/browser/customtabs/TabObserverRegistrar.java",
"java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityInitialPageLoader.java",
"java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationController.java",
"java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java", "java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java",
"java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabFactory.java", "java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabFactory.java",
"java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabProvider.java",
"java/src/org/chromium/chrome/browser/customtabs/content/TabCreationMode.java",
"java/src/org/chromium/chrome/browser/customtabs/dependency_injection/CustomTabActivityComponent.java", "java/src/org/chromium/chrome/browser/customtabs/dependency_injection/CustomTabActivityComponent.java",
"java/src/org/chromium/chrome/browser/customtabs/dependency_injection/CustomTabActivityModule.java", "java/src/org/chromium/chrome/browser/customtabs/dependency_injection/CustomTabActivityModule.java",
"java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ActivityDelegate.java", "java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ActivityDelegate.java",
......
...@@ -58,7 +58,9 @@ chrome_junit_test_java_sources = [ ...@@ -58,7 +58,9 @@ chrome_junit_test_java_sources = [
"junit/src/org/chromium/chrome/browser/crash/LogcatExtractionRunnableUnitTest.java", "junit/src/org/chromium/chrome/browser/crash/LogcatExtractionRunnableUnitTest.java",
"junit/src/org/chromium/chrome/browser/customtabs/CloseButtonNavigatorTest.java", "junit/src/org/chromium/chrome/browser/customtabs/CloseButtonNavigatorTest.java",
"junit/src/org/chromium/chrome/browser/customtabs/NavigationInfoCaptureTriggerTest.java", "junit/src/org/chromium/chrome/browser/customtabs/NavigationInfoCaptureTriggerTest.java",
"junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java",
"junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerTest.java", "junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerTest.java",
"junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityUrlLoadingTest.java",
"junit/src/org/chromium/chrome/browser/display_cutout/DisplayCutoutControllerTest.java", "junit/src/org/chromium/chrome/browser/display_cutout/DisplayCutoutControllerTest.java",
"junit/src/org/chromium/chrome/browser/download/DownloadResumptionSchedulerTest.java", "junit/src/org/chromium/chrome/browser/download/DownloadResumptionSchedulerTest.java",
"junit/src/org/chromium/chrome/browser/download/DownloadSharedPreferenceEntryTest.java", "junit/src/org/chromium/chrome/browser/download/DownloadSharedPreferenceEntryTest.java",
......
...@@ -5,11 +5,11 @@ ...@@ -5,11 +5,11 @@
package org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller; package org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller;
import android.support.annotation.IntDef; import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.customtabs.CustomTabsService; import android.support.customtabs.CustomTabsService;
import org.chromium.base.ObserverList; import org.chromium.base.ObserverList;
import org.chromium.chrome.browser.ActivityTabProvider;
import org.chromium.chrome.browser.ChromeFeatureList; import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.chrome.browser.browserservices.Origin; import org.chromium.chrome.browser.browserservices.Origin;
import org.chromium.chrome.browser.browserservices.OriginVerifier; import org.chromium.chrome.browser.browserservices.OriginVerifier;
...@@ -17,7 +17,7 @@ import org.chromium.chrome.browser.browserservices.trustedwebactivityui.TrustedW ...@@ -17,7 +17,7 @@ import org.chromium.chrome.browser.browserservices.trustedwebactivityui.TrustedW
import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider; import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
import org.chromium.chrome.browser.customtabs.CustomTabsConnection; import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
import org.chromium.chrome.browser.customtabs.TabObserverRegistrar; import org.chromium.chrome.browser.customtabs.TabObserverRegistrar;
import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabController; import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabProvider;
import org.chromium.chrome.browser.dependency_injection.ActivityScope; import org.chromium.chrome.browser.dependency_injection.ActivityScope;
import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher; import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
import org.chromium.chrome.browser.lifecycle.Destroyable; import org.chromium.chrome.browser.lifecycle.Destroyable;
...@@ -49,7 +49,7 @@ public class TrustedWebActivityVerifier implements NativeInitObserver, Destroyab ...@@ -49,7 +49,7 @@ public class TrustedWebActivityVerifier implements NativeInitObserver, Destroyab
private final Lazy<ClientAppDataRecorder> mClientAppDataRecorder; private final Lazy<ClientAppDataRecorder> mClientAppDataRecorder;
private final CustomTabsConnection mCustomTabsConnection; private final CustomTabsConnection mCustomTabsConnection;
private final CustomTabIntentDataProvider mIntentDataProvider; private final CustomTabIntentDataProvider mIntentDataProvider;
private final ActivityTabProvider mActivityTabProvider; private final CustomTabActivityTabProvider mTabProvider;
private final TabObserverRegistrar mTabObserverRegistrar; private final TabObserverRegistrar mTabObserverRegistrar;
private final String mClientPackageName; private final String mClientPackageName;
private final OriginVerifier mOriginVerifier; private final OriginVerifier mOriginVerifier;
...@@ -94,20 +94,14 @@ public class TrustedWebActivityVerifier implements NativeInitObserver, Destroyab ...@@ -94,20 +94,14 @@ public class TrustedWebActivityVerifier implements NativeInitObserver, Destroyab
} }
}; };
private final CustomTabActivityTabController.Observer mVerifyOnTabSwitchObserver = private final CustomTabActivityTabProvider.Observer mVerifyOnTabSwitchObserver =
new CustomTabActivityTabController.Observer() { new CustomTabActivityTabProvider.Observer() {
@Override @Override
public void onTabChanged() { public void onTabSwapped(@NonNull Tab tab) {
// During startup, onTabChanged is called before the ActivityTabProvider has
// updated to have a Tab. Subsequent calls to onTabChanged occur after the
// corresponding ActivityTabProvider update. Initial startup is covered by the
// verify on page load observer, so it's fine to no-op here.
if (mActivityTabProvider.getActivityTab() == null) return;
// When a link with target="_blank" is followed and the user navigates back, we // When a link with target="_blank" is followed and the user navigates back, we
// don't get the onDidFinishNavigation event (because the original page wasn't // don't get the onDidFinishNavigation event (because the original page wasn't
// navigated away from, it was only ever hidden). https://crbug.com/942088 // navigated away from, it was only ever hidden). https://crbug.com/942088
verify(new Origin(mActivityTabProvider.getActivityTab().getUrl())); verify(new Origin(tab.getUrl()));
} }
}; };
...@@ -117,13 +111,12 @@ public class TrustedWebActivityVerifier implements NativeInitObserver, Destroyab ...@@ -117,13 +111,12 @@ public class TrustedWebActivityVerifier implements NativeInitObserver, Destroyab
CustomTabsConnection customTabsConnection, CustomTabsConnection customTabsConnection,
ActivityLifecycleDispatcher lifecycleDispatcher, ActivityLifecycleDispatcher lifecycleDispatcher,
TabObserverRegistrar tabObserverRegistrar, TabObserverRegistrar tabObserverRegistrar,
ActivityTabProvider activityTabProvider,
OriginVerifier.Factory originVerifierFactory, OriginVerifier.Factory originVerifierFactory,
CustomTabActivityTabController activityTabController) { CustomTabActivityTabProvider tabProvider) {
mClientAppDataRecorder = clientAppDataRecorder; mClientAppDataRecorder = clientAppDataRecorder;
mCustomTabsConnection = customTabsConnection; mCustomTabsConnection = customTabsConnection;
mIntentDataProvider = intentDataProvider; mIntentDataProvider = intentDataProvider;
mActivityTabProvider = activityTabProvider; mTabProvider = tabProvider;
mTabObserverRegistrar = tabObserverRegistrar; mTabObserverRegistrar = tabObserverRegistrar;
mClientPackageName = customTabsConnection.getClientPackageNameForSession( mClientPackageName = customTabsConnection.getClientPackageNameForSession(
intentDataProvider.getSession()); intentDataProvider.getSession());
...@@ -132,7 +125,7 @@ public class TrustedWebActivityVerifier implements NativeInitObserver, Destroyab ...@@ -132,7 +125,7 @@ public class TrustedWebActivityVerifier implements NativeInitObserver, Destroyab
mOriginVerifier = originVerifierFactory.create(mClientPackageName, RELATIONSHIP); mOriginVerifier = originVerifierFactory.create(mClientPackageName, RELATIONSHIP);
tabObserverRegistrar.registerTabObserver(mVerifyOnPageLoadObserver); tabObserverRegistrar.registerTabObserver(mVerifyOnPageLoadObserver);
activityTabController.addObserver(mVerifyOnTabSwitchObserver); tabProvider.addObserver(mVerifyOnTabSwitchObserver);
lifecycleDispatcher.register(this); lifecycleDispatcher.register(this);
} }
...@@ -215,8 +208,8 @@ public class TrustedWebActivityVerifier implements NativeInitObserver, Destroyab ...@@ -215,8 +208,8 @@ public class TrustedWebActivityVerifier implements NativeInitObserver, Destroyab
private void onVerificationResult(Origin origin, boolean verified) { private void onVerificationResult(Origin origin, boolean verified) {
mOriginsToVerify.remove(origin); mOriginsToVerify.remove(origin);
if (verified) registerClientAppData(origin); if (verified) registerClientAppData(origin);
boolean stillOnSameOrigin = Tab tab = mTabProvider.getTab();
origin.equals(new Origin(mActivityTabProvider.getActivityTab().getUrl())); boolean stillOnSameOrigin = tab != null && origin.equals(new Origin(tab.getUrl()));
if (stillOnSameOrigin) { if (stillOnSameOrigin) {
updateState(origin, verified ? VerificationStatus.SUCCESS : VerificationStatus.FAILURE); updateState(origin, verified ? VerificationStatus.SUCCESS : VerificationStatus.FAILURE);
} }
......
...@@ -18,6 +18,7 @@ import android.os.Bundle; ...@@ -18,6 +18,7 @@ import android.os.Bundle;
import android.os.StrictMode; import android.os.StrictMode;
import android.provider.Browser; import android.provider.Browser;
import android.support.annotation.IntDef; import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.customtabs.CustomTabsIntent; import android.support.customtabs.CustomTabsIntent;
import android.support.customtabs.CustomTabsSessionToken; import android.support.customtabs.CustomTabsSessionToken;
...@@ -56,8 +57,11 @@ import org.chromium.chrome.browser.browserservices.BrowserSessionContentHandler; ...@@ -56,8 +57,11 @@ import org.chromium.chrome.browser.browserservices.BrowserSessionContentHandler;
import org.chromium.chrome.browser.browserservices.BrowserSessionContentUtils; import org.chromium.chrome.browser.browserservices.BrowserSessionContentUtils;
import org.chromium.chrome.browser.compositor.layouts.LayoutManager; import org.chromium.chrome.browser.compositor.layouts.LayoutManager;
import org.chromium.chrome.browser.contextual_suggestions.ContextualSuggestionsModule; import org.chromium.chrome.browser.contextual_suggestions.ContextualSuggestionsModule;
import org.chromium.chrome.browser.customtabs.content.CustomTabActivityNavigationController;
import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabController; import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabController;
import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabFactory; import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabFactory;
import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabProvider;
import org.chromium.chrome.browser.customtabs.content.TabCreationMode;
import org.chromium.chrome.browser.customtabs.dependency_injection.CustomTabActivityComponent; import org.chromium.chrome.browser.customtabs.dependency_injection.CustomTabActivityComponent;
import org.chromium.chrome.browser.customtabs.dependency_injection.CustomTabActivityModule; import org.chromium.chrome.browser.customtabs.dependency_injection.CustomTabActivityModule;
import org.chromium.chrome.browser.customtabs.dynamicmodule.DynamicModuleCoordinator; import org.chromium.chrome.browser.customtabs.dynamicmodule.DynamicModuleCoordinator;
...@@ -127,7 +131,9 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -127,7 +131,9 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
private CustomTabBottomBarDelegate mBottomBarDelegate; private CustomTabBottomBarDelegate mBottomBarDelegate;
private CustomTabTopBarDelegate mTopBarDelegate; private CustomTabTopBarDelegate mTopBarDelegate;
private CustomTabActivityTabController mTabController; private CustomTabActivityTabController mTabController;
private CustomTabActivityTabProvider mTabProvider;
private CustomTabActivityTabFactory mTabFactory; private CustomTabActivityTabFactory mTabFactory;
private CustomTabActivityNavigationController mNavigationController;
// This is to give the right package name while using the client's resources during an // This is to give the right package name while using the client's resources during an
// overridePendingTransition call. // overridePendingTransition call.
...@@ -161,9 +167,21 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -161,9 +167,21 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
return (getIntent().getFlags() & separateTaskFlags) != 0; return (getIntent().getFlags() & separateTaskFlags) != 0;
} }
private CustomTabActivityTabController.Observer mTabChangeObserver = () -> { private CustomTabActivityTabProvider.Observer mTabChangeObserver =
resetPostMessageHandlersForCurrentSession(); new CustomTabActivityTabProvider.Observer() {
if (mTabController.getTab() == null) { @Override
public void onInitialTabCreated(@NonNull Tab tab, int mode) {
resetPostMessageHandlersForCurrentSession();
}
@Override
public void onTabSwapped(@NonNull Tab tab) {
resetPostMessageHandlersForCurrentSession();
}
@Override
public void onAllTabsClosed() {
resetPostMessageHandlersForCurrentSession();
finishAndClose(false); finishAndClose(false);
} }
}; };
...@@ -216,8 +234,8 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -216,8 +234,8 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
mIntentDataProvider = new CustomTabIntentDataProvider(getIntent(), this); mIntentDataProvider = new CustomTabIntentDataProvider(getIntent(), this);
super.preInflationStartup(); super.preInflationStartup();
mTabController.addObserver(mTabChangeObserver); mTabProvider.addObserver(mTabChangeObserver);
// We might have missed an onTabChanged event. // We might have missed an onInitialTabCreated event.
resetPostMessageHandlersForCurrentSession(); resetPostMessageHandlersForCurrentSession();
mSession = mIntentDataProvider.getSession(); mSession = mIntentDataProvider.getSession();
...@@ -274,9 +292,9 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -274,9 +292,9 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
// Properly attach tab's infobar to the view hierarchy, as the main tab might have been // Properly attach tab's infobar to the view hierarchy, as the main tab might have been
// initialized prior to inflation. // initialized prior to inflation.
if (mTabController.getTab() != null) { if (mTabProvider.getTab() != null) {
ViewGroup bottomContainer = (ViewGroup) findViewById(R.id.bottom_container); ViewGroup bottomContainer = (ViewGroup) findViewById(R.id.bottom_container);
InfoBarContainer.get(mTabController.getTab()).setParentView(bottomContainer); InfoBarContainer.get(mTabProvider.getTab()).setParentView(bottomContainer);
} }
// Setting task title and icon to be null will preserve the client app's title and icon. // Setting task title and icon to be null will preserve the client app's title and icon.
...@@ -349,7 +367,7 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -349,7 +367,7 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
params.setUrl(DataReductionProxySettings.getInstance() params.setUrl(DataReductionProxySettings.getInstance()
.maybeRewriteWebliteUrl(params.getUrl())); .maybeRewriteWebliteUrl(params.getUrl()));
} }
mTabController.loadUrlInTab(params, timestamp); mNavigationController.navigate(params, timestamp);
} }
@Override @Override
...@@ -454,7 +472,7 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -454,7 +472,7 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
} }
private void resetPostMessageHandlersForCurrentSession() { private void resetPostMessageHandlersForCurrentSession() {
Tab tab = mTabController.getTab(); Tab tab = mTabProvider.getTab();
WebContents webContents = tab == null ? null : tab.getWebContents(); WebContents webContents = tab == null ? null : tab.getWebContents();
mConnection.resetPostMessageHandlerForSession( mConnection.resetPostMessageHandlerForSession(
mIntentDataProvider.getSession(), webContents); mIntentDataProvider.getSession(), webContents);
...@@ -538,7 +556,11 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -538,7 +556,11 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
public void onStartWithNative() { public void onStartWithNative() {
super.onStartWithNative(); super.onStartWithNative();
BrowserSessionContentUtils.setActiveContentHandler(mBrowserSessionContentHandler); BrowserSessionContentUtils.setActiveContentHandler(mBrowserSessionContentHandler);
if (mTabController.earlyCreatedTabIsReady()) postDeferredStartupIfNeeded(); @TabCreationMode int mode = mTabProvider.getInitialTabCreationMode();
boolean earlyCreatedTabIsReady =
(mode == TabCreationMode.HIDDEN || mode == TabCreationMode.EARLY)
&& !mTabProvider.getTab().isLoading();
if (earlyCreatedTabIsReady) postDeferredStartupIfNeeded();
} }
@Override @Override
...@@ -577,7 +599,7 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -577,7 +599,7 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
@Override @Override
@Nullable @Nullable
public Tab getActivityTab() { public Tab getActivityTab() {
return mTabController.getTab(); return mTabProvider.getTab();
} }
@Override @Override
...@@ -969,7 +991,7 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -969,7 +991,7 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
return false; return false;
} }
Tab tab = mTabController.getTab(); Tab tab = mTabProvider.getTab();
if (tab != null && tab.isPreview()) { if (tab != null && tab.isPreview()) {
return false; return false;
} }
...@@ -1007,8 +1029,11 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -1007,8 +1029,11 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
mTabObserverRegistrar = component.resolveTabObserverRegistrar(); mTabObserverRegistrar = component.resolveTabObserverRegistrar();
mTabController = component.resolveTabController(); mTabController = component.resolveTabController();
mTabProvider = component.resolveTabProvider();
mTabFactory = component.resolveTabFactory(); mTabFactory = component.resolveTabFactory();
component.resolveUmaTracker(); component.resolveUmaTracker();
mNavigationController = component.resolveNavigationController();
component.resolveInitialPageLoader();
if (mIntentDataProvider.isTrustedWebActivity()) { if (mIntentDataProvider.isTrustedWebActivity()) {
component.resolveTrustedWebActivityCoordinator(); component.resolveTrustedWebActivityCoordinator();
......
...@@ -7,15 +7,19 @@ package org.chromium.chrome.browser.customtabs; ...@@ -7,15 +7,19 @@ package org.chromium.chrome.browser.customtabs;
import android.support.customtabs.CustomTabsCallback; import android.support.customtabs.CustomTabsCallback;
import android.support.customtabs.CustomTabsSessionToken; import android.support.customtabs.CustomTabsSessionToken;
import org.chromium.chrome.browser.dependency_injection.ActivityScope;
import org.chromium.chrome.browser.tab.EmptyTabObserver; import org.chromium.chrome.browser.tab.EmptyTabObserver;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.Tab.TabHidingType; import org.chromium.chrome.browser.tab.Tab.TabHidingType;
import org.chromium.chrome.browser.tabmodel.TabSelectionType; import org.chromium.chrome.browser.tabmodel.TabSelectionType;
import org.chromium.components.security_state.ConnectionSecurityLevel; import org.chromium.components.security_state.ConnectionSecurityLevel;
import javax.inject.Inject;
/** /**
* An observer for firing navigation events on {@link CustomTabsCallback}. * An observer for firing navigation events on {@link CustomTabsCallback}.
*/ */
@ActivityScope
public class CustomTabNavigationEventObserver extends EmptyTabObserver { public class CustomTabNavigationEventObserver extends EmptyTabObserver {
// An operation was aborted (due to user action). Should match the value in net_error_list.h. // An operation was aborted (due to user action). Should match the value in net_error_list.h.
private static final int NET_ERROR_ABORTED = -3; private static final int NET_ERROR_ABORTED = -3;
...@@ -23,9 +27,10 @@ public class CustomTabNavigationEventObserver extends EmptyTabObserver { ...@@ -23,9 +27,10 @@ public class CustomTabNavigationEventObserver extends EmptyTabObserver {
private final CustomTabsSessionToken mSessionToken; private final CustomTabsSessionToken mSessionToken;
private final CustomTabsConnection mConnection; private final CustomTabsConnection mConnection;
public CustomTabNavigationEventObserver(CustomTabsSessionToken sessionToken, @Inject
public CustomTabNavigationEventObserver(CustomTabIntentDataProvider intentDataProvider,
CustomTabsConnection connection) { CustomTabsConnection connection) {
mSessionToken = sessionToken; mSessionToken = intentDataProvider.getSession();
mConnection = connection; mConnection = connection;
} }
......
...@@ -5,10 +5,12 @@ ...@@ -5,10 +5,12 @@
package org.chromium.chrome.browser.customtabs; package org.chromium.chrome.browser.customtabs;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.support.annotation.NonNull;
import android.view.Gravity; import android.view.Gravity;
import android.view.WindowManager; import android.view.WindowManager;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabProvider;
import org.chromium.chrome.browser.payments.ServiceWorkerPaymentAppBridge; import org.chromium.chrome.browser.payments.ServiceWorkerPaymentAppBridge;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
...@@ -21,22 +23,27 @@ import org.chromium.ui.display.DisplayUtil; ...@@ -21,22 +23,27 @@ import org.chromium.ui.display.DisplayUtil;
public class PaymentHandlerActivity extends CustomTabActivity { public class PaymentHandlerActivity extends CustomTabActivity {
private static final double BOTTOM_SHEET_HEIGHT_RATIO = 0.7; private static final double BOTTOM_SHEET_HEIGHT_RATIO = 0.7;
private boolean mHaveNotifiedServiceWorker; private boolean mHaveNotifiedServiceWorker;
private boolean mTabObserverAdded;
@Override @Override
public void preInflationStartup() { public void preInflationStartup() {
super.preInflationStartup(); super.preInflationStartup();
updateHeight(); updateHeight();
getComponent().resolveTabController().addObserver(this::addTabObserverIfTabReady); addObserverForPaymentsWhenTabReady();
addTabObserverIfTabReady();
} }
private void addTabObserverIfTabReady() { private void addObserverForPaymentsWhenTabReady() {
if (mTabObserverAdded) return; CustomTabActivityTabProvider tabProvider = getComponent().resolveTabProvider();
Tab tab = getComponent().resolveTabController().getTab(); Tab tab = tabProvider.getTab();
if (tab != null) { if (tab != null) {
ServiceWorkerPaymentAppBridge.addTabObserverForPaymentRequestTab(tab); ServiceWorkerPaymentAppBridge.addTabObserverForPaymentRequestTab(tab);
mTabObserverAdded = true; } else {
tabProvider.addObserver(new CustomTabActivityTabProvider.Observer() {
@Override
public void onInitialTabCreated(@NonNull Tab tab, int mode) {
tabProvider.removeObserver(this);
ServiceWorkerPaymentAppBridge.addTabObserverForPaymentRequestTab(tab);
}
});
} }
} }
......
// Copyright 2019 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.customtabs.content;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import org.chromium.chrome.browser.IntentHandler;
import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
import org.chromium.chrome.browser.customtabs.CustomTabNavigationEventObserver;
import org.chromium.chrome.browser.customtabs.CustomTabObserver;
import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.util.UrlUtilities;
import org.chromium.content_public.browser.LoadUrlParams;
import javax.inject.Inject;
import dagger.Lazy;
/**
* Loads the url received in the Intent as soon as the initial tab is created.
*
* Lifecycle: should be created once on Activity creation. When finishes its work, detaches
* observers, and should get gc-ed. Consider adding @ActivityScope if this policy changes.
*/
public class CustomTabActivityInitialPageLoader {
private final CustomTabActivityTabProvider mTabProvider;
private final CustomTabIntentDataProvider mIntentDataProvider;
private final Lazy<CustomTabObserver> mCustomTabObserver;
private final CustomTabNavigationEventObserver mNavigationEventObserver;
private final CustomTabActivityNavigationController mNavigationController;
@Nullable
private final String mSpeculatedUrl;
@Inject
public CustomTabActivityInitialPageLoader(CustomTabActivityTabProvider tabProvider,
CustomTabIntentDataProvider intentDataProvider, CustomTabsConnection connection,
Lazy<CustomTabObserver> customTabObserver,
CustomTabNavigationEventObserver navigationEventObserver,
CustomTabActivityNavigationController navigationController) {
mTabProvider = tabProvider;
mIntentDataProvider = intentDataProvider;
mCustomTabObserver = customTabObserver;
mNavigationEventObserver = navigationEventObserver;
mNavigationController = navigationController;
mSpeculatedUrl = connection.getSpeculatedUrl(mIntentDataProvider.getSession());
mTabProvider.addObserver(new CustomTabActivityTabProvider.Observer() {
@Override
public void onInitialTabCreated(@NonNull Tab tab, @TabCreationMode int mode) {
loadInitialUrlIfNecessary(tab, mode);
mTabProvider.removeObserver(this);
}
});
}
private void loadInitialUrlIfNecessary(@NonNull Tab tab, @TabCreationMode int mode) {
if (mode == TabCreationMode.RESTORED) return;
if (mode == TabCreationMode.HIDDEN) {
handleInitialLoadForHiddedTab(tab);
return;
}
LoadUrlParams params = new LoadUrlParams(mIntentDataProvider.getUrlToLoad());
navigate(params);
}
// The hidden tab case needs a bit of special treatment.
private void handleInitialLoadForHiddedTab(Tab tab) {
String url = mIntentDataProvider.getUrlToLoad();
// Manually generating metrics in case the hidden tab has completely finished loading.
if (!tab.isLoading() && !tab.isShowingErrorPage()) {
mCustomTabObserver.get().onPageLoadStarted(tab, url);
mCustomTabObserver.get().onPageLoadFinished(tab, url);
mNavigationEventObserver.onPageLoadStarted(tab, url);
mNavigationEventObserver.onPageLoadFinished(tab, url);
}
// No actual load to do if the hidden tab already has the exact correct url.
if (TextUtils.equals(mSpeculatedUrl, url)) {
return;
}
LoadUrlParams params = new LoadUrlParams(url);
// The following block is a hack that deals with urls preloaded with
// the wrong fragment. Does an extra pageload and replaces history.
if (mSpeculatedUrl != null
&& UrlUtilities.urlsFragmentsDiffer(mSpeculatedUrl, url)) {
params.setShouldReplaceCurrentEntry(true);
}
navigate(params);
}
private void navigate(LoadUrlParams params) {
mNavigationController.navigate(params,
IntentHandler.getTimestampFromIntent(mIntentDataProvider.getIntent()));
}
}
// Copyright 2019 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.customtabs.content;
import android.os.SystemClock;
import org.chromium.chrome.browser.IntentHandler;
import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
import org.chromium.chrome.browser.customtabs.CustomTabObserver;
import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
import org.chromium.chrome.browser.dependency_injection.ActivityScope;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.ui.base.PageTransition;
import javax.inject.Inject;
import dagger.Lazy;
/**
* Responsible for navigating to new pages and going back to previous pages.
* TODO(pshmakov): move back/close navigation from CustomTabActivity into this class.
*/
@ActivityScope
public class CustomTabActivityNavigationController {
private final CustomTabActivityTabProvider mTabProvider;
private final CustomTabIntentDataProvider mIntentDataProvider;
private final CustomTabsConnection mConnection;
private final Lazy<CustomTabObserver> mCustomTabObserver;
@Inject
public CustomTabActivityNavigationController(CustomTabActivityTabProvider tabProvider,
CustomTabIntentDataProvider intentDataProvider, CustomTabsConnection connection,
Lazy<CustomTabObserver> customTabObserver) {
mTabProvider = tabProvider;
mIntentDataProvider = intentDataProvider;
mConnection = connection;
mCustomTabObserver = customTabObserver;
}
/**
* Navigates to given url.
*/
public void navigate(String url) {
navigate(new LoadUrlParams(url), SystemClock.elapsedRealtime());
}
/**
* Performs navigation using given {@link LoadUrlParams}.
* Uses provided timestamp as the initial time for tracking page loading times
* (see {@link CustomTabObserver}).
*/
public void navigate(final LoadUrlParams params, long timeStamp) {
Tab tab = mTabProvider.getTab();
if (tab == null) {
assert false;
return;
}
mCustomTabObserver.get().trackNextPageLoadFromTimestamp(tab, timeStamp);
IntentHandler.addReferrerAndHeaders(params, mIntentDataProvider.getIntent());
if (params.getReferrer() == null) {
params.setReferrer(mConnection.getReferrerForSession(mIntentDataProvider.getSession()));
}
// See ChromeTabCreator#getTransitionType(). If the sender of the intent was a WebAPK, mark
// the intent as a standard link navigation. Pass the user gesture along since one must have
// been active to open a new tab and reach here. Otherwise, mark the navigation chain as
// starting from an external intent. See crbug.com/792990.
int defaultTransition = PageTransition.LINK | PageTransition.FROM_API;
if (mIntentDataProvider.isOpenedByWebApk()) {
params.setHasUserGesture(true);
defaultTransition = PageTransition.LINK;
}
params.setTransitionType(IntentHandler.getTransitionTypeFromIntent(
mIntentDataProvider.getIntent(), defaultTransition));
tab.loadUrl(params);
}
}
/*
* Copyright 2019 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.customtabs.content;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.chromium.base.ObserverList;
import org.chromium.chrome.browser.ActivityTabProvider;
import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
import org.chromium.chrome.browser.dependency_injection.ActivityScope;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tabmodel.TabModel;
import javax.inject.Inject;
/**
* Holds the Tab currently shown in a Custom Tab activity. Unlike {@link ActivityTabProvider}, is
* aware of early created tabs that are not yet attached. Is also aware of tab swapping when
* navigating by links with target="_blank". Thus it is a single source of truth about
* the current Tab of a Custom Tab activity.
*/
@ActivityScope
public class CustomTabActivityTabProvider {
private final ObserverList<Observer> mObservers = new ObserverList<>();
@Nullable
private Tab mTab;
@TabCreationMode
private int mTabCreationMode = TabCreationMode.NONE;
@Inject
CustomTabActivityTabProvider() {}
/** Adds an {@link Observer} */
public void addObserver(Observer observer) {
mObservers.addObserver(observer);
}
/** Removes an {@link Observer} */
public void removeObserver(Observer observer) {
mObservers.removeObserver(observer);
}
/**
* Returns tab currently managed by the Custom Tab activity.
*
* The difference from {@link ActivityTabProvider#getActivityTab()} is that we may have acquired
* a hidden tab (see {@link CustomTabsConnection#takeHiddenTab}), which is not yet added to a
* {@link TabModel}. In that case this method returns the hidden tab, and ActivityTabProvider
* returns null.
*
* During reparenting, both this method and ActivityTabProvider return null.
*/
@Nullable
public Tab getTab() {
return mTab;
}
/**
* Returns a {@link TabCreationMode} specifying how the initial tab was created.
* Returns {@link TabCreationMode#NONE} if and only if the initial tab has not been yet created.
*/
@TabCreationMode
public int getInitialTabCreationMode() {
return mTabCreationMode;
}
void setInitialTab(@NonNull Tab tab, @TabCreationMode int creationMode) {
assert mTab == null;
assert creationMode != TabCreationMode.NONE;
mTab = tab;
mTabCreationMode = creationMode;
for (Observer observer : mObservers) {
observer.onInitialTabCreated(tab, creationMode);
}
}
void removeTab() {
if (mTab == null) return;
mTab = null;
mTabCreationMode = TabCreationMode.NONE;
for (Observer observer : mObservers) {
observer.onAllTabsClosed();
}
}
void swapTab(@Nullable Tab tab) {
assert mTab != null : "swapTab shouldn't be called before setInitialTab";
if (mTab == tab) return;
mTab = tab;
if (mTab == null) {
for (Observer observer : mObservers) {
observer.onAllTabsClosed();
}
} else {
for (Observer observer : mObservers) {
observer.onTabSwapped(tab);
}
}
}
/**
* Observer that gets notified about changes of the Tab currently managed by Custom Tab
* activity.
*/
public static abstract class Observer {
/**
* Fired when the initial tab has been created.
*/
public void onInitialTabCreated(@NonNull Tab tab, @TabCreationMode int mode) {}
/**
* Fired when the currently visible tab has changed when navigating by a link with
* target="_blank" or backwards.
*/
public void onTabSwapped(@NonNull Tab tab) {}
/**
* Fired when all the Tabs are closed (during shutdown or reparenting).
*
* Following a target="_blank" link creates a new tab; going back closes it and brings back
* the previous tab. In that case onTabSwapped is called, and onAllTabsClosed isn't.
* If the user unwinds the entire stack of tabs and closes the last one, then
* onAllTabsClosed is called.
*/
public void onAllTabsClosed() {}
}
}
// Copyright 2019 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.customtabs.content;
import android.support.annotation.IntDef;
import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Specifies the way the initial Tab in a Custom Tab activity was created.
*/
@IntDef({TabCreationMode.NONE,
TabCreationMode.DEFAULT, TabCreationMode.EARLY,
TabCreationMode.RESTORED, TabCreationMode.HIDDEN})
@Retention(RetentionPolicy.SOURCE)
public @interface TabCreationMode {
/** The tab has not been created yet */
int NONE = 0;
/** New tab that was created on native initialization. */
int DEFAULT = 1;
/** A tab that was restored after activity re-creation. */
int RESTORED = 2;
/**
* A new tab that was created in onPreInflationStartup, after
* {@link CustomTabsConnection#warmup)} has finished.
*/
int EARLY = 3;
/**
* A hidden tab that was created preemptively via {@link CustomTabsConnection#mayLaunchUrl}.
*/
int HIDDEN = 4;
}
\ No newline at end of file
...@@ -12,8 +12,11 @@ import org.chromium.chrome.browser.customtabs.CustomTabBottomBarDelegate; ...@@ -12,8 +12,11 @@ import org.chromium.chrome.browser.customtabs.CustomTabBottomBarDelegate;
import org.chromium.chrome.browser.customtabs.CustomTabTabPersistencePolicy; import org.chromium.chrome.browser.customtabs.CustomTabTabPersistencePolicy;
import org.chromium.chrome.browser.customtabs.CustomTabTopBarDelegate; import org.chromium.chrome.browser.customtabs.CustomTabTopBarDelegate;
import org.chromium.chrome.browser.customtabs.TabObserverRegistrar; import org.chromium.chrome.browser.customtabs.TabObserverRegistrar;
import org.chromium.chrome.browser.customtabs.content.CustomTabActivityInitialPageLoader;
import org.chromium.chrome.browser.customtabs.content.CustomTabActivityNavigationController;
import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabController; import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabController;
import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabFactory; import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabFactory;
import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabProvider;
import org.chromium.chrome.browser.customtabs.dynamicmodule.DynamicModuleCoordinator; import org.chromium.chrome.browser.customtabs.dynamicmodule.DynamicModuleCoordinator;
import org.chromium.chrome.browser.customtabs.dynamicmodule.DynamicModuleToolbarController; import org.chromium.chrome.browser.customtabs.dynamicmodule.DynamicModuleToolbarController;
import org.chromium.chrome.browser.dependency_injection.ActivityScope; import org.chromium.chrome.browser.dependency_injection.ActivityScope;
...@@ -41,6 +44,9 @@ public interface CustomTabActivityComponent extends ChromeActivityComponent { ...@@ -41,6 +44,9 @@ public interface CustomTabActivityComponent extends ChromeActivityComponent {
CustomTabActivityTabController resolveTabController(); CustomTabActivityTabController resolveTabController();
CustomTabActivityTabFactory resolveTabFactory(); CustomTabActivityTabFactory resolveTabFactory();
CustomTabActivityLifecycleUmaTracker resolveUmaTracker(); CustomTabActivityLifecycleUmaTracker resolveUmaTracker();
CustomTabActivityInitialPageLoader resolveInitialPageLoader();
CustomTabActivityNavigationController resolveNavigationController();
CustomTabActivityTabProvider resolveTabProvider();
CustomTabTabPersistencePolicy resolveTabPersistencePolicy(); // For testing CustomTabTabPersistencePolicy resolveTabPersistencePolicy(); // For testing
} }
...@@ -9,7 +9,6 @@ import static org.chromium.chrome.browser.customtabs.dynamicmodule.DynamicModule ...@@ -9,7 +9,6 @@ import static org.chromium.chrome.browser.customtabs.dynamicmodule.DynamicModule
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.os.SystemClock;
import android.support.annotation.IntDef; import android.support.annotation.IntDef;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.customtabs.CustomTabsService; import android.support.customtabs.CustomTabsService;
...@@ -33,7 +32,8 @@ import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider; ...@@ -33,7 +32,8 @@ import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
import org.chromium.chrome.browser.customtabs.CustomTabTopBarDelegate; import org.chromium.chrome.browser.customtabs.CustomTabTopBarDelegate;
import org.chromium.chrome.browser.customtabs.CustomTabsConnection; import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
import org.chromium.chrome.browser.customtabs.TabObserverRegistrar; import org.chromium.chrome.browser.customtabs.TabObserverRegistrar;
import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabController; import org.chromium.chrome.browser.customtabs.content.CustomTabActivityNavigationController;
import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabProvider;
import org.chromium.chrome.browser.dependency_injection.ActivityScope; import org.chromium.chrome.browser.dependency_injection.ActivityScope;
import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager; import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher; import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
...@@ -44,7 +44,6 @@ import org.chromium.chrome.browser.tab.EmptyTabObserver; ...@@ -44,7 +44,6 @@ import org.chromium.chrome.browser.tab.EmptyTabObserver;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabObserver; import org.chromium.chrome.browser.tab.TabObserver;
import org.chromium.chrome.browser.util.UrlUtilities; import org.chromium.chrome.browser.util.UrlUtilities;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.NavigationHandle; import org.chromium.content_public.browser.NavigationHandle;
import org.chromium.content_public.browser.UiThreadTaskTraits; import org.chromium.content_public.browser.UiThreadTaskTraits;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
...@@ -65,7 +64,8 @@ public class DynamicModuleCoordinator implements NativeInitObserver, Destroyable ...@@ -65,7 +64,8 @@ public class DynamicModuleCoordinator implements NativeInitObserver, Destroyable
private final CustomTabIntentDataProvider mIntentDataProvider; private final CustomTabIntentDataProvider mIntentDataProvider;
private final TabObserverRegistrar mTabObserverRegistrar; private final TabObserverRegistrar mTabObserverRegistrar;
private final CustomTabsConnection mConnection; private final CustomTabsConnection mConnection;
private final CustomTabActivityTabController mTabController; private final CustomTabActivityTabProvider mTabProvider;
private final CustomTabActivityNavigationController mNavigationController;
private final ChromeActivity mActivity; private final ChromeActivity mActivity;
...@@ -163,18 +163,20 @@ public class DynamicModuleCoordinator implements NativeInitObserver, Destroyable ...@@ -163,18 +163,20 @@ public class DynamicModuleCoordinator implements NativeInitObserver, Destroyable
CloseButtonNavigator closeButtonNavigator, CloseButtonNavigator closeButtonNavigator,
TabObserverRegistrar tabObserverRegistrar, TabObserverRegistrar tabObserverRegistrar,
ActivityLifecycleDispatcher activityLifecycleDispatcher, ActivityLifecycleDispatcher activityLifecycleDispatcher,
CustomTabActivityNavigationController navigationController,
ActivityDelegate activityDelegate, ActivityDelegate activityDelegate,
Lazy<CustomTabTopBarDelegate> topBarDelegate, Lazy<CustomTabTopBarDelegate> topBarDelegate,
Lazy<CustomTabBottomBarDelegate> bottomBarDelegate, Lazy<CustomTabBottomBarDelegate> bottomBarDelegate,
Lazy<ChromeFullscreenManager> fullscreenManager, Lazy<ChromeFullscreenManager> fullscreenManager,
Lazy<DynamicModuleToolbarController> toolbarController, Lazy<DynamicModuleToolbarController> toolbarController,
CustomTabsConnection connection, ChromeActivity activity, CustomTabsConnection connection, ChromeActivity activity,
CustomTabActivityTabController tabController, CustomTabActivityTabProvider tabProvider,
DynamicModulePageLoadObserver pageLoadObserver) { DynamicModulePageLoadObserver pageLoadObserver) {
mIntentDataProvider = intentDataProvider; mIntentDataProvider = intentDataProvider;
mTabObserverRegistrar = tabObserverRegistrar; mTabObserverRegistrar = tabObserverRegistrar;
mNavigationController = navigationController;
mActivity = activity; mActivity = activity;
mTabController = tabController; mTabProvider = tabProvider;
mConnection = connection; mConnection = connection;
mTabObserverRegistrar.registerTabObserver(mModuleNavigationEventObserver); mTabObserverRegistrar.registerTabObserver(mModuleNavigationEventObserver);
...@@ -252,8 +254,7 @@ public class DynamicModuleCoordinator implements NativeInitObserver, Destroyable ...@@ -252,8 +254,7 @@ public class DynamicModuleCoordinator implements NativeInitObserver, Destroyable
} }
/* package */ void loadUri(Uri uri) { /* package */ void loadUri(Uri uri) {
mTabController.loadUrlInTab(new LoadUrlParams(uri.toString()), mNavigationController.navigate(uri.toString());
SystemClock.elapsedRealtime());
} }
@VisibleForTesting @VisibleForTesting
...@@ -425,7 +426,7 @@ public class DynamicModuleCoordinator implements NativeInitObserver, Destroyable ...@@ -425,7 +426,7 @@ public class DynamicModuleCoordinator implements NativeInitObserver, Destroyable
} }
private String getContentUrl() { private String getContentUrl() {
Tab tab = mTabController.getTab(); Tab tab = mTabProvider.getTab();
if (tab != null && tab.getWebContents() != null && !tab.getWebContents().isDestroyed() if (tab != null && tab.getWebContents() != null && !tab.getWebContents().isDestroyed()
&& tab.getWebContents().getLastCommittedUrl() != null) { && tab.getWebContents().getLastCommittedUrl() != null) {
return tab.getWebContents().getLastCommittedUrl(); return tab.getWebContents().getLastCommittedUrl();
......
...@@ -28,7 +28,6 @@ import org.mockito.MockitoAnnotations; ...@@ -28,7 +28,6 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.ActivityTabProvider;
import org.chromium.chrome.browser.ChromeFeatureList; import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.chrome.browser.browserservices.Origin; import org.chromium.chrome.browser.browserservices.Origin;
import org.chromium.chrome.browser.browserservices.OriginVerifier; import org.chromium.chrome.browser.browserservices.OriginVerifier;
...@@ -37,7 +36,7 @@ import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controll ...@@ -37,7 +36,7 @@ import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controll
import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider; import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
import org.chromium.chrome.browser.customtabs.CustomTabsConnection; import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
import org.chromium.chrome.browser.customtabs.TabObserverRegistrar; import org.chromium.chrome.browser.customtabs.TabObserverRegistrar;
import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabController; import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabProvider;
import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher; import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabObserver; import org.chromium.chrome.browser.tab.TabObserver;
...@@ -75,11 +74,10 @@ public class TrustedWebActivityVerifierTest { ...@@ -75,11 +74,10 @@ public class TrustedWebActivityVerifierTest {
@Mock ClientAppDataRecorder mClientAppDataRecorder; @Mock ClientAppDataRecorder mClientAppDataRecorder;
@Mock CustomTabsConnection mCustomTabsConnection; @Mock CustomTabsConnection mCustomTabsConnection;
@Mock CustomTabIntentDataProvider mIntentDataProvider; @Mock CustomTabIntentDataProvider mIntentDataProvider;
@Mock ActivityTabProvider mActivityTabProvider;
@Mock TabObserverRegistrar mTabObserverRegistrar; @Mock TabObserverRegistrar mTabObserverRegistrar;
@Mock ActivityLifecycleDispatcher mLifecycleDispatcher; @Mock ActivityLifecycleDispatcher mLifecycleDispatcher;
@Mock OriginVerifier.Factory mOriginVerifierFactory; @Mock OriginVerifier.Factory mOriginVerifierFactory;
@Mock CustomTabActivityTabController mCustomTabActivityTabController; @Mock CustomTabActivityTabProvider mTabProvider;
@Mock Tab mTab; @Mock Tab mTab;
@Captor ArgumentCaptor<TabObserver> mTabObserverCaptor; @Captor ArgumentCaptor<TabObserver> mTabObserverCaptor;
...@@ -92,14 +90,14 @@ public class TrustedWebActivityVerifierTest { ...@@ -92,14 +90,14 @@ public class TrustedWebActivityVerifierTest {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
when(mCustomTabsConnection.getClientPackageNameForSession(any())).thenReturn(PACKAGE_NAME); when(mCustomTabsConnection.getClientPackageNameForSession(any())).thenReturn(PACKAGE_NAME);
when(mOriginVerifierFactory.create(any(), anyInt())).thenReturn(mOriginVerifier.mock); when(mOriginVerifierFactory.create(any(), anyInt())).thenReturn(mOriginVerifier.mock);
when(mActivityTabProvider.getActivityTab()).thenReturn(mTab); when(mTabProvider.getTab()).thenReturn(mTab);
when(mIntentDataProvider.getTrustedWebActivityAdditionalOrigins()).thenReturn( when(mIntentDataProvider.getTrustedWebActivityAdditionalOrigins()).thenReturn(
Arrays.asList("https://www.origin2.com/")); Arrays.asList("https://www.origin2.com/"));
doNothing().when(mTabObserverRegistrar).registerTabObserver(mTabObserverCaptor.capture()); doNothing().when(mTabObserverRegistrar).registerTabObserver(mTabObserverCaptor.capture());
mVerifier = new TrustedWebActivityVerifier(() -> mClientAppDataRecorder, mVerifier = new TrustedWebActivityVerifier(() -> mClientAppDataRecorder,
mIntentDataProvider, mCustomTabsConnection, mLifecycleDispatcher, mIntentDataProvider, mCustomTabsConnection, mLifecycleDispatcher,
mTabObserverRegistrar, mActivityTabProvider, mOriginVerifierFactory, mTabObserverRegistrar, mOriginVerifierFactory,
mCustomTabActivityTabController); mTabProvider);
} }
@Test @Test
......
// Copyright 2019 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.customtabs.content;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.Intent;
import android.os.Bundle;
import android.support.customtabs.CustomTabsSessionToken;
import android.view.View;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.chromium.base.UserDataHost;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.browser.ActivityTabProvider;
import org.chromium.chrome.browser.ActivityTabProvider.ActivityTabObserver;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.IntentHandler;
import org.chromium.chrome.browser.WarmupManager;
import org.chromium.chrome.browser.WebContentsFactory;
import org.chromium.chrome.browser.compositor.CompositorViewHolder;
import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
import org.chromium.chrome.browser.customtabs.CustomTabDelegateFactory;
import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
import org.chromium.chrome.browser.customtabs.CustomTabNavigationEventObserver;
import org.chromium.chrome.browser.customtabs.CustomTabObserver;
import org.chromium.chrome.browser.customtabs.CustomTabTabPersistencePolicy;
import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
import org.chromium.chrome.browser.customtabs.TabObserverRegistrar;
import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tabmodel.AsyncTabParamsManager;
import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.tabmodel.TabModelSelectorImpl;
import org.chromium.chrome.browser.tabmodel.document.AsyncTabCreationParams;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.WebContents;
/**
* A TestRule that sets up the mocks and contains helper methods for JUnit/Robolectric tests scoped
* to the content layer of Custom Tabs code.
*/
public class CustomTabActivityContentTestEnvironment extends TestWatcher {
public static final String INITIAL_URL = "https://initial.com";
public static final String SPECULATED_URL = "https://speculated.com";
public static final String OTHER_URL = "https://other.com";
public final Intent intent = new Intent();
@Mock public CustomTabDelegateFactory customTabDelegateFactory;
@Mock public ChromeActivity activity;
@Mock public CustomTabsConnection connection;
@Mock public CustomTabIntentDataProvider intentDataProvider;
@Mock public TabContentManager tabContentManager;
@Mock public TabObserverRegistrar tabObserverRegistrar;
@Mock public CompositorViewHolder compositorViewHolder;
@Mock public WarmupManager warmupManager;
@Mock public CustomTabTabPersistencePolicy tabPersistencePolicy;
@Mock public CustomTabActivityTabFactory tabFactory;
@Mock public CustomTabObserver customTabObserver;
@Mock public WebContentsFactory webContentsFactory;
@Mock public ActivityTabProvider activityTabProvider;
@Mock public ActivityLifecycleDispatcher lifecycleDispatcher;
@Mock public CustomTabsSessionToken session;
@Mock public TabModelSelectorImpl tabModelSelector;
@Mock public TabModel tabModel;
@Mock public CustomTabNavigationEventObserver navigationEventObserver;
public final CustomTabActivityTabProvider tabProvider = new CustomTabActivityTabProvider();
@Captor public ArgumentCaptor<ActivityTabObserver> activityTabObserverCaptor;
// Captures the WebContents with which tabFromFactory is initialized
@Captor public ArgumentCaptor<WebContents> webContentsCaptor;
public Tab tabFromFactory;
@Override
protected void starting(Description description) {
RecordHistogram.setDisabledForTests(true);
MockitoAnnotations.initMocks(this);
tabFromFactory = prepareTab();
when(intentDataProvider.getIntent()).thenReturn(intent);
when(intentDataProvider.getSession()).thenReturn(session);
when(intentDataProvider.getUrlToLoad()).thenReturn(INITIAL_URL);
when(tabFactory.createTab()).thenReturn(tabFromFactory);
when(tabFactory.getTabModelSelector()).thenReturn(tabModelSelector);
when(tabModelSelector.getModel(anyBoolean())).thenReturn(tabModel);
when(connection.getSpeculatedUrl(any())).thenReturn(SPECULATED_URL);
doNothing().when(activityTabProvider).addObserverAndTrigger(
activityTabObserverCaptor.capture());
doNothing().when(tabFromFactory).initialize(webContentsCaptor.capture(), any(), any(),
anyBoolean(), anyBoolean());
}
@Override
protected void finished(Description description) {
RecordHistogram.setDisabledForTests(false);
AsyncTabParamsManager.getAsyncTabParams().clear();
}
public CustomTabActivityTabController createTabController() {
return new CustomTabActivityTabController(activity,
() -> customTabDelegateFactory, connection, intentDataProvider,
() -> tabContentManager, activityTabProvider,
tabObserverRegistrar, () -> compositorViewHolder, lifecycleDispatcher,
warmupManager, tabPersistencePolicy, tabFactory, () -> customTabObserver,
webContentsFactory, navigationEventObserver, tabProvider);
}
public CustomTabActivityNavigationController createNavigationController() {
return new CustomTabActivityNavigationController(tabProvider,
intentDataProvider, connection, () -> customTabObserver);
}
public CustomTabActivityInitialPageLoader createInitialPageLoader(
CustomTabActivityNavigationController navigationController) {
return new CustomTabActivityInitialPageLoader(tabProvider,
intentDataProvider, connection, () -> customTabObserver,
navigationEventObserver, navigationController);
}
public void warmUp() {
when(connection.hasWarmUpBeenFinished()).thenReturn(true);
}
public void changeTab(Tab newTab) {
when(activityTabProvider.getActivityTab()).thenReturn(newTab);
for (ActivityTabObserver observer : activityTabObserverCaptor.getAllValues()) {
observer.onActivityTabChanged(newTab, false);
}
}
public void saveTab(Tab tab) {
when(activity.getSavedInstanceState()).thenReturn(new Bundle());
when(tabModelSelector.getCurrentTab()).thenReturn(tab);
}
// Dispatches lifecycle events up to native init.
public void reachNativeInit(CustomTabActivityTabController tabController) {
tabController.onPreInflationStartup();
tabController.onPostInflationStartup();
tabController.onFinishNativeInitialization();
}
public WebContents prepareTransferredWebcontents() {
int tabId = 1;
WebContents webContents = mock(WebContents.class);
AsyncTabParamsManager.add(tabId, new AsyncTabCreationParams(mock(LoadUrlParams.class),
webContents));
intent.putExtra(IntentHandler.EXTRA_TAB_ID, tabId);
return webContents;
}
public WebContents prepareSpareWebcontents() {
WebContents webContents = mock(WebContents.class);
when(warmupManager.takeSpareWebContents(
anyBoolean(), anyBoolean(), eq(WarmupManager.FOR_CCT)))
.thenReturn(webContents);
return webContents;
}
public Tab prepareHiddenTab() {
warmUp();
Tab hiddenTab = prepareTab();
when(connection.takeHiddenTab(any(), any(), any())).thenReturn(hiddenTab);
return hiddenTab;
}
public Tab prepareTab() {
Tab tab = mock(Tab.class);
when(tab.getView()).thenReturn(mock(View.class));
when(tab.getUserDataHost()).thenReturn(new UserDataHost());
return tab;
}
}
// Copyright 2019 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.customtabs.content;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.chromium.chrome.browser.customtabs.content.CustomTabActivityContentTestEnvironment.INITIAL_URL;
import static org.chromium.chrome.browser.customtabs.content.CustomTabActivityContentTestEnvironment.OTHER_URL;
import static org.chromium.chrome.browser.customtabs.content.CustomTabActivityContentTestEnvironment.SPECULATED_URL;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.robolectric.annotation.Config;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.util.test.ShadowUrlUtilities;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.testing.local.LocalRobolectricTestRunner;
/**
* Integration tests involving several classes in Custom Tabs content layer, checking that urls are
* properly loaded in Custom Tabs in different conditions.
*/
@RunWith(LocalRobolectricTestRunner.class)
@Config(manifest = Config.NONE, shadows = {ShadowUrlUtilities.class})
public class CustomTabActivityUrlLoadingTest {
@Rule
public final CustomTabActivityContentTestEnvironment env =
new CustomTabActivityContentTestEnvironment();
private CustomTabActivityTabController mTabController;
private CustomTabActivityNavigationController mNavigationController;
private CustomTabActivityInitialPageLoader mInitialPageLoader;
@Before
public void setUp() {
mTabController = env.createTabController();
mNavigationController = env.createNavigationController();
mInitialPageLoader = env.createInitialPageLoader(mNavigationController);
}
@Test
public void startsLoadingPage_InEarlyCreatedTab() {
env.warmUp();
mTabController.onPreInflationStartup();
verify(env.tabFromFactory).loadUrl(argThat(params -> INITIAL_URL.equals(params.getUrl())));
}
@Test
public void requestsWindowFeature_BeforeAddingContent() {
env.warmUp();
mTabController.onPreInflationStartup();
InOrder inOrder = inOrder(env.activity, env.tabFromFactory);
inOrder.verify(env.activity).supportRequestWindowFeature(anyInt());
inOrder.verify(env.tabFromFactory).loadUrl(any());
}
@Test
public void doesntLoadInitialUrlAgain_IfTabChanges() {
env.reachNativeInit(mTabController);
clearInvocations(env.tabFromFactory);
Tab newTab = mock(Tab.class);
env.changeTab(newTab);
verify(newTab, never()).loadUrl(any());
verify(env.tabFromFactory, never()).loadUrl(any());
}
@Test
public void loadsUrlInNewTab_IfTabChanges() {
env.reachNativeInit(mTabController);
Tab newTab = mock(Tab.class);
env.changeTab(newTab);
clearInvocations(env.tabFromFactory);
mNavigationController.navigate(OTHER_URL);
verify(newTab).loadUrl(any());
verify(env.tabFromFactory, never()).loadUrl(any());
}
@Test
public void doesntLoadInitialUrl_InRestoredTab() {
Tab savedTab = env.prepareTab();
env.saveTab(savedTab);
env.reachNativeInit(mTabController);
verify(savedTab, never()).loadUrl(any());
}
@Test
public void doesntLoadUrl_IfEqualsSpeculatedUrl_AndIsFirstLoad() {
Tab hiddenTab = env.prepareHiddenTab();
when(env.intentDataProvider.getUrlToLoad()).thenReturn(SPECULATED_URL);
env.reachNativeInit(mTabController);
verify(hiddenTab, never()).loadUrl(any());
}
@Test
public void loadUrl_IfEqualsSpeculatedUrl_ButIsntFirstLoad() {
Tab hiddenTab = env.prepareHiddenTab();
when(env.intentDataProvider.getUrlToLoad()).thenReturn(OTHER_URL);
env.reachNativeInit(mTabController);
clearInvocations(env.tabFromFactory);
LoadUrlParams params = new LoadUrlParams(SPECULATED_URL);
mNavigationController.navigate(params, 0);
verify(hiddenTab).loadUrl(params);
}
@Test
public void loadsUrlInHiddenTab_IfExists() {
Tab hiddenTab = env.prepareHiddenTab();
env.reachNativeInit(mTabController);
verify(hiddenTab).loadUrl(any());
}
}
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