Commit 090f1f05 authored by Pavel Shmakov's avatar Pavel Shmakov Committed by Commit Bot

Migrate TrustedWebActivityUi and related classes to Dagger, decouple CustomTabActivity from it.

As a first step of simplifying CustomTabActivity, I extract all the code related
to TrustedWebActivityUi and use Dagger to organize dependencies for
TrustedWebActivityUi.

I make use of recently introduced activity lifecycle dispatching: https://crrev.com/c/1283256

Bug: 887926
Change-Id: Ia36a026a7f297e7c0f101189945c44454d12fa6e
Reviewed-on: https://chromium-review.googlesource.com/c/1281102
Commit-Queue: Pavel Shmakov <pshmakov@chromium.org>
Reviewed-by: default avatarYusuf Ozuysal <yusufo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#603122}
parent 78b9bb38
// Copyright 2018 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;
import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
import dagger.Module;
import dagger.Provides;
/**
* Makes entities provided by AppHooks available for injection with Dagger.
* TODO(pshmakov): merge this with Chrome's AppHooksImpl.
*/
@Module
public class AppHooksModule {
@Provides
public static CustomTabsConnection provideCustomTabsConnection() {
return CustomTabsConnection.getInstance();
}
}
...@@ -4,16 +4,23 @@ ...@@ -4,16 +4,23 @@
package org.chromium.chrome.browser.browserservices; package org.chromium.chrome.browser.browserservices;
import static org.chromium.chrome.browser.dependency_injection.ChromeCommonQualifiers.APP_CONTEXT;
import android.content.Context;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.text.TextUtils; import android.text.TextUtils;
import org.chromium.base.Log; import org.chromium.base.Log;
import org.chromium.chrome.browser.dependency_injection.ActivityScope;
import org.chromium.chrome.browser.util.UrlUtilities; import org.chromium.chrome.browser.util.UrlUtilities;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import javax.inject.Inject;
import javax.inject.Named;
/** /**
* Takes care of recording that Chrome contains data for the client app in the * Takes care of recording that Chrome contains data for the client app in the
* {@link ClientAppDataRegister}. It performs three main duties: * {@link ClientAppDataRegister}. It performs three main duties:
...@@ -25,15 +32,14 @@ import java.util.Set; ...@@ -25,15 +32,14 @@ import java.util.Set;
* {@link TrustedWebActivityUi}. Having more instances won't effect correctness, but will limit the * {@link TrustedWebActivityUi}. Having more instances won't effect correctness, but will limit the
* performance benefits of the cache. * performance benefits of the cache.
* Thread safety: All methods on this class should be called from the same thread. * Thread safety: All methods on this class should be called from the same thread.
* Native: The default {@link UrlTransformer} requires native to be loaded.
*/ */
@ActivityScope
public class ClientAppDataRecorder { public class ClientAppDataRecorder {
private static final String TAG = "TWAClientAppData"; private static final String TAG = "TWAClientAppData";
private final PackageManager mPackageManager; private final PackageManager mPackageManager;
/** Underlying data register. */ /** Underlying data register. */
private final ClientAppDataRegister mClientAppDataRegister; private final ClientAppDataRegister mClientAppDataRegister;
private final UrlTransformer mUrlTransformer;
/** /**
* Cache so we don't send the same request multiple times. {@link #register} is called on each * Cache so we don't send the same request multiple times. {@link #register} is called on each
...@@ -42,31 +48,18 @@ public class ClientAppDataRecorder { ...@@ -42,31 +48,18 @@ public class ClientAppDataRecorder {
*/ */
private final Set<String> mCache = new HashSet<>(); private final Set<String> mCache = new HashSet<>();
/** Class to allow mocking native calls in unit tests. */ @Inject
/* package */ static class UrlTransformer { public ClientAppDataRecorder(@Named(APP_CONTEXT) Context context,
/* package */ String getDomain(Origin origin) { ClientAppDataRegister clientAppDataRegister) {
return UrlUtilities.getDomainAndRegistry( mPackageManager = context.getPackageManager();
origin.toString(), true /* includePrivateRegistries */);
}
}
/** Creates a ClientAppDataRecorder with default dependencies. */
public ClientAppDataRecorder(PackageManager packageManager) {
this(packageManager, new ClientAppDataRegister(), new UrlTransformer());
}
/** Creates a ClientAppDataRecorder providing all dependencies. */
public ClientAppDataRecorder(PackageManager packageManager,
ClientAppDataRegister clientAppDataRegister, UrlTransformer urlTransformer) {
mPackageManager = packageManager;
mClientAppDataRegister = clientAppDataRegister; mClientAppDataRegister = clientAppDataRegister;
mUrlTransformer = urlTransformer;
} }
/** /**
* Calls {@link ClientAppDataRegister#registerPackageForDomain}, looking up the uid * Calls {@link ClientAppDataRegister#registerPackageForDomain}, looking up the uid
* and app name for the |packageName|, extracting the domain from the origin and deduplicating * and app name for the |packageName|, extracting the domain from the origin and deduplicating
* multiple requests with the same parameters. * multiple requests with the same parameters.
* Requires native to be loaded.
*/ */
/* package */ void register(String packageName, Origin origin) { /* package */ void register(String packageName, Origin origin) {
if (mCache.contains(combine(packageName, origin))) return; if (mCache.contains(combine(packageName, origin))) return;
...@@ -82,7 +75,8 @@ public class ClientAppDataRecorder { ...@@ -82,7 +75,8 @@ public class ClientAppDataRecorder {
return; return;
} }
String domain = mUrlTransformer.getDomain(origin); String domain = UrlUtilities.getDomainAndRegistry(
origin.toString(), true /*includePrivateRegistries*/);
Log.d(TAG, "Registering %d (%s) for %s", ai.uid, appLabel, domain); Log.d(TAG, "Registering %d (%s) for %s", ai.uid, appLabel, domain);
mClientAppDataRegister.registerPackageForDomain(ai.uid, appLabel, domain); mClientAppDataRegister.registerPackageForDomain(ai.uid, appLabel, domain);
......
...@@ -14,6 +14,8 @@ import org.chromium.chrome.browser.snackbar.SnackbarManager; ...@@ -14,6 +14,8 @@ import org.chromium.chrome.browser.snackbar.SnackbarManager;
import javax.inject.Inject; import javax.inject.Inject;
import dagger.Lazy;
/** /**
* Shows the Trusted Web Activity disclosure when appropriate and records its acceptance. * Shows the Trusted Web Activity disclosure when appropriate and records its acceptance.
* *
...@@ -26,6 +28,7 @@ public class TrustedWebActivityDisclosure { ...@@ -26,6 +28,7 @@ public class TrustedWebActivityDisclosure {
// TODO(peconn): Make this package private once TrustedWebActivityUi can be injected. // TODO(peconn): Make this package private once TrustedWebActivityUi can be injected.
private final Resources mResources; private final Resources mResources;
private final ChromePreferenceManager mPreferenceManager; private final ChromePreferenceManager mPreferenceManager;
private final Lazy<SnackbarManager> mSnackbarManager;
private boolean mSnackbarShowing; private boolean mSnackbarShowing;
...@@ -50,30 +53,31 @@ public class TrustedWebActivityDisclosure { ...@@ -50,30 +53,31 @@ public class TrustedWebActivityDisclosure {
@Inject @Inject
/* package */ TrustedWebActivityDisclosure(Resources resources, /* package */ TrustedWebActivityDisclosure(Resources resources,
ChromePreferenceManager preferenceManager) { ChromePreferenceManager preferenceManager, Lazy<SnackbarManager> snackbarManager) {
mResources = resources; mResources = resources;
mPreferenceManager = preferenceManager; mPreferenceManager = preferenceManager;
mSnackbarManager = snackbarManager;
} }
/** Dismisses the Snackbar if it is showing. */ /** Dismisses the disclosure if it is showing. */
/* package */ void dismissSnackbarIfNeeded(SnackbarManager snackbarManager) { /* package */ void dismiss() {
if (!mSnackbarShowing) return; if (!mSnackbarShowing) return;
snackbarManager.dismissSnackbars(mSnackbarController); mSnackbarManager.get().dismissSnackbars(mSnackbarController);
mSnackbarShowing = false; mSnackbarShowing = false;
} }
/** Shows the Snackbar if it is not already showing and hasn't been accepted. */ /** Shows the disclosure if it is not already showing and hasn't been accepted. */
/* package */ void showSnackbarIfNeeded(SnackbarManager snackbarManager, String packageName) { /* package */ void showIfNeeded(String packageName) {
if (mSnackbarShowing) return; if (mSnackbarShowing) return;
if (wasSnackbarDismissed(packageName)) return; if (wasDismissed(packageName)) return;
snackbarManager.showSnackbar(makeRunningInChromeInfobar(packageName)); mSnackbarManager.get().showSnackbar(makeRunningInChromeInfobar(packageName));
mSnackbarShowing = true; mSnackbarShowing = true;
} }
/** Has a Snackbar been dismissed for this client package before? */ /** Has a disclosure been dismissed for this client package before? */
private boolean wasSnackbarDismissed(String packageName) { private boolean wasDismissed(String packageName) {
return mPreferenceManager.hasUserAcceptedTwaDisclosureForPackage(packageName); return mPreferenceManager.hasUserAcceptedTwaDisclosureForPackage(packageName);
} }
......
...@@ -4,79 +4,54 @@ ...@@ -4,79 +4,54 @@
package org.chromium.chrome.browser.browserservices; package org.chromium.chrome.browser.browserservices;
import android.support.annotation.Nullable;
import android.support.customtabs.CustomTabsService; import android.support.customtabs.CustomTabsService;
import org.chromium.chrome.browser.ActivityTabProvider;
import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.chrome.browser.customtabs.CustomTabBrowserControlsVisibilityDelegate;
import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
import org.chromium.chrome.browser.customtabs.TabObserverRegistrar;
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.fullscreen.FullscreenManager; import org.chromium.chrome.browser.fullscreen.FullscreenManager;
import org.chromium.chrome.browser.snackbar.SnackbarManager; import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
import org.chromium.chrome.browser.tab.BrowserControlsVisibilityDelegate; import org.chromium.chrome.browser.lifecycle.InflationObserver;
import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
import org.chromium.chrome.browser.lifecycle.PauseResumeWithNativeObserver;
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.TabObserver; import org.chromium.chrome.browser.tab.TabObserver;
import javax.inject.Inject;
/** /**
* Class to handle the state and logic for CustomTabActivity to do with Trusted Web Activities. * Class to handle the state and logic for CustomTabActivity to do with Trusted Web Activities.
* *
* Lifecycle: There should be a 1-1 relationship between this class and * Lifecycle: There should be a 1-1 relationship between this class and
* {@link org.chromium.chrome.browser.customtabs.CustomTabActivity}. * {@link org.chromium.chrome.browser.customtabs.CustomTabActivity}.
* Thread safety: All methods on this class should be called on the UI thread.
*/ */
public class TrustedWebActivityUi { @ActivityScope
// TODO(peconn): Convert this class to use dependency injection, when you do clean up public class TrustedWebActivityUi
// CustomTabActivityComponent and TrustedWebActivityDisclosure. implements InflationObserver, PauseResumeWithNativeObserver, NativeInitObserver {
/** The Digital Asset Link relationship used for Trusted Web Activities. */ /** The Digital Asset Link relationship used for Trusted Web Activities. */
private final static int RELATIONSHIP = CustomTabsService.RELATION_HANDLE_ALL_URLS; private final static int RELATIONSHIP = CustomTabsService.RELATION_HANDLE_ALL_URLS;
private final TrustedWebActivityUiDelegate mDelegate;
private final TrustedWebActivityDisclosure mDisclosure; private final TrustedWebActivityDisclosure mDisclosure;
private final TrustedWebActivityOpenTimeRecorder mOpenTimeRecorder = private final TrustedWebActivityOpenTimeRecorder mOpenTimeRecorder =
new TrustedWebActivityOpenTimeRecorder(); new TrustedWebActivityOpenTimeRecorder();
private final ChromeFullscreenManager mFullscreenManager;
private final ClientAppDataRecorder mClientAppDataRecorder; private final ClientAppDataRecorder mClientAppDataRecorder;
private final CustomTabsConnection mCustomTabsConnection;
private final CustomTabIntentDataProvider mIntentDataProvider;
private final ActivityTabProvider mActivityTabProvider;
private final CustomTabBrowserControlsVisibilityDelegate mControlsVisibilityDelegate;
private boolean mInTrustedWebActivity = true; private boolean mInTrustedWebActivity = true;
private int mControlsHidingToken = FullscreenManager.INVALID_TOKEN; private int mControlsHidingToken = FullscreenManager.INVALID_TOKEN;
/**
* A delegate for embedders to implement to inject information into this class. The reason for
* using this instead of passing these dependencies into the constructor is because they may not
* be available at construction.
*/
public interface TrustedWebActivityUiDelegate {
/**
* Provides a {@link ChromeFullscreenManager} that is used to control visibility of the
* toolbar.
*/
ChromeFullscreenManager getFullscreenManager();
/**
* Provides the package name of the client for verification.
*/
String getClientPackageName();
/**
* Gives the SnackbarManager to use to display the disclosure.
*/
SnackbarManager getSnackbarManager();
}
/**
* A {@link BrowserControlsVisibilityDelegate} that disallows showing the Browser Controls when
* we are in a Trusted Web Activity.
*/
private final BrowserControlsVisibilityDelegate mInTwaVisibilityDelegate =
new BrowserControlsVisibilityDelegate() {
@Override
public boolean canShowBrowserControls() {
return !mInTrustedWebActivity;
}
@Override
public boolean canAutoHideBrowserControls() {
return true;
}
};
/** A {@link TabObserver} that checks whether we are on a verified Origin on page navigation. */ /** A {@link TabObserver} that checks whether we are on a verified Origin on page navigation. */
private final TabObserver mVerifyOnPageLoadObserver = new EmptyTabObserver() { private final TabObserver mVerifyOnPageLoadObserver = new EmptyTabObserver() {
@Override @Override
...@@ -86,7 +61,7 @@ public class TrustedWebActivityUi { ...@@ -86,7 +61,7 @@ public class TrustedWebActivityUi {
int httpStatusCode) { int httpStatusCode) {
if (!hasCommitted || !isInMainFrame) return; if (!hasCommitted || !isInMainFrame) return;
String packageName = mDelegate.getClientPackageName(); String packageName = getClientPackageName();
assert packageName != null; assert packageName != null;
// This doesn't perform a network request or attempt new verification - it checks to // This doesn't perform a network request or attempt new verification - it checks to
...@@ -95,58 +70,57 @@ public class TrustedWebActivityUi { ...@@ -95,58 +70,57 @@ public class TrustedWebActivityUi {
boolean verified = boolean verified =
OriginVerifier.isValidOrigin(packageName, origin, RELATIONSHIP); OriginVerifier.isValidOrigin(packageName, origin, RELATIONSHIP);
if (verified) registerClientAppData(packageName, origin); if (verified) registerClientAppData(packageName, origin);
setTrustedWebActivityMode(verified, tab); setTrustedWebActivityMode(verified);
} }
}; };
@Inject
/** Creates a TrustedWebActivityUi, providing a delegate from the embedder. */ public TrustedWebActivityUi(TrustedWebActivityDisclosure disclosure,
public TrustedWebActivityUi(TrustedWebActivityUiDelegate delegate, ChromeFullscreenManager fullscreenManager, ClientAppDataRecorder clientAppDataRecorder,
TrustedWebActivityDisclosure disclosure, ClientAppDataRecorder clientAppDataRecorder) { CustomTabIntentDataProvider intentDataProvider,
mDelegate = delegate; CustomTabsConnection customTabsConnection,
ActivityLifecycleDispatcher lifecycleDispatcher,
TabObserverRegistrar tabObserverRegistrar, ActivityTabProvider activityTabProvider,
CustomTabBrowserControlsVisibilityDelegate controlsVisibilityDelegate) {
mFullscreenManager = fullscreenManager;
mClientAppDataRecorder = clientAppDataRecorder; mClientAppDataRecorder = clientAppDataRecorder;
mDisclosure = disclosure; mDisclosure = disclosure;
mCustomTabsConnection = customTabsConnection;
mIntentDataProvider = intentDataProvider;
mActivityTabProvider = activityTabProvider;
mControlsVisibilityDelegate = controlsVisibilityDelegate;
tabObserverRegistrar.registerTabObserver(mVerifyOnPageLoadObserver);
lifecycleDispatcher.register(this);
} }
/** @Nullable
* Gets a {@link BrowserControlsVisibilityDelegate} that will hide/show the Custom Tab toolbar private String getClientPackageName() {
* on verification/leaving the verified origin. return mCustomTabsConnection.getClientPackageNameForSession(
*/ mIntentDataProvider.getSession());
public BrowserControlsVisibilityDelegate getBrowserControlsVisibilityDelegate() {
return mInTwaVisibilityDelegate;
}
/**
* Gets a {@link TabObserver} that watches for navigations and sets whether we are in a Trusted
* Web Activity accordingly.
*/
public TabObserver getTabObserver() {
return mVerifyOnPageLoadObserver;
} }
/** /**
* Shows the disclosure Snackbar if needed on the first Tab. Subsequent navigations will update * Shows the disclosure Snackbar if needed on the first Tab. Subsequent navigations will update
* the disclosure state automatically. * the disclosure state automatically.
*/ */
public void initialShowSnackbarIfNeeded() { private void initialShowSnackbarIfNeeded() {
assert mDelegate.getSnackbarManager() != null; String packageName = getClientPackageName();
assert mDelegate.getClientPackageName() != null; assert packageName != null;
// If we have left Trusted Web Activity mode (through onDidFinishNavigation), we don't need // If we have left Trusted Web Activity mode (through onDidFinishNavigation), we don't need
// to show the Snackbar. // to show the Snackbar.
if (!mInTrustedWebActivity) return; if (!mInTrustedWebActivity) return;
mDisclosure.showSnackbarIfNeeded(mDelegate.getSnackbarManager(), mDisclosure.showIfNeeded(packageName);
mDelegate.getClientPackageName());
} }
/** /**
* Perform verification for the URL that the CustomTabActivity starts on. * Perform verification for the URL that the CustomTabActivity starts on.
*/ */
public void attemptVerificationForInitialUrl(String url, Tab tab) { public void attemptVerificationForInitialUrl(String url, Tab tab) {
assert mDelegate.getClientPackageName() != null; String packageName = getClientPackageName();
assert packageName != null;
String packageName = mDelegate.getClientPackageName();
Origin origin = new Origin(url); Origin origin = new Origin(url);
new OriginVerifier((packageName2, origin2, verified, online) -> { new OriginVerifier((packageName2, origin2, verified, online) -> {
...@@ -154,55 +128,64 @@ public class TrustedWebActivityUi { ...@@ -154,55 +128,64 @@ public class TrustedWebActivityUi {
BrowserServicesMetrics.recordTwaOpened(); BrowserServicesMetrics.recordTwaOpened();
if (verified) registerClientAppData(packageName, origin); if (verified) registerClientAppData(packageName, origin);
setTrustedWebActivityMode(verified, tab); setTrustedWebActivityMode(verified);
}, packageName, RELATIONSHIP).start(origin); }, packageName, RELATIONSHIP).start(origin);
} }
@Override
public void onPreInflationStartup() {}
@Override
public void onPostInflationStartup() { public void onPostInflationStartup() {
// TODO(pshmakov): Move this over to LifecycleObserver or something similar once available.
if (mInTrustedWebActivity) { if (mInTrustedWebActivity) {
// Hide Android controls as soon as they are inflated. // Hide Android controls as soon as they are inflated.
mControlsHidingToken = mDelegate.getFullscreenManager().hideAndroidControls(); mControlsHidingToken = mFullscreenManager.hideAndroidControls();
mControlsVisibilityDelegate.setTrustedWebActivityMode(true);
} }
} }
/** Notify (for metrics purposes) that the TWA has been resumed. */ @Override
public void onResume() { public void onResumeWithNative() {
// TODO(peconn): Move this over to LifecycleObserver or something similar once available.
mOpenTimeRecorder.onResume(); mOpenTimeRecorder.onResume();
} }
/** Notify (for metrics purposes) that the TWA has been paused. */ @Override
public void onPause() { public void onPauseWithNative() {
// TODO(peconn): Move this over to LifecycleObserver or something similar once available.
mOpenTimeRecorder.onPause(); mOpenTimeRecorder.onPause();
} }
@Override
public void onFinishNativeInitialization() {
if (!ChromeFeatureList.isEnabled(ChromeFeatureList.TRUSTED_WEB_ACTIVITY_POST_MESSAGE)) {
mCustomTabsConnection.resetPostMessageHandlerForSession(
mIntentDataProvider.getSession(), null);
}
attemptVerificationForInitialUrl(
mIntentDataProvider.getUrlToLoad(), mActivityTabProvider.getActivityTab());
initialShowSnackbarIfNeeded();
}
/** /**
* Updates the UI appropriately for whether or not Trusted Web Activity mode is enabled. * Updates the UI appropriately for whether or not Trusted Web Activity mode is enabled.
*/ */
private void setTrustedWebActivityMode(boolean enabled, Tab tab) { private void setTrustedWebActivityMode(boolean enabled) {
if (mInTrustedWebActivity == enabled) return; if (mInTrustedWebActivity == enabled) return;
mInTrustedWebActivity = enabled; mInTrustedWebActivity = enabled;
mControlsVisibilityDelegate.setTrustedWebActivityMode(mInTrustedWebActivity);
ChromeFullscreenManager fullscreenManager = mDelegate.getFullscreenManager();
if (enabled) { if (enabled) {
mControlsHidingToken = mControlsHidingToken =
fullscreenManager.hideAndroidControlsAndClearOldToken(mControlsHidingToken); mFullscreenManager.hideAndroidControlsAndClearOldToken(mControlsHidingToken);
mDisclosure.showSnackbarIfNeeded(mDelegate.getSnackbarManager(), mDisclosure.showIfNeeded(getClientPackageName());
mDelegate.getClientPackageName());
} else { } else {
fullscreenManager.releaseAndroidControlsHidingToken(mControlsHidingToken); mFullscreenManager.releaseAndroidControlsHidingToken(mControlsHidingToken);
// Force showing the controls for a bit when leaving Trusted Web Activity mode. // Force showing the controls for a bit when leaving Trusted Web Activity mode.
fullscreenManager.getBrowserVisibilityDelegate().showControlsTransient(); mFullscreenManager.getBrowserVisibilityDelegate().showControlsTransient();
mDisclosure.dismissSnackbarIfNeeded(mDelegate.getSnackbarManager()); mDisclosure.dismiss();
} }
// Apply the change in the BrowserControlsVisibilityDelegate
tab.updateFullscreenEnabledState();
} }
/** /**
......
...@@ -58,29 +58,25 @@ import org.chromium.chrome.browser.IntentHandler.ExternalAppId; ...@@ -58,29 +58,25 @@ import org.chromium.chrome.browser.IntentHandler.ExternalAppId;
import org.chromium.chrome.browser.KeyboardShortcuts; import org.chromium.chrome.browser.KeyboardShortcuts;
import org.chromium.chrome.browser.LaunchIntentDispatcher; import org.chromium.chrome.browser.LaunchIntentDispatcher;
import org.chromium.chrome.browser.ServiceTabLauncher; import org.chromium.chrome.browser.ServiceTabLauncher;
import org.chromium.chrome.browser.UrlConstants;
import org.chromium.chrome.browser.WarmupManager; import org.chromium.chrome.browser.WarmupManager;
import org.chromium.chrome.browser.WebContentsFactory; import org.chromium.chrome.browser.WebContentsFactory;
import org.chromium.chrome.browser.appmenu.AppMenuPropertiesDelegate; import org.chromium.chrome.browser.appmenu.AppMenuPropertiesDelegate;
import org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiController; import org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiController;
import org.chromium.chrome.browser.browserservices.BrowserSessionContentHandler; 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.browserservices.ClientAppDataRecorder;
import org.chromium.chrome.browser.browserservices.TrustedWebActivityUi;
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.dependency_injection.CustomTabActivityComponent;
import org.chromium.chrome.browser.customtabs.dependency_injection.CustomTabActivityModule;
import org.chromium.chrome.browser.customtabs.dynamicmodule.ActivityDelegate; import org.chromium.chrome.browser.customtabs.dynamicmodule.ActivityDelegate;
import org.chromium.chrome.browser.customtabs.dynamicmodule.ActivityHostImpl; import org.chromium.chrome.browser.customtabs.dynamicmodule.ActivityHostImpl;
import org.chromium.chrome.browser.customtabs.dynamicmodule.ModuleEntryPoint; import org.chromium.chrome.browser.customtabs.dynamicmodule.ModuleEntryPoint;
import org.chromium.chrome.browser.customtabs.dynamicmodule.ModuleLoader; import org.chromium.chrome.browser.customtabs.dynamicmodule.ModuleLoader;
import org.chromium.chrome.browser.customtabs.dynamicmodule.ModuleMetrics; import org.chromium.chrome.browser.customtabs.dynamicmodule.ModuleMetrics;
import org.chromium.chrome.browser.dependency_injection.ChromeActivityCommonsModule; import org.chromium.chrome.browser.dependency_injection.ChromeActivityCommonsModule;
import org.chromium.chrome.browser.dependency_injection.CustomTabActivityComponent;
import org.chromium.chrome.browser.externalauth.ExternalAuthUtils; import org.chromium.chrome.browser.externalauth.ExternalAuthUtils;
import org.chromium.chrome.browser.externalnav.ExternalNavigationDelegateImpl; import org.chromium.chrome.browser.externalnav.ExternalNavigationDelegateImpl;
import org.chromium.chrome.browser.firstrun.FirstRunSignInProcessor; import org.chromium.chrome.browser.firstrun.FirstRunSignInProcessor;
import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
import org.chromium.chrome.browser.fullscreen.ComposedBrowserControlsVisibilityDelegate;
import org.chromium.chrome.browser.gsa.GSAState; import org.chromium.chrome.browser.gsa.GSAState;
import org.chromium.chrome.browser.incognito.IncognitoTabHost; import org.chromium.chrome.browser.incognito.IncognitoTabHost;
import org.chromium.chrome.browser.incognito.IncognitoTabHostRegistry; import org.chromium.chrome.browser.incognito.IncognitoTabHostRegistry;
...@@ -88,8 +84,6 @@ import org.chromium.chrome.browser.infobar.InfoBarContainer; ...@@ -88,8 +84,6 @@ import org.chromium.chrome.browser.infobar.InfoBarContainer;
import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings; import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
import org.chromium.chrome.browser.page_info.PageInfoController; import org.chromium.chrome.browser.page_info.PageInfoController;
import org.chromium.chrome.browser.rappor.RapporServiceBridge; import org.chromium.chrome.browser.rappor.RapporServiceBridge;
import org.chromium.chrome.browser.snackbar.SnackbarManager;
import org.chromium.chrome.browser.tab.BrowserControlsVisibilityDelegate;
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.TabDelegateFactory; import org.chromium.chrome.browser.tab.TabDelegateFactory;
...@@ -175,8 +169,6 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -175,8 +169,6 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
/** Adds and removes observers from tabs when needed. */ /** Adds and removes observers from tabs when needed. */
private final TabObserverRegistrar mTabObserverRegistrar = new TabObserverRegistrar(); private final TabObserverRegistrar mTabObserverRegistrar = new TabObserverRegistrar();
private @Nullable TrustedWebActivityUi mTrustedWebActivityUi;
private String mSpeculatedUrl; private String mSpeculatedUrl;
private boolean mUsingHiddenTab; private boolean mUsingHiddenTab;
...@@ -276,7 +268,7 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -276,7 +268,7 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
super.preInflationStartup(); super.preInflationStartup();
if (mIntentDataProvider.isTrustedWebActivity()) { if (mIntentDataProvider.isTrustedWebActivity()) {
mTrustedWebActivityUi = createTrustedWebActivityUi(); getComponent().resolveTrustedWebActivityUi();
} }
mSession = mIntentDataProvider.getSession(); mSession = mIntentDataProvider.getSession();
...@@ -288,7 +280,7 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -288,7 +280,7 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
mMainTab = getHiddenTab(); mMainTab = getHiddenTab();
if (mMainTab == null) mMainTab = createMainTab(); if (mMainTab == null) mMainTab = createMainTab();
mIsFirstLoad = true; mIsFirstLoad = true;
loadUrlInTab(mMainTab, new LoadUrlParams(getUrlToLoad()), loadUrlInTab(mMainTab, new LoadUrlParams(mIntentDataProvider.getUrlToLoad()),
IntentHandler.getTimestampFromIntent(getIntent())); IntentHandler.getTimestampFromIntent(getIntent()));
mHasCreatedTabEarly = true; mHasCreatedTabEarly = true;
} }
...@@ -309,28 +301,6 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -309,28 +301,6 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
} }
} }
private TrustedWebActivityUi createTrustedWebActivityUi() {
return new TrustedWebActivityUi(
new TrustedWebActivityUi.TrustedWebActivityUiDelegate() {
@Override
public ChromeFullscreenManager getFullscreenManager() {
return CustomTabActivity.this.getFullscreenManager();
}
@Override
public String getClientPackageName() {
return mConnection != null
? mConnection.getClientPackageNameForSession(mSession) : null;
}
@Override
public SnackbarManager getSnackbarManager() {
return CustomTabActivity.this.getSnackbarManager();
}
}, getComponent().resolveTrustedWebActivityDisclosure(),
new ClientAppDataRecorder(getPackageManager()));
}
/** /**
* Dynamically loads a module using the component name specified in the intent if the feature is * Dynamically loads a module using the component name specified in the intent if the feature is
* enabled, the package is Google-signed, and it is not loaded yet. * enabled, the package is Google-signed, and it is not loaded yet.
...@@ -476,10 +446,6 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -476,10 +446,6 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
getFullscreenManager()); getFullscreenManager());
mBottomBarDelegate.showBottomBarIfNecessary(); mBottomBarDelegate.showBottomBarIfNecessary();
mTopBarDelegate = new CustomTabTopBarDelegate(this); mTopBarDelegate = new CustomTabTopBarDelegate(this);
if (mTrustedWebActivityUi != null) {
mTrustedWebActivityUi.onPostInflationStartup();
}
} }
@Override @Override
...@@ -505,16 +471,9 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -505,16 +471,9 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
} }
private CustomTabDelegateFactory createCustomTabDelegateFactory() { private CustomTabDelegateFactory createCustomTabDelegateFactory() {
BrowserControlsVisibilityDelegate delegate =
getFullscreenManager().getBrowserVisibilityDelegate();
if (mTrustedWebActivityUi != null) {
delegate = new ComposedBrowserControlsVisibilityDelegate(delegate,
mTrustedWebActivityUi.getBrowserControlsVisibilityDelegate()
);
}
return new CustomTabDelegateFactory(mIntentDataProvider.shouldEnableUrlBarHiding(), return new CustomTabDelegateFactory(mIntentDataProvider.shouldEnableUrlBarHiding(),
mIntentDataProvider.isOpenedByChrome(), delegate); mIntentDataProvider.isOpenedByChrome(),
getComponent().resolveControlsVisibilityDelegate());
} }
@Override @Override
...@@ -657,7 +616,7 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -657,7 +616,7 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
recordClientPackageName(); recordClientPackageName();
mConnection.showSignInToastIfNecessary(mSession, getIntent()); mConnection.showSignInToastIfNecessary(mSession, getIntent());
String url = getUrlToLoad(); String url = mIntentDataProvider.getUrlToLoad();
String packageName = mConnection.getClientPackageNameForSession(mSession); String packageName = mConnection.getClientPackageNameForSession(mSession);
if (TextUtils.isEmpty(packageName)) { if (TextUtils.isEmpty(packageName)) {
packageName = mConnection.extractCreatorPackage(getIntent()); packageName = mConnection.extractCreatorPackage(getIntent());
...@@ -684,15 +643,6 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -684,15 +643,6 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
mAutofillAssistantUiController = new AutofillAssistantUiController(this); mAutofillAssistantUiController = new AutofillAssistantUiController(this);
} }
if (mTrustedWebActivityUi != null) {
if (!ChromeFeatureList.isEnabled(ChromeFeatureList.TRUSTED_WEB_ACTIVITY_POST_MESSAGE)) {
mConnection.resetPostMessageHandlerForSession(mSession, null);
}
mTrustedWebActivityUi.attemptVerificationForInitialUrl(url, getActivityTab());
mTrustedWebActivityUi.initialShowSnackbarIfNeeded();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && useSeparateTask()) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && useSeparateTask()) {
mTaskDescriptionHelper = new ActivityTabTaskDescriptionHelper(this, mTaskDescriptionHelper = new ActivityTabTaskDescriptionHelper(this,
ApiCompatibilityUtils.getColor(getResources(), R.color.default_primary_color)); ApiCompatibilityUtils.getColor(getResources(), R.color.default_primary_color));
...@@ -706,7 +656,7 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -706,7 +656,7 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
* with additional initialization logic. * with additional initialization logic.
*/ */
private Tab getHiddenTab() { private Tab getHiddenTab() {
String url = getUrlToLoad(); String url = mIntentDataProvider.getUrlToLoad();
String referrerUrl = mConnection.getReferrer(mSession, getIntent()); String referrerUrl = mConnection.getReferrer(mSession, getIntent());
Tab tab = mConnection.takeHiddenTab(mSession, url, referrerUrl); Tab tab = mConnection.takeHiddenTab(mSession, url, referrerUrl);
mUsingHiddenTab = tab != null; mUsingHiddenTab = tab != null;
...@@ -800,9 +750,6 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -800,9 +750,6 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
mTabObserverRegistrar.registerTabObserver(mTabObserver); mTabObserverRegistrar.registerTabObserver(mTabObserver);
mTabObserverRegistrar.registerTabObserver(mTabNavigationEventObserver); mTabObserverRegistrar.registerTabObserver(mTabNavigationEventObserver);
if (mTrustedWebActivityUi != null) {
mTabObserverRegistrar.registerTabObserver(mTrustedWebActivityUi.getTabObserver());
}
mTabObserverRegistrar.registerPageLoadMetricsObserver( mTabObserverRegistrar.registerPageLoadMetricsObserver(
new PageLoadMetricsObserver(mConnection, mSession, tab)); new PageLoadMetricsObserver(mConnection, mSession, tab));
mTabObserverRegistrar.registerPageLoadMetricsObserver( mTabObserverRegistrar.registerPageLoadMetricsObserver(
...@@ -929,10 +876,11 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -929,10 +876,11 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
} else { } else {
SharedPreferences preferences = ContextUtils.getAppSharedPreferences(); SharedPreferences preferences = ContextUtils.getAppSharedPreferences();
String lastUrl = preferences.getString(LAST_URL_PREF, null); String lastUrl = preferences.getString(LAST_URL_PREF, null);
if (lastUrl != null && lastUrl.equals(getUrlToLoad())) { String urlToLoad = mIntentDataProvider.getUrlToLoad();
if (lastUrl != null && lastUrl.equals(urlToLoad)) {
RecordUserAction.record("CustomTabsMenuOpenSameUrl"); RecordUserAction.record("CustomTabsMenuOpenSameUrl");
} else { } else {
preferences.edit().putString(LAST_URL_PREF, getUrlToLoad()).apply(); preferences.edit().putString(LAST_URL_PREF, urlToLoad).apply();
} }
if (mIntentDataProvider.isOpenedByChrome()) { if (mIntentDataProvider.isOpenedByChrome()) {
...@@ -956,8 +904,6 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -956,8 +904,6 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
} else if (isModuleLoading()) { } else if (isModuleLoading()) {
mModuleOnResumePending = true; mModuleOnResumePending = true;
} }
if (mTrustedWebActivityUi != null) mTrustedWebActivityUi.onResume();
} }
@Override @Override
...@@ -968,8 +914,6 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -968,8 +914,6 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
} }
if (mModuleActivityDelegate != null) mModuleActivityDelegate.onPause(); if (mModuleActivityDelegate != null) mModuleActivityDelegate.onPause();
mModuleOnResumePending = false; mModuleOnResumePending = false;
if (mTrustedWebActivityUi != null) mTrustedWebActivityUi.onPause();
} }
@Override @Override
...@@ -1027,7 +971,7 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -1027,7 +971,7 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
*/ */
private void loadUrlInTab(final Tab tab, final LoadUrlParams params, long timeStamp) { private void loadUrlInTab(final Tab tab, final LoadUrlParams params, long timeStamp) {
Intent intent = getIntent(); Intent intent = getIntent();
String url = getUrlToLoad(); String url = mIntentDataProvider.getUrlToLoad();
// Caching isFirstLoad value to deal with multiple return points. // Caching isFirstLoad value to deal with multiple return points.
boolean isFirstLoad = mIsFirstLoad; boolean isFirstLoad = mIsFirstLoad;
...@@ -1388,7 +1332,7 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -1388,7 +1332,7 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
if (DomDistillerUrlUtils.isDistilledPage(url)) { if (DomDistillerUrlUtils.isDistilledPage(url)) {
url = DomDistillerUrlUtils.getOriginalUrlFromDistillerUrl(url); url = DomDistillerUrlUtils.getOriginalUrlFromDistillerUrl(url);
} }
if (TextUtils.isEmpty(url)) url = getUrlToLoad(); if (TextUtils.isEmpty(url)) url = mIntentDataProvider.getUrlToLoad();
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(LaunchIntentDispatcher.EXTRA_IS_ALLOWED_TO_RETURN_TO_PARENT, false); intent.putExtra(LaunchIntentDispatcher.EXTRA_IS_ALLOWED_TO_RETURN_TO_PARENT, false);
...@@ -1435,32 +1379,6 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -1435,32 +1379,6 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
return true; return true;
} }
/**
* @return The URL that should be used from this intent. If it is a WebLite url, it may be
* overridden if the Data Reduction Proxy is using Lo-Fi previews.
*/
private String getUrlToLoad() {
String url = IntentHandler.getUrlFromIntent(getIntent());
// Intents fired for media viewers have an additional file:// URI passed along so that the
// tab can display the actual filename to the user when it is loaded.
if (mIntentDataProvider.isMediaViewer()) {
String mediaViewerUrl = mIntentDataProvider.getMediaViewerUrl();
if (!TextUtils.isEmpty(mediaViewerUrl)) {
Uri mediaViewerUri = Uri.parse(mediaViewerUrl);
if (UrlConstants.FILE_SCHEME.equals(mediaViewerUri.getScheme())) {
url = mediaViewerUrl;
}
}
}
if (!TextUtils.isEmpty(url)) {
url = DataReductionProxySettings.getInstance().maybeRewriteWebliteUrl(url);
}
return url;
}
/** Sets the initial background color for the Tab, shown before the page content is ready. */ /** Sets the initial background color for the Tab, shown before the page content is ready. */
private void prepareTabBackground(final Tab tab) { private void prepareTabBackground(final Tab tab) {
if (!IntentHandler.notSecureIsIntentChromeOrFirstParty(getIntent())) return; if (!IntentHandler.notSecureIsIntentChromeOrFirstParty(getIntent())) return;
...@@ -1580,7 +1498,9 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -1580,7 +1498,9 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
@Override @Override
protected CustomTabActivityComponent createComponent(ChromeActivityCommonsModule commonsModule, protected CustomTabActivityComponent createComponent(ChromeActivityCommonsModule commonsModule,
ContextualSuggestionsModule contextualSuggestionsModule) { ContextualSuggestionsModule contextualSuggestionsModule) {
return ChromeApplication.getComponent().createCustomTabActivityComponent(commonsModule, CustomTabActivityModule customTabsModule =
contextualSuggestionsModule); new CustomTabActivityModule(mIntentDataProvider, mTabObserverRegistrar);
return ChromeApplication.getComponent().createCustomTabActivityComponent(
commonsModule, contextualSuggestionsModule, customTabsModule);
} }
} }
// Copyright 2018 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;
import org.chromium.chrome.browser.ActivityTabProvider;
import org.chromium.chrome.browser.dependency_injection.ActivityScope;
import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
import org.chromium.chrome.browser.tab.BrowserControlsVisibilityDelegate;
import org.chromium.chrome.browser.tab.Tab;
import javax.inject.Inject;
/**
* Implementation of {@link BrowserControlsVisibilityDelegate} for custom tabs.
*/
@ActivityScope
public class CustomTabBrowserControlsVisibilityDelegate
implements BrowserControlsVisibilityDelegate {
private final BrowserControlsVisibilityDelegate mFullscreenManagerDelegate;
private final ActivityTabProvider mTabProvider;
private boolean mIsInTwaMode;
@Inject
public CustomTabBrowserControlsVisibilityDelegate(
ChromeFullscreenManager fullscreenManager, ActivityTabProvider tabProvider) {
mFullscreenManagerDelegate = fullscreenManager.getBrowserVisibilityDelegate();
mTabProvider = tabProvider;
}
/**
* Sets trusted web activity mode. In trusted web activity mode browser controls should be
* hidden.
*/
public void setTrustedWebActivityMode(boolean isInTwaMode) {
if (mIsInTwaMode == isInTwaMode) {
return;
}
mIsInTwaMode = isInTwaMode;
Tab activeTab = mTabProvider.getActivityTab();
if (activeTab != null) {
activeTab.updateFullscreenEnabledState();
}
}
@Override
public boolean canShowBrowserControls() {
return !mIsInTwaMode && mFullscreenManagerDelegate.canShowBrowserControls();
}
@Override
public boolean canAutoHideBrowserControls() {
return mFullscreenManagerDelegate.canAutoHideBrowserControls();
}
}
...@@ -35,8 +35,10 @@ import org.chromium.chrome.browser.ChromeFeatureList; ...@@ -35,8 +35,10 @@ import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.ChromeVersionInfo; import org.chromium.chrome.browser.ChromeVersionInfo;
import org.chromium.chrome.browser.IntentHandler; import org.chromium.chrome.browser.IntentHandler;
import org.chromium.chrome.browser.UrlConstants;
import org.chromium.chrome.browser.browserservices.BrowserSessionDataProvider; import org.chromium.chrome.browser.browserservices.BrowserSessionDataProvider;
import org.chromium.chrome.browser.externalauth.ExternalAuthUtils; import org.chromium.chrome.browser.externalauth.ExternalAuthUtils;
import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
import org.chromium.chrome.browser.util.ColorUtils; import org.chromium.chrome.browser.util.ColorUtils;
import org.chromium.chrome.browser.util.IntentUtils; import org.chromium.chrome.browser.util.IntentUtils;
import org.chromium.chrome.browser.widget.TintedDrawable; import org.chromium.chrome.browser.widget.TintedDrawable;
...@@ -143,6 +145,8 @@ public class CustomTabIntentDataProvider extends BrowserSessionDataProvider { ...@@ -143,6 +145,8 @@ public class CustomTabIntentDataProvider extends BrowserSessionDataProvider {
+ "To make locally-built Chrome a first-party app, sign with release-test " + "To make locally-built Chrome a first-party app, sign with release-test "
+ "signing keys and run on userdebug devices. See use_signing_keys GN arg."; + "signing keys and run on userdebug devices. See use_signing_keys GN arg.";
private final Intent mIntent;
private final int mUiType; private final int mUiType;
private final int mTitleVisibilityState; private final int mTitleVisibilityState;
private final String mMediaViewerUrl; private final String mMediaViewerUrl;
...@@ -156,6 +160,8 @@ public class CustomTabIntentDataProvider extends BrowserSessionDataProvider { ...@@ -156,6 +160,8 @@ public class CustomTabIntentDataProvider extends BrowserSessionDataProvider {
@Nullable @Nullable
private final ComponentName mModuleComponentName; private final ComponentName mModuleComponentName;
private final boolean mIsIncognito; private final boolean mIsIncognito;
@Nullable
private String mUrlToLoad;
private int mToolbarColor; private int mToolbarColor;
private int mBottomBarColor; private int mBottomBarColor;
...@@ -200,6 +206,7 @@ public class CustomTabIntentDataProvider extends BrowserSessionDataProvider { ...@@ -200,6 +206,7 @@ public class CustomTabIntentDataProvider extends BrowserSessionDataProvider {
super(intent); super(intent);
if (intent == null) assert false; if (intent == null) assert false;
mIntent = intent;
mIsOpenedByChrome = IntentUtils.safeGetBooleanExtra( mIsOpenedByChrome = IntentUtils.safeGetBooleanExtra(
intent, EXTRA_IS_OPENED_BY_CHROME, false); intent, EXTRA_IS_OPENED_BY_CHROME, false);
...@@ -407,6 +414,40 @@ public class CustomTabIntentDataProvider extends BrowserSessionDataProvider { ...@@ -407,6 +414,40 @@ public class CustomTabIntentDataProvider extends BrowserSessionDataProvider {
return color | 0xFF000000; return color | 0xFF000000;
} }
private String resolveUrlToLoad(Intent intent) {
String url = IntentHandler.getUrlFromIntent(intent);
// Intents fired for media viewers have an additional file:// URI passed along so that the
// tab can display the actual filename to the user when it is loaded.
if (isMediaViewer()) {
String mediaViewerUrl = getMediaViewerUrl();
if (!TextUtils.isEmpty(mediaViewerUrl)) {
Uri mediaViewerUri = Uri.parse(mediaViewerUrl);
if (UrlConstants.FILE_SCHEME.equals(mediaViewerUri.getScheme())) {
url = mediaViewerUrl;
}
}
}
if (!TextUtils.isEmpty(url)) {
url = DataReductionProxySettings.getInstance().maybeRewriteWebliteUrl(url);
}
return url;
}
/**
* @return The URL that should be used from this intent. If it is a WebLite url, it may be
* overridden if the Data Reduction Proxy is using Lo-Fi previews.
* Must be called only after native has loaded.
*/
public String getUrlToLoad() {
if (mUrlToLoad == null) {
mUrlToLoad = resolveUrlToLoad(mIntent);
}
return mUrlToLoad;
}
/** /**
* @return Whether url bar hiding should be enabled in the custom tab. Default is false. * @return Whether url bar hiding should be enabled in the custom tab. Default is false.
* It should be impossible to hide the url bar when the tab is opened for Payment Request. * It should be impossible to hide the url bar when the tab is opened for Payment Request.
......
...@@ -2,10 +2,14 @@ ...@@ -2,10 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
package org.chromium.chrome.browser.dependency_injection; package org.chromium.chrome.browser.customtabs.dependency_injection;
import org.chromium.chrome.browser.browserservices.TrustedWebActivityDisclosure; import org.chromium.chrome.browser.browserservices.TrustedWebActivityUi;
import org.chromium.chrome.browser.contextual_suggestions.ContextualSuggestionsModule; import org.chromium.chrome.browser.contextual_suggestions.ContextualSuggestionsModule;
import org.chromium.chrome.browser.customtabs.CustomTabBrowserControlsVisibilityDelegate;
import org.chromium.chrome.browser.dependency_injection.ActivityScope;
import org.chromium.chrome.browser.dependency_injection.ChromeActivityCommonsModule;
import org.chromium.chrome.browser.dependency_injection.ChromeActivityComponent;
import dagger.Subcomponent; import dagger.Subcomponent;
...@@ -13,8 +17,10 @@ import dagger.Subcomponent; ...@@ -13,8 +17,10 @@ import dagger.Subcomponent;
* Activity-scoped component associated with * Activity-scoped component associated with
* {@link org.chromium.chrome.browser.customtabs.CustomTabActivity}. * {@link org.chromium.chrome.browser.customtabs.CustomTabActivity}.
*/ */
@Subcomponent(modules = {ChromeActivityCommonsModule.class, ContextualSuggestionsModule.class}) @Subcomponent(modules = {ChromeActivityCommonsModule.class, ContextualSuggestionsModule.class,
CustomTabActivityModule.class})
@ActivityScope @ActivityScope
public interface CustomTabActivityComponent extends ChromeActivityComponent { public interface CustomTabActivityComponent extends ChromeActivityComponent {
TrustedWebActivityDisclosure resolveTrustedWebActivityDisclosure(); TrustedWebActivityUi resolveTrustedWebActivityUi();
CustomTabBrowserControlsVisibilityDelegate resolveControlsVisibilityDelegate();
} }
// Copyright 2018 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.dependency_injection;
import org.chromium.chrome.browser.browserservices.ClientAppDataRegister;
import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
import org.chromium.chrome.browser.customtabs.TabObserverRegistrar;
import dagger.Module;
import dagger.Provides;
/**
* Module for custom tab specific bindings.
*/
@Module
public class CustomTabActivityModule {
private final CustomTabIntentDataProvider mIntentDataProvider;
private final TabObserverRegistrar mTabObserverRegistrar;
public CustomTabActivityModule(CustomTabIntentDataProvider intentDataProvider,
TabObserverRegistrar tabObserverRegistrar) {
mIntentDataProvider = intentDataProvider;
mTabObserverRegistrar = tabObserverRegistrar;
}
@Provides
public CustomTabIntentDataProvider provideIntentDataProvider() {
return mIntentDataProvider;
}
@Provides
public ClientAppDataRegister provideClientAppDataRegister() {
return new ClientAppDataRegister();
}
@Provides
public TabObserverRegistrar provideTabObserverRegistrar() {
return mTabObserverRegistrar;
}
}
...@@ -4,16 +4,23 @@ ...@@ -4,16 +4,23 @@
package org.chromium.chrome.browser.dependency_injection; package org.chromium.chrome.browser.dependency_injection;
import static org.chromium.chrome.browser.dependency_injection.ChromeCommonQualifiers.ACTIVITY_CONTEXT;
import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import org.chromium.chrome.browser.ActivityTabProvider;
import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.compositor.layouts.LayoutManager; import org.chromium.chrome.browser.compositor.layouts.LayoutManager;
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;
import org.chromium.chrome.browser.snackbar.SnackbarManager;
import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.toolbar.ToolbarManager; import org.chromium.chrome.browser.toolbar.ToolbarManager;
import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController; import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
import javax.inject.Named;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
...@@ -64,8 +71,14 @@ public class ChromeActivityCommonsModule { ...@@ -64,8 +71,14 @@ public class ChromeActivityCommonsModule {
@Provides @Provides
public ChromeActivity provideChromeActivity() { public ChromeActivity provideChromeActivity() {
// Ideally this should provide only the Context instead of specific activity, but currently // Ideally providing Context should be enough, but currently a lot of code is coupled
// a lot of code is coupled specifically to ChromeActivity. // specifically to ChromeActivity.
return mActivity;
}
@Provides
@Named(ACTIVITY_CONTEXT)
public Context provideContext() {
return mActivity; return mActivity;
} }
...@@ -78,4 +91,14 @@ public class ChromeActivityCommonsModule { ...@@ -78,4 +91,14 @@ public class ChromeActivityCommonsModule {
public ActivityLifecycleDispatcher provideLifecycleDispatcher() { public ActivityLifecycleDispatcher provideLifecycleDispatcher() {
return mLifecycleDispatcher; return mLifecycleDispatcher;
} }
@Provides
public SnackbarManager provideSnackbarManager() {
return mActivity.getSnackbarManager();
}
@Provides
public ActivityTabProvider provideActivityTabProvider() {
return mActivity.getActivityTabProvider();
}
} }
...@@ -4,8 +4,11 @@ ...@@ -4,8 +4,11 @@
package org.chromium.chrome.browser.dependency_injection; package org.chromium.chrome.browser.dependency_injection;
import org.chromium.chrome.browser.AppHooksModule;
import org.chromium.chrome.browser.contextual_suggestions.ContextualSuggestionsModule; import org.chromium.chrome.browser.contextual_suggestions.ContextualSuggestionsModule;
import org.chromium.chrome.browser.contextual_suggestions.EnabledStateMonitor; import org.chromium.chrome.browser.contextual_suggestions.EnabledStateMonitor;
import org.chromium.chrome.browser.customtabs.dependency_injection.CustomTabActivityComponent;
import org.chromium.chrome.browser.customtabs.dependency_injection.CustomTabActivityModule;
import javax.inject.Singleton; import javax.inject.Singleton;
...@@ -14,14 +17,15 @@ import dagger.Component; ...@@ -14,14 +17,15 @@ import dagger.Component;
/** /**
* Component representing the Singletons in the main process of the application. * Component representing the Singletons in the main process of the application.
*/ */
@Component(modules = {ChromeAppModule.class}) @Component(modules = {ChromeAppModule.class, AppHooksModule.class})
@Singleton @Singleton
public interface ChromeAppComponent { public interface ChromeAppComponent {
ChromeActivityComponent createChromeActivityComponent(ChromeActivityCommonsModule module, ChromeActivityComponent createChromeActivityComponent(ChromeActivityCommonsModule module,
ContextualSuggestionsModule contextualSuggestionsModule); ContextualSuggestionsModule contextualSuggestionsModule);
CustomTabActivityComponent createCustomTabActivityComponent(ChromeActivityCommonsModule module, CustomTabActivityComponent createCustomTabActivityComponent(ChromeActivityCommonsModule module,
ContextualSuggestionsModule contextualSuggestionsModule); ContextualSuggestionsModule contextualSuggestionsModule,
CustomTabActivityModule customTabActivityModule);
// Temporary getters for DI migration process. All of these getters // Temporary getters for DI migration process. All of these getters
// should eventually be replaced with constructor injection. // should eventually be replaced with constructor injection.
......
...@@ -4,8 +4,12 @@ ...@@ -4,8 +4,12 @@
package org.chromium.chrome.browser.dependency_injection; package org.chromium.chrome.browser.dependency_injection;
import static org.chromium.chrome.browser.dependency_injection.ChromeCommonQualifiers.APP_CONTEXT;
import static org.chromium.chrome.browser.dependency_injection.ChromeCommonQualifiers.LAST_USED_PROFILE; import static org.chromium.chrome.browser.dependency_injection.ChromeCommonQualifiers.LAST_USED_PROFILE;
import android.content.Context;
import org.chromium.base.ContextUtils;
import org.chromium.chrome.browser.contextual_suggestions.EnabledStateMonitor; import org.chromium.chrome.browser.contextual_suggestions.EnabledStateMonitor;
import org.chromium.chrome.browser.contextual_suggestions.EnabledStateMonitorImpl; import org.chromium.chrome.browser.contextual_suggestions.EnabledStateMonitorImpl;
import org.chromium.chrome.browser.preferences.ChromePreferenceManager; import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
...@@ -41,4 +45,10 @@ public class ChromeAppModule { ...@@ -41,4 +45,10 @@ public class ChromeAppModule {
public ChromePreferenceManager providesChromePreferenceManager() { public ChromePreferenceManager providesChromePreferenceManager() {
return ChromePreferenceManager.getInstance(); return ChromePreferenceManager.getInstance();
} }
@Provides
@Named(APP_CONTEXT)
public Context provideContext() {
return ContextUtils.getApplicationContext();
}
} }
...@@ -10,4 +10,7 @@ package org.chromium.chrome.browser.dependency_injection; ...@@ -10,4 +10,7 @@ package org.chromium.chrome.browser.dependency_injection;
*/ */
public interface ChromeCommonQualifiers { public interface ChromeCommonQualifiers {
String LAST_USED_PROFILE = "LAST_USED_PROFILE"; String LAST_USED_PROFILE = "LAST_USED_PROFILE";
String ACTIVITY_CONTEXT = "ACTIVITY_CONTEXT";
String APP_CONTEXT = "APP_CONTEXT";
} }
...@@ -53,7 +53,7 @@ public class ChromeFullscreenManager ...@@ -53,7 +53,7 @@ public class ChromeFullscreenManager
private final boolean mExitFullscreenOnStop; private final boolean mExitFullscreenOnStop;
private final TokenHolder mHidingTokenHolder = new TokenHolder(this::scheduleVisibilityUpdate); private final TokenHolder mHidingTokenHolder = new TokenHolder(this::scheduleVisibilityUpdate);
private ControlContainer mControlContainer; @Nullable private ControlContainer mControlContainer;
private int mTopControlContainerHeight; private int mTopControlContainerHeight;
private int mBottomControlContainerHeight; private int mBottomControlContainerHeight;
private boolean mControlsResizeView; private boolean mControlsResizeView;
...@@ -225,6 +225,7 @@ public class ChromeFullscreenManager ...@@ -225,6 +225,7 @@ public class ChromeFullscreenManager
mRendererTopContentOffset = mTopControlContainerHeight; mRendererTopContentOffset = mTopControlContainerHeight;
updateControlOffset(); updateControlOffset();
scheduleVisibilityUpdate();
} }
/** /**
...@@ -398,8 +399,9 @@ public class ChromeFullscreenManager ...@@ -398,8 +399,9 @@ public class ChromeFullscreenManager
} }
/** /**
* @return The toolbar control container. * @return The toolbar control container, null until {@link #initialize} is called.
*/ */
@Nullable
public ControlContainer getControlContainer() { public ControlContainer getControlContainer() {
return mControlContainer; return mControlContainer;
} }
...@@ -486,6 +488,9 @@ public class ChromeFullscreenManager ...@@ -486,6 +488,9 @@ public class ChromeFullscreenManager
* animation, preventing message loop stalls due to untimely invalidation. * animation, preventing message loop stalls due to untimely invalidation.
*/ */
private void scheduleVisibilityUpdate() { private void scheduleVisibilityUpdate() {
if (mControlContainer == null) {
return;
}
final int desiredVisibility = shouldShowAndroidControls() ? View.VISIBLE : View.INVISIBLE; final int desiredVisibility = shouldShowAndroidControls() ? View.VISIBLE : View.INVISIBLE;
if (mControlContainer.getView().getVisibility() == desiredVisibility) return; if (mControlContainer.getView().getVisibility() == desiredVisibility) return;
mControlContainer.getView().removeCallbacks(mUpdateVisibilityRunnable); mControlContainer.getView().removeCallbacks(mUpdateVisibilityRunnable);
......
...@@ -26,6 +26,9 @@ import java.util.regex.Pattern; ...@@ -26,6 +26,9 @@ import java.util.regex.Pattern;
* Utilities for working with URIs (and URLs). These methods may be used in security-sensitive * Utilities for working with URIs (and URLs). These methods may be used in security-sensitive
* contexts (after all, origins are the security boundary on the web), and so the correctness bar * contexts (after all, origins are the security boundary on the web), and so the correctness bar
* must be high. * must be high.
*
* Use ShadowUrlUtilities to mock out native-dependent methods in tests.
* TODO(pshmakov): we probably should just make those methods non-static.
*/ */
public class UrlUtilities { public class UrlUtilities {
private static final String TAG = "UrlUtilities"; private static final String TAG = "UrlUtilities";
......
...@@ -14,6 +14,7 @@ chrome_java_sources = [ ...@@ -14,6 +14,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/ActivityTaskDescriptionIconGenerator.java", "java/src/org/chromium/chrome/browser/ActivityTaskDescriptionIconGenerator.java",
"java/src/org/chromium/chrome/browser/AfterStartupTaskUtils.java", "java/src/org/chromium/chrome/browser/AfterStartupTaskUtils.java",
"java/src/org/chromium/chrome/browser/AppHooks.java", "java/src/org/chromium/chrome/browser/AppHooks.java",
"java/src/org/chromium/chrome/browser/AppHooksModule.java",
"java/src/org/chromium/chrome/browser/AppIndexingUtil.java", "java/src/org/chromium/chrome/browser/AppIndexingUtil.java",
"java/src/org/chromium/chrome/browser/ApplicationInitialization.java", "java/src/org/chromium/chrome/browser/ApplicationInitialization.java",
"java/src/org/chromium/chrome/browser/ApplicationLifetime.java", "java/src/org/chromium/chrome/browser/ApplicationLifetime.java",
...@@ -385,6 +386,7 @@ chrome_java_sources = [ ...@@ -385,6 +386,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java", "java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java",
"java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java", "java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java",
"java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java", "java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java",
"java/src/org/chromium/chrome/browser/customtabs/CustomTabBrowserControlsVisibilityDelegate.java",
"java/src/org/chromium/chrome/browser/customtabs/CustomTabDelegateFactory.java", "java/src/org/chromium/chrome/browser/customtabs/CustomTabDelegateFactory.java",
"java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java", "java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java",
"java/src/org/chromium/chrome/browser/customtabs/CustomTabNavigationEventObserver.java", "java/src/org/chromium/chrome/browser/customtabs/CustomTabNavigationEventObserver.java",
...@@ -411,6 +413,8 @@ chrome_java_sources = [ ...@@ -411,6 +413,8 @@ 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/dependency_injection/CustomTabActivityComponent.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",
"java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ActivityHostImpl.java", "java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ActivityHostImpl.java",
"java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleEntryPoint.java", "java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleEntryPoint.java",
...@@ -425,7 +429,6 @@ chrome_java_sources = [ ...@@ -425,7 +429,6 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppComponent.java", "java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppComponent.java",
"java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppModule.java", "java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppModule.java",
"java/src/org/chromium/chrome/browser/dependency_injection/ChromeCommonQualifiers.java", "java/src/org/chromium/chrome/browser/dependency_injection/ChromeCommonQualifiers.java",
"java/src/org/chromium/chrome/browser/dependency_injection/CustomTabActivityComponent.java",
"java/src/org/chromium/chrome/browser/dependency_injection/ModuleFactoryOverrides.java", "java/src/org/chromium/chrome/browser/dependency_injection/ModuleFactoryOverrides.java",
"java/src/org/chromium/chrome/browser/device/DeviceClassManager.java", "java/src/org/chromium/chrome/browser/device/DeviceClassManager.java",
"java/src/org/chromium/chrome/browser/display_cutout/DisplayCutoutController.java", "java/src/org/chromium/chrome/browser/display_cutout/DisplayCutoutController.java",
......
...@@ -10,12 +10,16 @@ import static org.mockito.ArgumentMatchers.anyString; ...@@ -10,12 +10,16 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
...@@ -25,35 +29,29 @@ import org.robolectric.annotation.Config; ...@@ -25,35 +29,29 @@ import org.robolectric.annotation.Config;
import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.Feature; import org.chromium.base.test.util.Feature;
import org.chromium.chrome.browser.util.test.ShadowUrlUtilities;
/** /**
* Tests for {@link ClientAppDataRecorder}. * Tests for {@link ClientAppDataRecorder}.
*/ */
@RunWith(BaseRobolectricTestRunner.class) @RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE) @Config(manifest = Config.NONE, shadows = {ShadowUrlUtilities.class})
public class ClientAppDataRecorderTest { public class ClientAppDataRecorderTest {
private static final int APP_UID = 123; private static final int APP_UID = 123;
private static final String APP_NAME = "Example App"; private static final String APP_NAME = "Example App";
private static final String APP_PACKAGE = "com.example.app"; private static final String APP_PACKAGE = "com.example.app";
private static final String MISSING_PACKAGE = "com.missing.app"; private static final String MISSING_PACKAGE = "com.missing.app";
private static final Origin ORIGIN = new Origin("https://www.example.com/"); private static final Origin ORIGIN = new Origin("https://www.example.com/");
private static final Origin OTHER_ORIGIN = new Origin("https://www.example.com/"); private static final Origin OTHER_ORIGIN = new Origin("https://www.other.com/");
@Mock public ClientAppDataRegister mRegister; @Mock private ClientAppDataRegister mRegister;
@Mock public PackageManager mPackageManager; @Mock private PackageManager mPackageManager;
private ClientAppDataRecorder mRecorder; private ClientAppDataRecorder mRecorder;
private final ClientAppDataRecorder.UrlTransformer mUrlTransformer =
new ClientAppDataRecorder.UrlTransformer() { private static String transform(String origin) {
@Override
String getDomain(Origin origin) {
return transform(origin);
}
};
private static String transform(Origin origin) {
// Just an arbitrary string transformation so we can check it is applied. // Just an arbitrary string transformation so we can check it is applied.
return origin.toString().toUpperCase(); return origin.toUpperCase();
} }
@Before @Before
...@@ -72,7 +70,22 @@ public class ClientAppDataRecorderTest { ...@@ -72,7 +70,22 @@ public class ClientAppDataRecorderTest {
.when(mPackageManager) .when(mPackageManager)
.getApplicationInfo(eq(MISSING_PACKAGE), anyInt()); .getApplicationInfo(eq(MISSING_PACKAGE), anyInt());
mRecorder = new ClientAppDataRecorder(mPackageManager, mRegister, mUrlTransformer); Context context = mock(Context.class);
when(context.getPackageManager()).thenReturn(mPackageManager);
ShadowUrlUtilities.setTestImpl(new ShadowUrlUtilities.TestImpl() {
@Override
public String getDomainAndRegistry(String uri, boolean includePrivateRegistries) {
return transform(uri);
}
});
mRecorder = new ClientAppDataRecorder(context, mRegister);
}
@After
public void tearDown() {
ShadowUrlUtilities.reset();
} }
...@@ -80,7 +93,7 @@ public class ClientAppDataRecorderTest { ...@@ -80,7 +93,7 @@ public class ClientAppDataRecorderTest {
@Feature("TrustedWebActivities") @Feature("TrustedWebActivities")
public void testRegister() { public void testRegister() {
mRecorder.register(APP_PACKAGE, ORIGIN); mRecorder.register(APP_PACKAGE, ORIGIN);
verify(mRegister).registerPackageForDomain(APP_UID, APP_NAME, transform(ORIGIN)); verify(mRegister).registerPackageForDomain(APP_UID, APP_NAME, transform(ORIGIN.toString()));
} }
@Test @Test
...@@ -88,7 +101,7 @@ public class ClientAppDataRecorderTest { ...@@ -88,7 +101,7 @@ public class ClientAppDataRecorderTest {
public void testDeduplicate() { public void testDeduplicate() {
mRecorder.register(APP_PACKAGE, ORIGIN); mRecorder.register(APP_PACKAGE, ORIGIN);
mRecorder.register(APP_PACKAGE, ORIGIN); mRecorder.register(APP_PACKAGE, ORIGIN);
verify(mRegister).registerPackageForDomain(APP_UID, APP_NAME, transform(ORIGIN)); verify(mRegister).registerPackageForDomain(APP_UID, APP_NAME, transform(ORIGIN.toString()));
} }
@Test @Test
...@@ -96,9 +109,9 @@ public class ClientAppDataRecorderTest { ...@@ -96,9 +109,9 @@ public class ClientAppDataRecorderTest {
public void testDifferentOrigins() { public void testDifferentOrigins() {
mRecorder.register(APP_PACKAGE, ORIGIN); mRecorder.register(APP_PACKAGE, ORIGIN);
mRecorder.register(APP_PACKAGE, OTHER_ORIGIN); mRecorder.register(APP_PACKAGE, OTHER_ORIGIN);
verify(mRegister).registerPackageForDomain(APP_UID, APP_NAME, transform(ORIGIN)); verify(mRegister).registerPackageForDomain(APP_UID, APP_NAME, transform(ORIGIN.toString()));
verify(mRegister).registerPackageForDomain( verify(mRegister).registerPackageForDomain(
APP_UID, APP_NAME, transform(OTHER_ORIGIN)); APP_UID, APP_NAME, transform(OTHER_ORIGIN.toString()));
} }
@Test @Test
......
...@@ -30,11 +30,14 @@ import org.chromium.chrome.browser.util.test.ShadowUrlUtilities; ...@@ -30,11 +30,14 @@ import org.chromium.chrome.browser.util.test.ShadowUrlUtilities;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/** Tests for {@link DomainDataCleaner}. */ /** Tests for {@link DomainDataCleaner}. */
@RunWith(BaseRobolectricTestRunner.class) @RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE, shadows = {ShadowUrlUtilities.class}) @Config(manifest = Config.NONE, shadows = {ShadowUrlUtilities.class})
public class DomainDataCleanerTest { public class DomainDataCleanerTest {
private final Map<String, String> mUrlToDomain = new HashMap<>();
@Mock @Mock
private ChromeBrowserInitializer mChromeBrowserInitializer; private ChromeBrowserInitializer mChromeBrowserInitializer;
@Mock @Mock
...@@ -64,6 +67,13 @@ public class DomainDataCleanerTest { ...@@ -64,6 +67,13 @@ public class DomainDataCleanerTest {
return null; return null;
}).when(mWebsitePermissionsFetcher).fetchAllPreferences(any()); }).when(mWebsitePermissionsFetcher).fetchAllPreferences(any());
ShadowUrlUtilities.setTestImpl(new ShadowUrlUtilities.TestImpl() {
@Override
public String getDomainAndRegistry(String url, boolean includePrivateRegistries) {
return mUrlToDomain.get(url);
}
});
mCleaner = new DomainDataCleaner( mCleaner = new DomainDataCleaner(
mChromeBrowserInitializer, mSiteDataCleaner, mWebsitePermissionsFetcher); mChromeBrowserInitializer, mSiteDataCleaner, mWebsitePermissionsFetcher);
} }
...@@ -104,9 +114,9 @@ public class DomainDataCleanerTest { ...@@ -104,9 +114,9 @@ public class DomainDataCleanerTest {
verify(mFinishCallback).run(); verify(mFinishCallback).run();
} }
private Website makeMockWebsite(String origin, String domainAndRegistry) { private Website makeMockWebsite(String origin, String domain) {
Website website = new Website(WebsiteAddress.create(origin), null); Website website = new Website(WebsiteAddress.create(origin), null);
ShadowUrlUtilities.setUrlToToDomainMapping(origin, domainAndRegistry); mUrlToDomain.put(origin, domain);
return website; return website;
} }
......
...@@ -48,14 +48,15 @@ public class TrustedWebActivityDisclosureTest { ...@@ -48,14 +48,15 @@ public class TrustedWebActivityDisclosureTest {
doReturn("any string").when(mResources).getString(anyInt()); doReturn("any string").when(mResources).getString(anyInt());
doReturn(false).when(mPreferences).hasUserAcceptedTwaDisclosureForPackage(anyString()); doReturn(false).when(mPreferences).hasUserAcceptedTwaDisclosureForPackage(anyString());
mDisclosure = new TrustedWebActivityDisclosure(mResources, mPreferences); mDisclosure = new TrustedWebActivityDisclosure(mResources, mPreferences,
() -> mSnackbarManager);
} }
@Test @Test
@Feature("TrustedWebActivities") @Feature("TrustedWebActivities")
public void showIsIdempotent() { public void showIsIdempotent() {
mDisclosure.showSnackbarIfNeeded(mSnackbarManager, TWA_PACKAGE); mDisclosure.showIfNeeded(TWA_PACKAGE);
mDisclosure.showSnackbarIfNeeded(mSnackbarManager, TWA_PACKAGE); mDisclosure.showIfNeeded(TWA_PACKAGE);
verify(mSnackbarManager).showSnackbar(any()); verify(mSnackbarManager).showSnackbar(any());
} }
...@@ -63,10 +64,10 @@ public class TrustedWebActivityDisclosureTest { ...@@ -63,10 +64,10 @@ public class TrustedWebActivityDisclosureTest {
@Test @Test
@Feature("TrustedWebActivities") @Feature("TrustedWebActivities")
public void hideIsIdempotent() { public void hideIsIdempotent() {
mDisclosure.showSnackbarIfNeeded(mSnackbarManager, TWA_PACKAGE); mDisclosure.showIfNeeded(TWA_PACKAGE);
mDisclosure.dismissSnackbarIfNeeded(mSnackbarManager); mDisclosure.dismiss();
mDisclosure.dismissSnackbarIfNeeded(mSnackbarManager); mDisclosure.dismiss();
verify(mSnackbarManager).dismissSnackbars(any()); verify(mSnackbarManager).dismissSnackbars(any());
} }
...@@ -76,7 +77,7 @@ public class TrustedWebActivityDisclosureTest { ...@@ -76,7 +77,7 @@ public class TrustedWebActivityDisclosureTest {
public void noShowIfAlreadyAccepted() { public void noShowIfAlreadyAccepted() {
doReturn(true).when(mPreferences).hasUserAcceptedTwaDisclosureForPackage(anyString()); doReturn(true).when(mPreferences).hasUserAcceptedTwaDisclosureForPackage(anyString());
mDisclosure.showSnackbarIfNeeded(mSnackbarManager, TWA_PACKAGE); mDisclosure.showIfNeeded(TWA_PACKAGE);
verify(mSnackbarManager, times(0)).showSnackbar(any()); verify(mSnackbarManager, times(0)).showSnackbar(any());
} }
...@@ -85,7 +86,7 @@ public class TrustedWebActivityDisclosureTest { ...@@ -85,7 +86,7 @@ public class TrustedWebActivityDisclosureTest {
@Feature("TrustedWebActivities") @Feature("TrustedWebActivities")
public void recordDismiss() { public void recordDismiss() {
ArgumentCaptor<Snackbar> snackbarCaptor = ArgumentCaptor.forClass(Snackbar.class); ArgumentCaptor<Snackbar> snackbarCaptor = ArgumentCaptor.forClass(Snackbar.class);
mDisclosure.showSnackbarIfNeeded(mSnackbarManager, TWA_PACKAGE); mDisclosure.showIfNeeded(TWA_PACKAGE);
verify(mSnackbarManager).showSnackbar(snackbarCaptor.capture()); verify(mSnackbarManager).showSnackbar(snackbarCaptor.capture());
...@@ -101,7 +102,7 @@ public class TrustedWebActivityDisclosureTest { ...@@ -101,7 +102,7 @@ public class TrustedWebActivityDisclosureTest {
@Feature("TrustedWebActivities") @Feature("TrustedWebActivities")
public void doNothingOnNoSnackbarAction() { public void doNothingOnNoSnackbarAction() {
ArgumentCaptor<Snackbar> snackbarCaptor = ArgumentCaptor.forClass(Snackbar.class); ArgumentCaptor<Snackbar> snackbarCaptor = ArgumentCaptor.forClass(Snackbar.class);
mDisclosure.showSnackbarIfNeeded(mSnackbarManager, TWA_PACKAGE); mDisclosure.showIfNeeded(TWA_PACKAGE);
verify(mSnackbarManager).showSnackbar(snackbarCaptor.capture()); verify(mSnackbarManager).showSnackbar(snackbarCaptor.capture());
......
...@@ -8,35 +8,44 @@ import android.text.TextUtils; ...@@ -8,35 +8,44 @@ import android.text.TextUtils;
import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements; import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import org.chromium.chrome.browser.util.UrlUtilities; import org.chromium.chrome.browser.util.UrlUtilities;
import java.util.HashMap;
import java.util.Map;
/** Implementation of UrlUtilities which does not rely on native. */ /** Implementation of UrlUtilities which does not rely on native. */
@Implements(UrlUtilities.class) @Implements(UrlUtilities.class)
public class ShadowUrlUtilities { public class ShadowUrlUtilities {
private static Map<String, String> sUrlToToDomain = new HashMap<>();
private static TestImpl sTestImpl = new TestImpl();
/** Set implementation for tests. Don't forget to call {@link #reset} later. */
public static void setTestImpl(TestImpl impl) {
sTestImpl = impl;
}
@Resetter
public static void reset() {
sTestImpl = new TestImpl();
}
@Implementation @Implementation
public static boolean urlsMatchIgnoringFragments(String url, String url2) { public static boolean urlsMatchIgnoringFragments(String url, String url2) {
return TextUtils.equals(url, url2); return sTestImpl.urlsMatchIgnoringFragments(url, url2);
} }
@Implementation @Implementation
public static String getDomainAndRegistry(String url, boolean includePrivateRegistries) { public static String getDomainAndRegistry(String uri, boolean includePrivateRegistries) {
String domain = sUrlToToDomain.get(url); return sTestImpl.getDomainAndRegistry(uri, includePrivateRegistries);
return domain == null ? url : domain;
} }
/** Add a url to domain mapping. */ /** Default implementation for tests. Override methods or add new ones as necessary. */
public static void setUrlToToDomainMapping(String url, String domain) { public static class TestImpl {
sUrlToToDomain.put(url, domain); public boolean urlsMatchIgnoringFragments(String url, String url2) {
} return TextUtils.equals(url, url2);
}
/** Clear the static state. */ public String getDomainAndRegistry(String uri, boolean includePrivateRegistries) {
public static void reset() { return uri;
sUrlToToDomain.clear(); }
} }
} }
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