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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.chrome.browser;
6
7 import android.app.Activity;
8 import android.content.Context;
9 import android.content.SharedPreferences;
10 import android.os.Handler;
11 import android.preference.PreferenceManager;
12 import android.provider.Settings;
13 import android.util.Log;
14 import android.view.View;
15
16 import com.google.ipc.invalidation.external.client.android.service.AndroidLogger ;
17
18 import org.chromium.base.ApiCompatibilityUtils;
19 import org.chromium.base.ApplicationState;
20 import org.chromium.base.ApplicationStatus;
21 import org.chromium.base.ApplicationStatus.ApplicationStateListener;
22 import org.chromium.base.PathUtils;
23 import org.chromium.base.ResourceExtractor;
24 import org.chromium.base.ThreadUtils;
25 import org.chromium.base.VisibleForTesting;
26 import org.chromium.base.annotations.SuppressFBWarnings;
27 import org.chromium.chrome.browser.accessibility.FontSizePrefs;
28 import org.chromium.chrome.browser.banners.AppBannerManager;
29 import org.chromium.chrome.browser.banners.AppDetailsDelegate;
30 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
31 import org.chromium.chrome.browser.document.DocumentActivity;
32 import org.chromium.chrome.browser.document.IncognitoDocumentActivity;
33 import org.chromium.chrome.browser.document.TabDelegateImpl;
34 import org.chromium.chrome.browser.download.DownloadManagerService;
35 import org.chromium.chrome.browser.firstrun.FirstRunActivityStaging;
36 import org.chromium.chrome.browser.gsa.GSAHelper;
37 import org.chromium.chrome.browser.help.HelpAndFeedback;
38 import org.chromium.chrome.browser.identity.UniqueIdentificationGeneratorFactory ;
39 import org.chromium.chrome.browser.identity.UuidBasedUniqueIdentificationGenerat or;
40 import org.chromium.chrome.browser.invalidation.UniqueIdInvalidationClientNameGe nerator;
41 import org.chromium.chrome.browser.metrics.UmaUtils;
42 import org.chromium.chrome.browser.metrics.VariationsSession;
43 import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
44 import org.chromium.chrome.browser.omaha.RequestGenerator;
45 import org.chromium.chrome.browser.omaha.UpdateInfoBarHelper;
46 import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomiza tions;
47 import org.chromium.chrome.browser.policy.PolicyAuditor;
48 import org.chromium.chrome.browser.policy.PolicyManager;
49 import org.chromium.chrome.browser.policy.PolicyManager.PolicyChangeListener;
50 import org.chromium.chrome.browser.policy.providers.AppRestrictionsProvider;
51 import org.chromium.chrome.browser.preferences.AccessibilityPreferences;
52 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
53 import org.chromium.chrome.browser.preferences.PreferencesStaging;
54 import org.chromium.chrome.browser.printing.PrintingControllerFactory;
55 import org.chromium.chrome.browser.rlz.RevenueStats;
56 import org.chromium.chrome.browser.services.GoogleServicesManager;
57 import org.chromium.chrome.browser.share.ShareHelper;
58 import org.chromium.chrome.browser.smartcard.EmptyPKCS11AuthenticationManager;
59 import org.chromium.chrome.browser.smartcard.PKCS11AuthenticationManager;
60 import org.chromium.chrome.browser.sync.SyncController;
61 import org.chromium.chrome.browser.tab.AuthenticatorNavigationInterceptor;
62 import org.chromium.chrome.browser.tabmodel.document.ActivityDelegate;
63 import org.chromium.chrome.browser.tabmodel.document.DocumentTabModelSelector;
64 import org.chromium.content.browser.ChildProcessLauncher;
65 import org.chromium.content.browser.ContentViewStatics;
66 import org.chromium.content.browser.DownloadController;
67 import org.chromium.printing.PrintingController;
68 import org.chromium.ui.UiUtils;
69
70 import java.util.Locale;
71
72 /**
73 * Per-process class that manages classes and functions shared by all of Chrome' s Activities.
74 */
75 public class ChromeMobileApplication extends ChromiumApplication {
76 private static final String PREF_LOCALE = "locale";
77 private static final float FLOAT_EPSILON = 0.001f;
78
79 private static DocumentTabModelSelector sDocumentTabModelSelector;
80
81 /**
82 * This class allows pausing scripts & network connections when we
83 * go to the background and resume when we are back in foreground again.
84 * TODO(pliard): Get rid of this class once JavaScript timers toggling is do ne directly on
85 * the native side by subscribing to the system monitor events.
86 */
87 private static class BackgroundProcessing {
88 private class SuspendRunnable implements Runnable {
89 @Override
90 public void run() {
91 mSuspendRunnable = null;
92 assert !mWebKitTimersAreSuspended;
93 mWebKitTimersAreSuspended = true;
94 ContentViewStatics.setWebKitSharedTimersSuspended(true);
95 }
96 }
97
98 private static final int SUSPEND_TIMERS_AFTER_MS = 5 * 60 * 1000;
99 private final Handler mHandler = new Handler();
100 private boolean mWebKitTimersAreSuspended = false;
101 private SuspendRunnable mSuspendRunnable;
102
103 private void onDestroy() {
104 if (mSuspendRunnable != null) {
105 mHandler.removeCallbacks(mSuspendRunnable);
106 mSuspendRunnable = null;
107 }
108 }
109
110 private void suspendTimers() {
111 if (mSuspendRunnable == null) {
112 mSuspendRunnable = new SuspendRunnable();
113 mHandler.postDelayed(mSuspendRunnable, SUSPEND_TIMERS_AFTER_MS);
114 }
115 }
116
117 private void startTimers() {
118 if (mSuspendRunnable != null) {
119 mHandler.removeCallbacks(mSuspendRunnable);
120 mSuspendRunnable = null;
121 } else if (mWebKitTimersAreSuspended) {
122 ContentViewStatics.setWebKitSharedTimersSuspended(false);
123 mWebKitTimersAreSuspended = false;
124 }
125 }
126 }
127
128 private static final String[] CHROME_MANDATORY_PAKS = {
129 "en-US.pak", "resources.pak", "chrome_100_percent.pak", "icudtl.dat",
130 "natives_blob.bin", "snapshot_blob.bin"
131 };
132 private static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "chrome";
133 private static final String DEV_TOOLS_SERVER_SOCKET_PREFIX = "chrome";
134 private static final String SESSIONS_UUID_PREF_KEY = "chromium.sync.sessions .id";
135
136 private final BackgroundProcessing mBackgroundProcessing = new BackgroundPro cessing();
137 private final PowerBroadcastReceiver mPowerBroadcastReceiver = new PowerBroa dcastReceiver();
138 private final UpdateInfoBarHelper mUpdateInfoBarHelper = new UpdateInfoBarHe lper();
139
140 // Used to trigger variation changes (such as seed fetches) upon application foregrounding.
141 private VariationsSession mVariationsSession;
142
143 private DevToolsServer mDevToolsServer;
144
145 private boolean mIsStarted;
146 private boolean mInitializedSharedClasses;
147 private boolean mIsProcessInitialized;
148
149 private ChromeLifetimeController mChromeLifetimeController;
150 private PrintingController mPrintingController;
151 private PolicyManager mPolicyManager;
152
153 /**
154 * This is called once per ChromeMobileApplication instance, which get creat ed per process
155 * (browser OR renderer). Don't stick anything in here that shouldn't be ca lled multiple times
156 * during Chrome's lifetime.
157 */
158 @Override
159 public void onCreate() {
160 UmaUtils.recordMainEntryPointTime();
161 super.onCreate();
162 UiUtils.setKeyboardShowingDelegate(new UiUtils.KeyboardShowingDelegate() {
163 @Override
164 public boolean disableKeyboardCheck(Context context, View view) {
165 Activity activity = null;
166 if (context instanceof Activity) {
167 activity = (Activity) context;
168 } else if (view != null && view.getContext() instanceof Activity ) {
169 activity = (Activity) view.getContext();
170 }
171
172 // For multiwindow mode we do not track keyboard visibility.
173 return activity != null && isMultiWindow(activity);
174 }
175 });
176
177 // Set the unique identification generator for invalidations. The
178 // invalidations system can start and attempt to fetch the client ID
179 // very early. We need this generator to be ready before that happens.
180 UniqueIdInvalidationClientNameGenerator.doInitializeAndInstallGenerator( this);
181
182 // Set minimum Tango log level. This sets an in-memory static field, and needs to be
183 // set in the ApplicationContext instead of an activity, since Tango can be woken up
184 // by the system directly though messages from GCM.
185 AndroidLogger.setMinimumAndroidLogLevel(Log.WARN);
186
187 // Set up the identification generator for sync. The ID is actually gene rated
188 // in the SyncController constructor.
189 UniqueIdentificationGeneratorFactory.registerGenerator(SyncController.GE NERATOR_ID,
190 new UuidBasedUniqueIdentificationGenerator(this, SESSIONS_UUID_P REF_KEY), false);
191 }
192
193 @Override
194 protected void initializeLibraryDependencies() {
195 // The ResourceExtractor is only needed by the browser process, but this will have no
196 // impact on the renderer process construction.
197 ResourceExtractor.setMandatoryPaksToExtract(CHROME_MANDATORY_PAKS);
198 PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX, t his);
199 }
200
201 @Override
202 protected void initializeGoogleServicesManager() {
203 GoogleServicesManager.get(getApplicationContext());
204 }
205
206 @Override
207 public void initCommandLine() {
208 ChromeCommandLineInitUtil.initChromeCommandLine(this);
209 }
210
211 /**
212 * The host activity should call this after the native library has loaded to ensure classes
213 * shared by Activities in the same process are properly initialized.
214 */
215 public void initializeSharedClasses() {
216 if (mInitializedSharedClasses) return;
217 mInitializedSharedClasses = true;
218
219 GoogleServicesManager.get(this).onMainActivityStart();
220 RevenueStats.getInstance();
221 ShortcutHelper.setFullScreenAction(ChromeLauncherActivity.ACTION_START_W EBAPP);
222
223 getPKCS11AuthenticationManager().initialize(ChromeMobileApplication.this );
224
225 mDevToolsServer = new DevToolsServer(DEV_TOOLS_SERVER_SOCKET_PREFIX);
226 mDevToolsServer.setRemoteDebuggingEnabled(
227 true, DevToolsServer.Security.ALLOW_DEBUG_PERMISSION);
228
229 startApplicationActivityTracker();
230
231 DownloadController.setDownloadNotificationService(
232 DownloadManagerService.getDownloadManagerService(this));
233
234 if (ApiCompatibilityUtils.isPrintingSupported()) {
235 mPrintingController = PrintingControllerFactory.create(getApplicatio nContext());
236 }
237 }
238
239 /**
240 * @return The Application's PowerBroadcastReceiver.
241 */
242 @VisibleForTesting
243 public PowerBroadcastReceiver getPowerBroadcastReceiver() {
244 return mPowerBroadcastReceiver;
245 }
246
247 /**
248 * Update the font size after changing the Android accessibility system sett ing. Doing so kills
249 * the Activities but it doesn't kill the ChromeMobileApplication, so this s hould be called in
250 * {@link #onStart} instead of {@link #initialize}.
251 */
252 private void updateFontSize() {
253 // This method is currently broken. http://crbug.com/439108
254 // Skip it (with the consequence of not updating the text scaling factor when the user
255 // changes system font size) rather than incurring the broken behavior.
256 // TODO(newt): fix this.
257 if (true) return;
258
259 FontSizePrefs fontSizePrefs = FontSizePrefs.getInstance(getApplicationCo ntext());
260
261 // Set font scale factor as the product of the system and browser scale settings.
262 float browserTextScale = PreferenceManager
263 .getDefaultSharedPreferences(this)
264 .getFloat(AccessibilityPreferences.PREF_TEXT_SCALE, 1.0f);
265 float fontScale = getResources().getConfiguration().fontScale * browserT extScale;
266
267 float scaleDelta = Math.abs(fontScale - fontSizePrefs.getFontScaleFactor ());
268 if (scaleDelta >= FLOAT_EPSILON) {
269 fontSizePrefs.setFontScaleFactor(fontScale);
270 }
271
272 // If force enable zoom has not been manually set, set it automatically based on
273 // font scale factor.
274 boolean shouldForceZoom =
275 fontScale >= AccessibilityPreferences.FORCE_ENABLE_ZOOM_THRESHOL D_MULTIPLIER;
276 if (!fontSizePrefs.getUserSetForceEnableZoom()
277 && fontSizePrefs.getForceEnableZoom() != shouldForceZoom) {
278 fontSizePrefs.setForceEnableZoom(shouldForceZoom);
279 }
280 }
281
282 /**
283 * Update the accept languages after changing Android locale setting. Doing so kills the
284 * Activities but it doesn't kill the ChromeMobileApplication, so this shoul d be called in
285 * {@link #onStart} instead of {@link #initialize}.
286 */
287 private void updateAcceptLanguages() {
288 PrefServiceBridge instance = PrefServiceBridge.getInstance();
289 String localeString = Locale.getDefault().toString(); // ex) en_US, de_ DE, zh_CN_#Hans
290 if (hasLocaleChanged(localeString)) {
291 instance.resetAcceptLanguages(localeString);
292 // Clear cache so that accept-languages change can be applied immedi ately.
293 // TODO(changwan): The underlying BrowsingDataRemover::Remove() is a n asynchronous call.
294 // So cache-clearing may not be effective if URL rendering can happe n before
295 // OnBrowsingDataRemoverDone() is called, in which case we may have to reload as well.
296 // Check if it can happen.
297 instance.clearBrowsingData(null, false, true /* cache */, false, fal se, false);
298 }
299 }
300
301 private boolean hasLocaleChanged(String newLocale) {
302 String previousLocale = PreferenceManager.getDefaultSharedPreferences(th is).getString(
303 PREF_LOCALE, "");
304
305 if (!previousLocale.equals(newLocale)) {
306 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferen ces(this);
307 SharedPreferences.Editor editor = prefs.edit();
308 editor.putString(PREF_LOCALE, newLocale);
309 editor.apply();
310 return true;
311 }
312 return false;
313 }
314
315 /**
316 * Should be called almost immediately after the native library has loaded t o initialize things
317 * that really, really have to be set up early. Avoid putting any long task s here.
318 */
319 @Override
320 public void initializeProcess() {
321 if (mIsProcessInitialized) return;
322 mIsProcessInitialized = true;
323 assert !mIsStarted;
324
325 super.initializeProcess();
326
327 mVariationsSession = createVariationsSession();
328 removeSessionCookies();
329 ApplicationStatus.registerApplicationStateListener(createApplicationStat eListener());
330 AppBannerManager.setAppDetailsDelegate(createAppDetailsDelegate());
331 mChromeLifetimeController = new ChromeLifetimeController(this);
332 mPolicyManager = new PolicyManager();
333 registerPolicyProviders(mPolicyManager);
334
335 PrefServiceBridge.getInstance().migratePreferences(this);
336 }
337
338 /**
339 * Each top-level activity (ChromeTabbedActivity, FullscreenActivity) should call this during
340 * its onStart phase. When called for the first time, this marks the beginni ng of a foreground
341 * session and calls onForegroundSessionStart(). Subsequent calls are noops until
342 * onForegroundSessionEnd() is called, to handle changing top-level Chrome a ctivities in one
343 * foreground session.
344 */
345 public void onStartWithNative() {
346 if (mIsStarted) return;
347 mIsStarted = true;
348
349 assert mIsProcessInitialized;
350
351 onForegroundSessionStart();
352 }
353
354 /**
355 * Called when a top-level Chrome activity (ChromeTabbedActivity, Fullscreen Activity) is
356 * started in foreground. It will not be called again when other Chrome acti vities take over
357 * (see onStart()), that is, when correct activity calls startActivity() for another Chrome
358 * activity.
359 */
360 private void onForegroundSessionStart() {
361 ChildProcessLauncher.onBroughtToForeground();
362 mBackgroundProcessing.startTimers();
363 updatePasswordEchoState();
364 updateFontSize();
365 updateAcceptLanguages();
366 changeAppStatus(true);
367 mVariationsSession.start(getApplicationContext());
368
369 mPowerBroadcastReceiver.registerReceiver(this);
370 mPowerBroadcastReceiver.runActions(this, true);
371 }
372
373 /**
374 * Called when last of Chrome activities is stopped, ending the foreground s ession. This will
375 * not be called when a Chrome activity is stopped because another Chrome ac tivity takes over.
376 * This is ensured by ActivityStatus, which switches to track new activity w hen its started and
377 * will not report the old one being stopped (see createStateListener() belo w).
378 */
379 private void onForegroundSessionEnd() {
380 if (!mIsStarted) return;
381 mBackgroundProcessing.suspendTimers();
382 flushPersistentData();
383 mIsStarted = false;
384 changeAppStatus(false);
385
386 try {
387 mPowerBroadcastReceiver.unregisterReceiver(this);
388 } catch (IllegalArgumentException e) {
389 // This may happen when onStop get called very early in UI test.
390 }
391
392 ChildProcessLauncher.onSentToBackground();
393 }
394
395 /**
396 * Called after onForegroundSessionEnd() indicating that the activity whose onStop() ended the
397 * last foreground session was destroyed.
398 */
399 private void onForegroundActivityDestroyed() {
400 if (ApplicationStatus.isEveryActivityDestroyed()) {
401 mBackgroundProcessing.onDestroy();
402 stopApplicationActivityTracker();
403 PartnerBrowserCustomizations.destroy();
404 ShareHelper.clearSharedScreenshots(this);
405 mPolicyManager.destroy();
406 mPolicyManager = null;
407 }
408 }
409
410 private ApplicationStateListener createApplicationStateListener() {
411 return new ApplicationStateListener() {
412 @Override
413 public void onApplicationStateChange(int newState) {
414 if (newState == ApplicationState.HAS_STOPPED_ACTIVITIES) {
415 onForegroundSessionEnd();
416 } else if (newState == ApplicationState.HAS_DESTROYED_ACTIVITIES ) {
417 onForegroundActivityDestroyed();
418 }
419 }
420 };
421 }
422
423 /**
424 * Returns a new instance of HelpAndFeedback.
425 */
426 public HelpAndFeedback createHelpAndFeedback() {
427 return new HelpAndFeedback();
428 }
429
430 /**
431 * Returns a new instance of VariationsSession.
432 */
433 public VariationsSession createVariationsSession() {
434 return new VariationsSession();
435 }
436
437 /**
438 * Return a {@link AuthenticatorNavigationInterceptor} for the given {@link Tab}.
439 * This can be null if there are no applicable interceptor to be built.
440 */
441 @SuppressWarnings("unused")
442 public AuthenticatorNavigationInterceptor createAuthenticatorNavigationInter ceptor(Tab tab) {
443 return null;
444 }
445
446 /**
447 * Starts the application activity tracker.
448 */
449 protected void startApplicationActivityTracker() {}
450
451 /**
452 * Stops the application activity tracker.
453 */
454 protected void stopApplicationActivityTracker() {}
455
456 // TODO(newt): delete this method after upstreaming. Callers can use
457 // MultiWindowUtils.getInstance() instead.
458 @Override
459 public boolean isMultiWindow(Activity activity) {
460 return MultiWindowUtils.getInstance().isMultiWindow(activity);
461 }
462
463 // TODO(newt): delete this after upstreaming.
464 @Override
465 public String getSettingsActivityName() {
466 return PreferencesStaging.class.getName();
467 }
468
469 // TODO(aurimas): delete this after upstreaming.
470 @Override
471 public String getFirstRunActivityName() {
472 return FirstRunActivityStaging.class.getName();
473 }
474
475 /**
476 * Honor the Android system setting about showing the last character of a pa ssword for a short
477 * period of time.
478 */
479 private void updatePasswordEchoState() {
480 boolean systemEnabled = Settings.System.getInt(
481 getApplicationContext().getContentResolver(),
482 Settings.System.TEXT_SHOW_PASSWORD, 1) == 1;
483 if (PrefServiceBridge.getInstance().getPasswordEchoEnabled() == systemEn abled) return;
484
485 PrefServiceBridge.getInstance().setPasswordEchoEnabled(systemEnabled);
486 }
487
488 @Override
489 protected PKCS11AuthenticationManager getPKCS11AuthenticationManager() {
490 return EmptyPKCS11AuthenticationManager.getInstance();
491 }
492
493 /**
494 * @return Instance of printing controller that is shared among all chromium activities. May
495 * return null if printing is not supported on the platform.
496 */
497 public PrintingController getPrintingController() {
498 return mPrintingController;
499 }
500
501 /**
502 * @return The UpdateInfoBarHelper used to inform the user about updates.
503 */
504 public UpdateInfoBarHelper getUpdateInfoBarHelper() {
505 return mUpdateInfoBarHelper;
506 }
507
508 /**
509 * @return An instance of {@link GSAHelper} that handles the start point of chrome's integration
510 * with GSA.
511 */
512 public GSAHelper createGsaHelper() {
513 return new GSAHelper();
514 }
515
516 @VisibleForTesting
517 public PolicyManager getPolicyManagerForTesting() {
518 return mPolicyManager;
519 }
520
521 /**
522 * Registers various policy providers with the policy manager.
523 * Providers are registered in increasing order of precedence so overrides s hould call this
524 * method in the end for this method to maintain the highest precedence.
525 * @param manager The {@link PolicyManager} to register the providers with.
526 */
527 protected void registerPolicyProviders(PolicyManager manager) {
528 manager.registerProvider(new AppRestrictionsProvider(getApplicationConte xt()));
529 }
530
531 public void addPolicyChangeListener(PolicyChangeListener listener) {
532 mPolicyManager.addPolicyChangeListener(listener);
533 }
534
535 public void removePolicyChangeListener(PolicyChangeListener listener) {
536 mPolicyManager.removePolicyChangeListener(listener);
537 }
538
539 /**
540 * @return An instance of PolicyAuditor that notifies the policy system of t he user's activity.
541 * Only applicable when the user has a policy active, that is tracking the a ctivity.
542 */
543 public PolicyAuditor getPolicyAuditor() {
544 // This class has a protected constructor to prevent accidental instanti ation.
545 return new PolicyAuditor() {};
546 }
547
548 /**
549 * @return An instance of MultiWindowUtils to be installed as a singleton.
550 */
551 public MultiWindowUtils createMultiWindowUtils() {
552 return new MultiWindowUtils();
553 }
554
555 /**
556 * @return An instance of RequestGenerator to be used for Omaha XML creation . Will be null if
557 * a generator is unavailable.
558 */
559 public RequestGenerator createOmahaRequestGenerator() {
560 return null;
561 }
562
563 /**
564 * @return An instance of AppDetailsDelegate that can be queried about app i nformation for the
565 * App Banner feature. Will be null if one is unavailable.
566 */
567 protected AppDetailsDelegate createAppDetailsDelegate() {
568 return null;
569 }
570
571 /**
572 * Returns the Singleton instance of the DocumentTabModelSelector.
573 * TODO(dfalcantara): Find a better place for this once we differentiate bet ween activity and
574 * application-level TabModelSelectors.
575 * @return The DocumentTabModelSelector for the application.
576 */
577 @SuppressFBWarnings("LI_LAZY_INIT_STATIC")
578 public static DocumentTabModelSelector getDocumentTabModelSelector() {
579 ThreadUtils.assertOnUiThread();
580 if (sDocumentTabModelSelector == null) {
581 sDocumentTabModelSelector = new DocumentTabModelSelector(
582 new ActivityDelegate(DocumentActivity.class, IncognitoDocume ntActivity.class),
583 new TabDelegateImpl());
584 }
585 return sDocumentTabModelSelector;
586 }
587
588 /**
589 * @return Whether or not the Singleton has been initialized.
590 */
591 @VisibleForTesting
592 public static boolean isDocumentTabModelSelectorInitializedForTests() {
593 return sDocumentTabModelSelector != null;
594 }
595
596 /**
597 * @return An instance of RevenueStats to be installed as a singleton.
598 */
599 public RevenueStats createRevenueStatsInstance() {
600 return new RevenueStats();
601 }
602 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698