diff --git a/app/build.gradle b/app/build.gradle index c48a9fed0f..b0af372af4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -504,7 +504,6 @@ dependencies { implementation Deps.mozilla_feature_webcompat implementation Deps.mozilla_feature_webnotifications implementation Deps.mozilla_feature_webcompat_reporter - implementation Deps.mozilla_feature_serviceworker implementation Deps.mozilla_service_pocket implementation Deps.mozilla_service_contile diff --git a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt index 1def45caf7..0ef5975320 100644 --- a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt +++ b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt @@ -36,7 +36,6 @@ import mozilla.components.feature.addons.update.GlobalAddonDependencyProvider import mozilla.components.feature.autofill.AutofillUseCases import mozilla.components.feature.search.ext.buildSearchUrl import mozilla.components.feature.search.ext.waitForSelectedOrDefaultSearchEngine -import mozilla.components.feature.serviceworker.ServiceWorkerSupport import mozilla.components.feature.top.sites.TopSitesProviderConfig import mozilla.components.lib.crash.CrashReporter import mozilla.components.service.fxa.manager.SyncEnginesStorage @@ -196,10 +195,6 @@ open class FenixApplication : LocaleAwareApplication(), Provider { setupLeakCanary() startMetricsIfEnabled() - ServiceWorkerSupport.install( - components.core.engine, - components.useCases.tabsUseCases.addTab - ) setupPush() visibilityLifecycleCallback = VisibilityLifecycleCallback(getSystemService()) diff --git a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt index 678a11b591..98d33dcd19 100644 --- a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt @@ -160,6 +160,10 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { WebExtensionPopupFeature(components.core.store, ::openPopup) } + private val serviceWorkerSupport by lazy { + ServiceWorkerSupportFeature(this) + } + private var inflater: LayoutInflater? = null private val navHost by lazy { @@ -264,7 +268,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { } supportActionBar?.hide() - lifecycle.addObservers(webExtensionPopupFeature) + lifecycle.addObservers(webExtensionPopupFeature, serviceWorkerSupport) if (shouldAddToRecentsScreen(intent)) { intent.removeExtra(START_IN_RECENTS_SCREEN) diff --git a/app/src/main/java/org/mozilla/fenix/ServiceWorkerSupportFeature.kt b/app/src/main/java/org/mozilla/fenix/ServiceWorkerSupportFeature.kt new file mode 100644 index 0000000000..7334c9faf9 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/ServiceWorkerSupportFeature.kt @@ -0,0 +1,47 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix + +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner +import mozilla.components.browser.state.state.SessionState +import mozilla.components.concept.engine.EngineSession +import mozilla.components.concept.engine.EngineSession.LoadUrlFlags +import mozilla.components.concept.engine.serviceworker.ServiceWorkerDelegate +import org.mozilla.fenix.ext.components + +/** + * Fenix own version of the `ServiceWorkerSupportFeature` from Android-Components + * which adds the ability to navigate to the browser before opening a new tab. + * + * Will automatically register callbacks for service workers requests and cleanup when [homeActivity] is destroyed. + * + * @param homeActivity [HomeActivity] used for navigating to browser or accessing various app components. + */ +class ServiceWorkerSupportFeature( + private val homeActivity: HomeActivity +) : ServiceWorkerDelegate, DefaultLifecycleObserver { + override fun onDestroy(owner: LifecycleOwner) { + homeActivity.components.core.engine.unregisterServiceWorkerDelegate() + } + + override fun onCreate(owner: LifecycleOwner) { + homeActivity.components.core.engine.registerServiceWorkerDelegate(this) + } + + override fun addNewTab(engineSession: EngineSession): Boolean { + with(homeActivity) { + openToBrowser(BrowserDirection.FromHome) + + components.useCases.tabsUseCases.addTab( + flags = LoadUrlFlags.external(), + engineSession = engineSession, + source = SessionState.Source.Internal.None + ) + } + + return true + } +} diff --git a/app/src/test/java/org/mozilla/fenix/ServiceWorkerSupportTest.kt b/app/src/test/java/org/mozilla/fenix/ServiceWorkerSupportTest.kt new file mode 100644 index 0000000000..a3352d3953 --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/ServiceWorkerSupportTest.kt @@ -0,0 +1,103 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix + +import android.content.Context +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.unmockkStatic +import io.mockk.verify +import io.mockk.verifyOrder +import mozilla.components.browser.engine.gecko.GeckoEngine +import mozilla.components.browser.state.state.SessionState.Source.Internal.None +import mozilla.components.concept.engine.EngineSession.LoadUrlFlags +import mozilla.components.feature.tabs.TabsUseCases.AddNewTabUseCase +import mozilla.components.support.test.eq +import org.junit.After +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mozilla.fenix.BrowserDirection.FromHome +import org.mozilla.fenix.ext.components + +@RunWith(AndroidJUnit4::class) +class ServiceWorkerSupportTest { + @Before + fun setup() { + // Needed to mock the response of the "Context.components" extension property. + mockkStatic("org.mozilla.fenix.ext.ContextKt") + } + + @After + fun teardown() { + unmockkStatic("org.mozilla.fenix.ext.ContextKt") + } + + @Test + fun `GIVEN the feature is registered for lifecycle events WHEN the owner is created THEN register itself as a service worker delegate`() { + val engine: GeckoEngine = mockk(relaxed = true) + every { any().components.core.engine } returns engine + val feature = ServiceWorkerSupportFeature(mockk(relaxed = true)) + + feature.onCreate(mockk()) + + verify { engine.registerServiceWorkerDelegate(feature) } + } + + @Test + fun `GIVEN the feature is registered for lifecycle events WHEN the owner is destroyed THEN unregister itself as a service worker delegate`() { + val engine: GeckoEngine = mockk(relaxed = true) + every { any().components.core.engine } returns engine + val feature = ServiceWorkerSupportFeature(mockk(relaxed = true)) + + feature.onDestroy(mockk()) + + verify { engine.unregisterServiceWorkerDelegate() } + } + + @Test + fun `WHEN a new tab is requested THEN navigate to browser then add a new tab`() { + val addNewTabUseCase: AddNewTabUseCase = mockk(relaxed = true) + every { any().components.useCases.tabsUseCases.addTab } returns addNewTabUseCase + val activity: HomeActivity = mockk(relaxed = true) + val feature = ServiceWorkerSupportFeature(activity) + + feature.addNewTab(mockk()) + + verifyOrder { + activity.openToBrowser(FromHome) + + addNewTabUseCase( + url = eq("about:blank"), + selectTab = eq(true), // default + startLoading = eq(true), // default + parentId = eq(null), // default + flags = eq(LoadUrlFlags.external()), + contextId = eq(null), // default + engineSession = any(), + source = eq(None), + searchTerms = eq(""), // default + private = eq(false), // default + historyMetadata = eq(null) // default + ) + } + } + + @Test + fun `WHEN a new tab is requested THEN return true`() { + val addNewTabUseCase: AddNewTabUseCase = mockk(relaxed = true) + every { any().components.useCases.tabsUseCases.addTab } returns addNewTabUseCase + + val activity: HomeActivity = mockk(relaxed = true) + val feature = ServiceWorkerSupportFeature(activity) + + val result = feature.addNewTab(mockk()) + + assertTrue(result) + } +} diff --git a/buildSrc/src/main/java/Dependencies.kt b/buildSrc/src/main/java/Dependencies.kt index 03acdabe6f..af5ea13a32 100644 --- a/buildSrc/src/main/java/Dependencies.kt +++ b/buildSrc/src/main/java/Dependencies.kt @@ -135,7 +135,6 @@ object Deps { const val mozilla_feature_webcompat = "org.mozilla.components:feature-webcompat:${Versions.mozilla_android_components}" const val mozilla_feature_webnotifications = "org.mozilla.components:feature-webnotifications:${Versions.mozilla_android_components}" const val mozilla_feature_webcompat_reporter = "org.mozilla.components:feature-webcompat-reporter:${Versions.mozilla_android_components}" - const val mozilla_feature_serviceworker = "org.mozilla.components:feature-serviceworker:${Versions.mozilla_android_components}" const val mozilla_service_pocket = "org.mozilla.components:service-pocket:${Versions.mozilla_android_components}" const val mozilla_service_contile =