From eb14532c3cea6874f3ddd087964b426e830b9704 Mon Sep 17 00:00:00 2001 From: Grisha Kruglov Date: Tue, 17 Mar 2020 23:30:49 -0700 Subject: [PATCH] Closes #7450: Lazy storage initialization Make sure that we actually lazily initialize our storage layers. With this patch applied, storage layers (history, logins, bookmarks) will be initialized when first accessed. We will no longer block GeckoEngine init, for example, on waiting for the logins storage to initialize (which needs to access the costly securePrefStorage). Similarly, BackgroundServices init will no longer require initialized instances of the storage components - references to their "lazy wrappers" will suffice. In practice, this change changes when our storage layers are initialized in the following ways. Currently, we will initialize everything on startup. This includes loading our megazord, as well. With this change, init path depends on if the user is signed-into FxA or not. If user is not an FxA user: - on startup, none of the storage layers are initialized - history storage will be initialized once, whenever: - first non-customTab page is loaded (access to the HistoryDelegate) - first interaction with the awesomebar - history UI is accessed - bookmarks storage will be initialized once, whenever: - something is bookmarked, or we need to figure out if something's bookmarked - bookmarks UI is accessed - logins storage will be initialized once, whenever: - first page is loaded with a login/password fields that can be autofilled - (or some other interaction by GV with the autofill/loginStorage delegates) - logins UI is accessed - all of these storages will be initialized if the user logs into FxA and starts syncing data - except, if a storage is not chosen to be synced, it will not be initialized If user is an FxA user: - on startup, none of the storage layers are initialized - sometime shortly after startup is complete, when a sync worker runs in the background, all storage layers that are enabled to sync will be initialized. This change also means that we delay loading the megazord until first access (as described above). --- .../org/mozilla/fenix/engine/GeckoProvider.kt | 4 +- .../org/mozilla/fenix/engine/GeckoProvider.kt | 4 +- .../fenix/browser/BaseBrowserFragment.kt | 2 +- .../fenix/components/BackgroundServices.kt | 6 +-- .../mozilla/fenix/components/Components.kt | 6 +-- .../java/org/mozilla/fenix/components/Core.kt | 38 +++++++++++++------ buildSrc/src/main/java/AndroidComponents.kt | 2 +- 7 files changed, 39 insertions(+), 23 deletions(-) diff --git a/app/src/geckoBeta/java/org/mozilla/fenix/engine/GeckoProvider.kt b/app/src/geckoBeta/java/org/mozilla/fenix/engine/GeckoProvider.kt index 224a79150..1e6ab61a5 100644 --- a/app/src/geckoBeta/java/org/mozilla/fenix/engine/GeckoProvider.kt +++ b/app/src/geckoBeta/java/org/mozilla/fenix/engine/GeckoProvider.kt @@ -23,7 +23,7 @@ object GeckoProvider { @Synchronized fun getOrCreateRuntime( context: Context, - storage: LoginsStorage + storage: Lazy ): GeckoRuntime { if (runtime == null) { runtime = createRuntime(context, storage) @@ -34,7 +34,7 @@ object GeckoProvider { private fun createRuntime( context: Context, - storage: LoginsStorage + storage: Lazy ): GeckoRuntime { val builder = GeckoRuntimeSettings.Builder() diff --git a/app/src/geckoNightly/java/org/mozilla/fenix/engine/GeckoProvider.kt b/app/src/geckoNightly/java/org/mozilla/fenix/engine/GeckoProvider.kt index 837a96e32..b72198260 100644 --- a/app/src/geckoNightly/java/org/mozilla/fenix/engine/GeckoProvider.kt +++ b/app/src/geckoNightly/java/org/mozilla/fenix/engine/GeckoProvider.kt @@ -22,7 +22,7 @@ object GeckoProvider { @Synchronized fun getOrCreateRuntime( context: Context, - storage: LoginsStorage + storage: Lazy ): GeckoRuntime { if (runtime == null) { runtime = createRuntime(context, storage) @@ -33,7 +33,7 @@ object GeckoProvider { private fun createRuntime( context: Context, - storage: LoginsStorage + storage: Lazy ): GeckoRuntime { val builder = GeckoRuntimeSettings.Builder() diff --git a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt index 46416e907..ee56f6327 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt @@ -331,7 +331,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session customTabId = customTabSessionId, fragmentManager = parentFragmentManager, loginValidationDelegate = DefaultLoginValidationDelegate( - context.components.core.passwordsStorage + context.components.core.lazyPasswordsStorage ), isSaveLoginEnabled = { context.settings().shouldPromptToSaveLogins diff --git a/app/src/main/java/org/mozilla/fenix/components/BackgroundServices.kt b/app/src/main/java/org/mozilla/fenix/components/BackgroundServices.kt index cfcae0f3d..7700788e5 100644 --- a/app/src/main/java/org/mozilla/fenix/components/BackgroundServices.kt +++ b/app/src/main/java/org/mozilla/fenix/components/BackgroundServices.kt @@ -46,9 +46,9 @@ class BackgroundServices( private val context: Context, private val push: Push, crashReporter: CrashReporter, - historyStorage: PlacesHistoryStorage, - bookmarkStorage: PlacesBookmarksStorage, - passwordsStorage: SyncableLoginsStorage + historyStorage: Lazy, + bookmarkStorage: Lazy, + passwordsStorage: Lazy ) { fun defaultDeviceName(context: Context): String = context.getString( diff --git a/app/src/main/java/org/mozilla/fenix/components/Components.kt b/app/src/main/java/org/mozilla/fenix/components/Components.kt index 29d84cac6..091405ed3 100644 --- a/app/src/main/java/org/mozilla/fenix/components/Components.kt +++ b/app/src/main/java/org/mozilla/fenix/components/Components.kt @@ -33,9 +33,9 @@ class Components(private val context: Context) { context, push, analytics.crashReporter, - core.historyStorage, - core.bookmarksStorage, - core.passwordsStorage + core.lazyHistoryStorage, + core.lazyBookmarksStorage, + core.lazyPasswordsStorage ) } val services by lazy { Services(context, backgroundServices.accountManager) } diff --git a/app/src/main/java/org/mozilla/fenix/components/Core.kt b/app/src/main/java/org/mozilla/fenix/components/Core.kt index 8a6d8fb67..49ab1d226 100644 --- a/app/src/main/java/org/mozilla/fenix/components/Core.kt +++ b/app/src/main/java/org/mozilla/fenix/components/Core.kt @@ -38,6 +38,7 @@ import mozilla.components.feature.webnotifications.WebNotificationFeature import mozilla.components.lib.dataprotect.SecureAbove22Preferences import mozilla.components.lib.dataprotect.generateEncryptionKey import mozilla.components.service.sync.logins.SyncableLoginsStorage +import mozilla.components.support.base.log.logger.Logger import org.mozilla.fenix.AppRequestInterceptor import org.mozilla.fenix.Config import org.mozilla.fenix.FeatureFlags @@ -52,6 +53,8 @@ import java.util.concurrent.TimeUnit */ @Mockable class Core(private val context: Context) { + private val logger = Logger("Core") + /** * The browser engine component initialized based on the build * configuration (see build variants). @@ -62,7 +65,7 @@ class Core(private val context: Context) { remoteDebuggingEnabled = context.settings().isRemoteDebuggingEnabled, testingModeEnabled = false, trackingProtectionPolicy = trackingProtectionPolicyFactory.createTrackingProtectionPolicy(), - historyTrackingDelegate = HistoryDelegate(historyStorage), + historyTrackingDelegate = HistoryDelegate(lazyHistoryStorage), preferredColorScheme = getPreferredColorScheme(), automaticFontSizeAdjustment = context.settings().shouldUseAutoSize, fontInflationEnabled = context.settings().shouldUseAutoSize, @@ -73,7 +76,7 @@ class Core(private val context: Context) { GeckoEngine( context, defaultSettings, - GeckoProvider.getOrCreateRuntime(context, passwordsStorage) + GeckoProvider.getOrCreateRuntime(context, lazyPasswordsStorage) ).also { WebCompatFeature.install(it) } @@ -85,7 +88,7 @@ class Core(private val context: Context) { val client: Client by lazy { GeckoViewFetchClient( context, - GeckoProvider.getOrCreateRuntime(context, passwordsStorage) + GeckoProvider.getOrCreateRuntime(context, lazyPasswordsStorage) ) } @@ -184,15 +187,28 @@ class Core(private val context: Context) { ) } - /** - * The storage component to persist browsing history (with the exception of - * private sessions). - */ - val historyStorage by lazy { PlacesHistoryStorage(context) } + // Lazy wrappers around storage components are used to pass references to these components without + // initializing them until they're accessed. + // Use these for startup-path code, where we don't want to do any work that's not strictly necessary. + // For example, this is how the GeckoEngine delegates (history, logins) are configured. + // We can fully initialize GeckoEngine without initialized our storage. + val lazyHistoryStorage = lazy { + logger.info("Initializing history storage") + PlacesHistoryStorage(context) + } + val lazyBookmarksStorage = lazy { + logger.info("Initializing bookmarks storage") + PlacesBookmarksStorage(context) + } + val lazyPasswordsStorage = lazy { + logger.info("Initializing logins storage") + SyncableLoginsStorage(context, passwordsEncryptionKey) + } - val bookmarksStorage by lazy { PlacesBookmarksStorage(context) } - - val passwordsStorage by lazy { SyncableLoginsStorage(context, passwordsEncryptionKey) } + // For most other application code (non-startup), these wrappers are perfectly fine and more ergonomic. + val historyStorage by lazy { lazyHistoryStorage.value } + val bookmarksStorage by lazy { lazyBookmarksStorage.value } + val passwordsStorage by lazy { lazyPasswordsStorage.value } val tabCollectionStorage by lazy { TabCollectionStorage(context, sessionManager) } diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 8f3dddc3d..7dbd00887 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "37.0.20200318190037" + const val VERSION = "37.0.20200319130119" }