Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1948)

Unified Diff: chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeMobileApplication.java

Issue 1141283003: Upstream oodles of Chrome for Android code into Chromium. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: final patch? Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeMobileApplication.java
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeMobileApplication.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeMobileApplication.java
new file mode 100644
index 0000000000000000000000000000000000000000..2fe4333b8b9175a5498a7216b556677b1d447828
--- /dev/null
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/ChromeMobileApplication.java
@@ -0,0 +1,602 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Handler;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.View;
+
+import com.google.ipc.invalidation.external.client.android.service.AndroidLogger;
+
+import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.base.ApplicationState;
+import org.chromium.base.ApplicationStatus;
+import org.chromium.base.ApplicationStatus.ApplicationStateListener;
+import org.chromium.base.PathUtils;
+import org.chromium.base.ResourceExtractor;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.base.annotations.SuppressFBWarnings;
+import org.chromium.chrome.browser.accessibility.FontSizePrefs;
+import org.chromium.chrome.browser.banners.AppBannerManager;
+import org.chromium.chrome.browser.banners.AppDetailsDelegate;
+import org.chromium.chrome.browser.document.ChromeLauncherActivity;
+import org.chromium.chrome.browser.document.DocumentActivity;
+import org.chromium.chrome.browser.document.IncognitoDocumentActivity;
+import org.chromium.chrome.browser.document.TabDelegateImpl;
+import org.chromium.chrome.browser.download.DownloadManagerService;
+import org.chromium.chrome.browser.firstrun.FirstRunActivityStaging;
+import org.chromium.chrome.browser.gsa.GSAHelper;
+import org.chromium.chrome.browser.help.HelpAndFeedback;
+import org.chromium.chrome.browser.identity.UniqueIdentificationGeneratorFactory;
+import org.chromium.chrome.browser.identity.UuidBasedUniqueIdentificationGenerator;
+import org.chromium.chrome.browser.invalidation.UniqueIdInvalidationClientNameGenerator;
+import org.chromium.chrome.browser.metrics.UmaUtils;
+import org.chromium.chrome.browser.metrics.VariationsSession;
+import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
+import org.chromium.chrome.browser.omaha.RequestGenerator;
+import org.chromium.chrome.browser.omaha.UpdateInfoBarHelper;
+import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations;
+import org.chromium.chrome.browser.policy.PolicyAuditor;
+import org.chromium.chrome.browser.policy.PolicyManager;
+import org.chromium.chrome.browser.policy.PolicyManager.PolicyChangeListener;
+import org.chromium.chrome.browser.policy.providers.AppRestrictionsProvider;
+import org.chromium.chrome.browser.preferences.AccessibilityPreferences;
+import org.chromium.chrome.browser.preferences.PrefServiceBridge;
+import org.chromium.chrome.browser.preferences.PreferencesStaging;
+import org.chromium.chrome.browser.printing.PrintingControllerFactory;
+import org.chromium.chrome.browser.rlz.RevenueStats;
+import org.chromium.chrome.browser.services.GoogleServicesManager;
+import org.chromium.chrome.browser.share.ShareHelper;
+import org.chromium.chrome.browser.smartcard.EmptyPKCS11AuthenticationManager;
+import org.chromium.chrome.browser.smartcard.PKCS11AuthenticationManager;
+import org.chromium.chrome.browser.sync.SyncController;
+import org.chromium.chrome.browser.tab.AuthenticatorNavigationInterceptor;
+import org.chromium.chrome.browser.tabmodel.document.ActivityDelegate;
+import org.chromium.chrome.browser.tabmodel.document.DocumentTabModelSelector;
+import org.chromium.content.browser.ChildProcessLauncher;
+import org.chromium.content.browser.ContentViewStatics;
+import org.chromium.content.browser.DownloadController;
+import org.chromium.printing.PrintingController;
+import org.chromium.ui.UiUtils;
+
+import java.util.Locale;
+
+/**
+ * Per-process class that manages classes and functions shared by all of Chrome's Activities.
+ */
+public class ChromeMobileApplication extends ChromiumApplication {
+ private static final String PREF_LOCALE = "locale";
+ private static final float FLOAT_EPSILON = 0.001f;
+
+ private static DocumentTabModelSelector sDocumentTabModelSelector;
+
+ /**
+ * This class allows pausing scripts & network connections when we
+ * go to the background and resume when we are back in foreground again.
+ * TODO(pliard): Get rid of this class once JavaScript timers toggling is done directly on
+ * the native side by subscribing to the system monitor events.
+ */
+ private static class BackgroundProcessing {
+ private class SuspendRunnable implements Runnable {
+ @Override
+ public void run() {
+ mSuspendRunnable = null;
+ assert !mWebKitTimersAreSuspended;
+ mWebKitTimersAreSuspended = true;
+ ContentViewStatics.setWebKitSharedTimersSuspended(true);
+ }
+ }
+
+ private static final int SUSPEND_TIMERS_AFTER_MS = 5 * 60 * 1000;
+ private final Handler mHandler = new Handler();
+ private boolean mWebKitTimersAreSuspended = false;
+ private SuspendRunnable mSuspendRunnable;
+
+ private void onDestroy() {
+ if (mSuspendRunnable != null) {
+ mHandler.removeCallbacks(mSuspendRunnable);
+ mSuspendRunnable = null;
+ }
+ }
+
+ private void suspendTimers() {
+ if (mSuspendRunnable == null) {
+ mSuspendRunnable = new SuspendRunnable();
+ mHandler.postDelayed(mSuspendRunnable, SUSPEND_TIMERS_AFTER_MS);
+ }
+ }
+
+ private void startTimers() {
+ if (mSuspendRunnable != null) {
+ mHandler.removeCallbacks(mSuspendRunnable);
+ mSuspendRunnable = null;
+ } else if (mWebKitTimersAreSuspended) {
+ ContentViewStatics.setWebKitSharedTimersSuspended(false);
+ mWebKitTimersAreSuspended = false;
+ }
+ }
+ }
+
+ private static final String[] CHROME_MANDATORY_PAKS = {
+ "en-US.pak", "resources.pak", "chrome_100_percent.pak", "icudtl.dat",
+ "natives_blob.bin", "snapshot_blob.bin"
+ };
+ private static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "chrome";
+ private static final String DEV_TOOLS_SERVER_SOCKET_PREFIX = "chrome";
+ private static final String SESSIONS_UUID_PREF_KEY = "chromium.sync.sessions.id";
+
+ private final BackgroundProcessing mBackgroundProcessing = new BackgroundProcessing();
+ private final PowerBroadcastReceiver mPowerBroadcastReceiver = new PowerBroadcastReceiver();
+ private final UpdateInfoBarHelper mUpdateInfoBarHelper = new UpdateInfoBarHelper();
+
+ // Used to trigger variation changes (such as seed fetches) upon application foregrounding.
+ private VariationsSession mVariationsSession;
+
+ private DevToolsServer mDevToolsServer;
+
+ private boolean mIsStarted;
+ private boolean mInitializedSharedClasses;
+ private boolean mIsProcessInitialized;
+
+ private ChromeLifetimeController mChromeLifetimeController;
+ private PrintingController mPrintingController;
+ private PolicyManager mPolicyManager;
+
+ /**
+ * This is called once per ChromeMobileApplication instance, which get created per process
+ * (browser OR renderer). Don't stick anything in here that shouldn't be called multiple times
+ * during Chrome's lifetime.
+ */
+ @Override
+ public void onCreate() {
+ UmaUtils.recordMainEntryPointTime();
+ super.onCreate();
+ UiUtils.setKeyboardShowingDelegate(new UiUtils.KeyboardShowingDelegate() {
+ @Override
+ public boolean disableKeyboardCheck(Context context, View view) {
+ Activity activity = null;
+ if (context instanceof Activity) {
+ activity = (Activity) context;
+ } else if (view != null && view.getContext() instanceof Activity) {
+ activity = (Activity) view.getContext();
+ }
+
+ // For multiwindow mode we do not track keyboard visibility.
+ return activity != null && isMultiWindow(activity);
+ }
+ });
+
+ // Set the unique identification generator for invalidations. The
+ // invalidations system can start and attempt to fetch the client ID
+ // very early. We need this generator to be ready before that happens.
+ UniqueIdInvalidationClientNameGenerator.doInitializeAndInstallGenerator(this);
+
+ // Set minimum Tango log level. This sets an in-memory static field, and needs to be
+ // set in the ApplicationContext instead of an activity, since Tango can be woken up
+ // by the system directly though messages from GCM.
+ AndroidLogger.setMinimumAndroidLogLevel(Log.WARN);
+
+ // Set up the identification generator for sync. The ID is actually generated
+ // in the SyncController constructor.
+ UniqueIdentificationGeneratorFactory.registerGenerator(SyncController.GENERATOR_ID,
+ new UuidBasedUniqueIdentificationGenerator(this, SESSIONS_UUID_PREF_KEY), false);
+ }
+
+ @Override
+ protected void initializeLibraryDependencies() {
+ // The ResourceExtractor is only needed by the browser process, but this will have no
+ // impact on the renderer process construction.
+ ResourceExtractor.setMandatoryPaksToExtract(CHROME_MANDATORY_PAKS);
+ PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX, this);
+ }
+
+ @Override
+ protected void initializeGoogleServicesManager() {
+ GoogleServicesManager.get(getApplicationContext());
+ }
+
+ @Override
+ public void initCommandLine() {
+ ChromeCommandLineInitUtil.initChromeCommandLine(this);
+ }
+
+ /**
+ * The host activity should call this after the native library has loaded to ensure classes
+ * shared by Activities in the same process are properly initialized.
+ */
+ public void initializeSharedClasses() {
+ if (mInitializedSharedClasses) return;
+ mInitializedSharedClasses = true;
+
+ GoogleServicesManager.get(this).onMainActivityStart();
+ RevenueStats.getInstance();
+ ShortcutHelper.setFullScreenAction(ChromeLauncherActivity.ACTION_START_WEBAPP);
+
+ getPKCS11AuthenticationManager().initialize(ChromeMobileApplication.this);
+
+ mDevToolsServer = new DevToolsServer(DEV_TOOLS_SERVER_SOCKET_PREFIX);
+ mDevToolsServer.setRemoteDebuggingEnabled(
+ true, DevToolsServer.Security.ALLOW_DEBUG_PERMISSION);
+
+ startApplicationActivityTracker();
+
+ DownloadController.setDownloadNotificationService(
+ DownloadManagerService.getDownloadManagerService(this));
+
+ if (ApiCompatibilityUtils.isPrintingSupported()) {
+ mPrintingController = PrintingControllerFactory.create(getApplicationContext());
+ }
+ }
+
+ /**
+ * @return The Application's PowerBroadcastReceiver.
+ */
+ @VisibleForTesting
+ public PowerBroadcastReceiver getPowerBroadcastReceiver() {
+ return mPowerBroadcastReceiver;
+ }
+
+ /**
+ * Update the font size after changing the Android accessibility system setting. Doing so kills
+ * the Activities but it doesn't kill the ChromeMobileApplication, so this should be called in
+ * {@link #onStart} instead of {@link #initialize}.
+ */
+ private void updateFontSize() {
+ // This method is currently broken. http://crbug.com/439108
+ // Skip it (with the consequence of not updating the text scaling factor when the user
+ // changes system font size) rather than incurring the broken behavior.
+ // TODO(newt): fix this.
+ if (true) return;
+
+ FontSizePrefs fontSizePrefs = FontSizePrefs.getInstance(getApplicationContext());
+
+ // Set font scale factor as the product of the system and browser scale settings.
+ float browserTextScale = PreferenceManager
+ .getDefaultSharedPreferences(this)
+ .getFloat(AccessibilityPreferences.PREF_TEXT_SCALE, 1.0f);
+ float fontScale = getResources().getConfiguration().fontScale * browserTextScale;
+
+ float scaleDelta = Math.abs(fontScale - fontSizePrefs.getFontScaleFactor());
+ if (scaleDelta >= FLOAT_EPSILON) {
+ fontSizePrefs.setFontScaleFactor(fontScale);
+ }
+
+ // If force enable zoom has not been manually set, set it automatically based on
+ // font scale factor.
+ boolean shouldForceZoom =
+ fontScale >= AccessibilityPreferences.FORCE_ENABLE_ZOOM_THRESHOLD_MULTIPLIER;
+ if (!fontSizePrefs.getUserSetForceEnableZoom()
+ && fontSizePrefs.getForceEnableZoom() != shouldForceZoom) {
+ fontSizePrefs.setForceEnableZoom(shouldForceZoom);
+ }
+ }
+
+ /**
+ * Update the accept languages after changing Android locale setting. Doing so kills the
+ * Activities but it doesn't kill the ChromeMobileApplication, so this should be called in
+ * {@link #onStart} instead of {@link #initialize}.
+ */
+ private void updateAcceptLanguages() {
+ PrefServiceBridge instance = PrefServiceBridge.getInstance();
+ String localeString = Locale.getDefault().toString(); // ex) en_US, de_DE, zh_CN_#Hans
+ if (hasLocaleChanged(localeString)) {
+ instance.resetAcceptLanguages(localeString);
+ // Clear cache so that accept-languages change can be applied immediately.
+ // TODO(changwan): The underlying BrowsingDataRemover::Remove() is an asynchronous call.
+ // So cache-clearing may not be effective if URL rendering can happen before
+ // OnBrowsingDataRemoverDone() is called, in which case we may have to reload as well.
+ // Check if it can happen.
+ instance.clearBrowsingData(null, false, true /* cache */, false, false, false);
+ }
+ }
+
+ private boolean hasLocaleChanged(String newLocale) {
+ String previousLocale = PreferenceManager.getDefaultSharedPreferences(this).getString(
+ PREF_LOCALE, "");
+
+ if (!previousLocale.equals(newLocale)) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putString(PREF_LOCALE, newLocale);
+ editor.apply();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Should be called almost immediately after the native library has loaded to initialize things
+ * that really, really have to be set up early. Avoid putting any long tasks here.
+ */
+ @Override
+ public void initializeProcess() {
+ if (mIsProcessInitialized) return;
+ mIsProcessInitialized = true;
+ assert !mIsStarted;
+
+ super.initializeProcess();
+
+ mVariationsSession = createVariationsSession();
+ removeSessionCookies();
+ ApplicationStatus.registerApplicationStateListener(createApplicationStateListener());
+ AppBannerManager.setAppDetailsDelegate(createAppDetailsDelegate());
+ mChromeLifetimeController = new ChromeLifetimeController(this);
+ mPolicyManager = new PolicyManager();
+ registerPolicyProviders(mPolicyManager);
+
+ PrefServiceBridge.getInstance().migratePreferences(this);
+ }
+
+ /**
+ * Each top-level activity (ChromeTabbedActivity, FullscreenActivity) should call this during
+ * its onStart phase. When called for the first time, this marks the beginning of a foreground
+ * session and calls onForegroundSessionStart(). Subsequent calls are noops until
+ * onForegroundSessionEnd() is called, to handle changing top-level Chrome activities in one
+ * foreground session.
+ */
+ public void onStartWithNative() {
+ if (mIsStarted) return;
+ mIsStarted = true;
+
+ assert mIsProcessInitialized;
+
+ onForegroundSessionStart();
+ }
+
+ /**
+ * Called when a top-level Chrome activity (ChromeTabbedActivity, FullscreenActivity) is
+ * started in foreground. It will not be called again when other Chrome activities take over
+ * (see onStart()), that is, when correct activity calls startActivity() for another Chrome
+ * activity.
+ */
+ private void onForegroundSessionStart() {
+ ChildProcessLauncher.onBroughtToForeground();
+ mBackgroundProcessing.startTimers();
+ updatePasswordEchoState();
+ updateFontSize();
+ updateAcceptLanguages();
+ changeAppStatus(true);
+ mVariationsSession.start(getApplicationContext());
+
+ mPowerBroadcastReceiver.registerReceiver(this);
+ mPowerBroadcastReceiver.runActions(this, true);
+ }
+
+ /**
+ * Called when last of Chrome activities is stopped, ending the foreground session. This will
+ * not be called when a Chrome activity is stopped because another Chrome activity takes over.
+ * This is ensured by ActivityStatus, which switches to track new activity when its started and
+ * will not report the old one being stopped (see createStateListener() below).
+ */
+ private void onForegroundSessionEnd() {
+ if (!mIsStarted) return;
+ mBackgroundProcessing.suspendTimers();
+ flushPersistentData();
+ mIsStarted = false;
+ changeAppStatus(false);
+
+ try {
+ mPowerBroadcastReceiver.unregisterReceiver(this);
+ } catch (IllegalArgumentException e) {
+ // This may happen when onStop get called very early in UI test.
+ }
+
+ ChildProcessLauncher.onSentToBackground();
+ }
+
+ /**
+ * Called after onForegroundSessionEnd() indicating that the activity whose onStop() ended the
+ * last foreground session was destroyed.
+ */
+ private void onForegroundActivityDestroyed() {
+ if (ApplicationStatus.isEveryActivityDestroyed()) {
+ mBackgroundProcessing.onDestroy();
+ stopApplicationActivityTracker();
+ PartnerBrowserCustomizations.destroy();
+ ShareHelper.clearSharedScreenshots(this);
+ mPolicyManager.destroy();
+ mPolicyManager = null;
+ }
+ }
+
+ private ApplicationStateListener createApplicationStateListener() {
+ return new ApplicationStateListener() {
+ @Override
+ public void onApplicationStateChange(int newState) {
+ if (newState == ApplicationState.HAS_STOPPED_ACTIVITIES) {
+ onForegroundSessionEnd();
+ } else if (newState == ApplicationState.HAS_DESTROYED_ACTIVITIES) {
+ onForegroundActivityDestroyed();
+ }
+ }
+ };
+ }
+
+ /**
+ * Returns a new instance of HelpAndFeedback.
+ */
+ public HelpAndFeedback createHelpAndFeedback() {
+ return new HelpAndFeedback();
+ }
+
+ /**
+ * Returns a new instance of VariationsSession.
+ */
+ public VariationsSession createVariationsSession() {
+ return new VariationsSession();
+ }
+
+ /**
+ * Return a {@link AuthenticatorNavigationInterceptor} for the given {@link Tab}.
+ * This can be null if there are no applicable interceptor to be built.
+ */
+ @SuppressWarnings("unused")
+ public AuthenticatorNavigationInterceptor createAuthenticatorNavigationInterceptor(Tab tab) {
+ return null;
+ }
+
+ /**
+ * Starts the application activity tracker.
+ */
+ protected void startApplicationActivityTracker() {}
+
+ /**
+ * Stops the application activity tracker.
+ */
+ protected void stopApplicationActivityTracker() {}
+
+ // TODO(newt): delete this method after upstreaming. Callers can use
+ // MultiWindowUtils.getInstance() instead.
+ @Override
+ public boolean isMultiWindow(Activity activity) {
+ return MultiWindowUtils.getInstance().isMultiWindow(activity);
+ }
+
+ // TODO(newt): delete this after upstreaming.
+ @Override
+ public String getSettingsActivityName() {
+ return PreferencesStaging.class.getName();
+ }
+
+ // TODO(aurimas): delete this after upstreaming.
+ @Override
+ public String getFirstRunActivityName() {
+ return FirstRunActivityStaging.class.getName();
+ }
+
+ /**
+ * Honor the Android system setting about showing the last character of a password for a short
+ * period of time.
+ */
+ private void updatePasswordEchoState() {
+ boolean systemEnabled = Settings.System.getInt(
+ getApplicationContext().getContentResolver(),
+ Settings.System.TEXT_SHOW_PASSWORD, 1) == 1;
+ if (PrefServiceBridge.getInstance().getPasswordEchoEnabled() == systemEnabled) return;
+
+ PrefServiceBridge.getInstance().setPasswordEchoEnabled(systemEnabled);
+ }
+
+ @Override
+ protected PKCS11AuthenticationManager getPKCS11AuthenticationManager() {
+ return EmptyPKCS11AuthenticationManager.getInstance();
+ }
+
+ /**
+ * @return Instance of printing controller that is shared among all chromium activities. May
+ * return null if printing is not supported on the platform.
+ */
+ public PrintingController getPrintingController() {
+ return mPrintingController;
+ }
+
+ /**
+ * @return The UpdateInfoBarHelper used to inform the user about updates.
+ */
+ public UpdateInfoBarHelper getUpdateInfoBarHelper() {
+ return mUpdateInfoBarHelper;
+ }
+
+ /**
+ * @return An instance of {@link GSAHelper} that handles the start point of chrome's integration
+ * with GSA.
+ */
+ public GSAHelper createGsaHelper() {
+ return new GSAHelper();
+ }
+
+ @VisibleForTesting
+ public PolicyManager getPolicyManagerForTesting() {
+ return mPolicyManager;
+ }
+
+ /**
+ * Registers various policy providers with the policy manager.
+ * Providers are registered in increasing order of precedence so overrides should call this
+ * method in the end for this method to maintain the highest precedence.
+ * @param manager The {@link PolicyManager} to register the providers with.
+ */
+ protected void registerPolicyProviders(PolicyManager manager) {
+ manager.registerProvider(new AppRestrictionsProvider(getApplicationContext()));
+ }
+
+ public void addPolicyChangeListener(PolicyChangeListener listener) {
+ mPolicyManager.addPolicyChangeListener(listener);
+ }
+
+ public void removePolicyChangeListener(PolicyChangeListener listener) {
+ mPolicyManager.removePolicyChangeListener(listener);
+ }
+
+ /**
+ * @return An instance of PolicyAuditor that notifies the policy system of the user's activity.
+ * Only applicable when the user has a policy active, that is tracking the activity.
+ */
+ public PolicyAuditor getPolicyAuditor() {
+ // This class has a protected constructor to prevent accidental instantiation.
+ return new PolicyAuditor() {};
+ }
+
+ /**
+ * @return An instance of MultiWindowUtils to be installed as a singleton.
+ */
+ public MultiWindowUtils createMultiWindowUtils() {
+ return new MultiWindowUtils();
+ }
+
+ /**
+ * @return An instance of RequestGenerator to be used for Omaha XML creation. Will be null if
+ * a generator is unavailable.
+ */
+ public RequestGenerator createOmahaRequestGenerator() {
+ return null;
+ }
+
+ /**
+ * @return An instance of AppDetailsDelegate that can be queried about app information for the
+ * App Banner feature. Will be null if one is unavailable.
+ */
+ protected AppDetailsDelegate createAppDetailsDelegate() {
+ return null;
+ }
+
+ /**
+ * Returns the Singleton instance of the DocumentTabModelSelector.
+ * TODO(dfalcantara): Find a better place for this once we differentiate between activity and
+ * application-level TabModelSelectors.
+ * @return The DocumentTabModelSelector for the application.
+ */
+ @SuppressFBWarnings("LI_LAZY_INIT_STATIC")
+ public static DocumentTabModelSelector getDocumentTabModelSelector() {
+ ThreadUtils.assertOnUiThread();
+ if (sDocumentTabModelSelector == null) {
+ sDocumentTabModelSelector = new DocumentTabModelSelector(
+ new ActivityDelegate(DocumentActivity.class, IncognitoDocumentActivity.class),
+ new TabDelegateImpl());
+ }
+ return sDocumentTabModelSelector;
+ }
+
+ /**
+ * @return Whether or not the Singleton has been initialized.
+ */
+ @VisibleForTesting
+ public static boolean isDocumentTabModelSelectorInitializedForTests() {
+ return sDocumentTabModelSelector != null;
+ }
+
+ /**
+ * @return An instance of RevenueStats to be installed as a singleton.
+ */
+ public RevenueStats createRevenueStatsInstance() {
+ return new RevenueStats();
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698