CVE-2024-8388
Overview
Background
- Fullscreen notification
- The transient on-screen message that Firefox for Android shows when a web page enters fullscreen mode, warning the user that the browser chrome has been hidden.
- `Toast`
- An Android system widget that displays a short, non-interactive message in a floating overlay that the OS controls and that other windows, prompts, and panels can render on top of.
- `DialogFragment`
- An Android component that hosts a
Dialogwithin the app’s own window and lifecycle, allowing the app to control its z-order, styling, and dismissal. - UI spoofing
- An attack where a page manipulates timing or overlays so the user misreads the true security state of the browser, e.g. believing they are not in fullscreen when they are.
Root Cause Analysis
The fix for CVE-2023-6870 in Firefox 121 announced the transition to fullscreen using FullScreenNotificationToast, which relied on the Android Toast mechanism. Because a Toast is drawn by the OS as a low-priority, non-app-controlled overlay, other prompts and panels originating from both Firefox and the Android OS could appear on top of it, obscuring the notification entirely. This violated the security invariant that the fullscreen-transition announcement must remain visible to the user precisely at the moment the browser chrome disappears, so that a sudden prompt could distract the user while the visual transition happened unseen behind it.
The fix removes FullScreenNotificationToast (and the GestureNavUtils helper it used) and replaces it with FullScreenNotificationDialog, a DialogFragment styled to look like a Snackbar that the app itself owns and z-orders. Because the app controls the dialog’s window, sets Gravity.BOTTOM, and marks it FLAG_NOT_FOCUSABLE while auto-dismissing after SNACKBAR_DURATION_LONG_MS, the notification is no longer trivially obscured by unrelated overlays, closing the spoofing vector.
Toast whose z-order the app cannot control, letting other windows hide it; the fix moves the announcement into an app-owned FullScreenNotificationDialog (DialogFragment) whose stacking and lifecycle Firefox controls directly.Attack Path
- Trigger fullscreen
A malicious page calls the Fullscreen API, causing Firefox for Android to hide its browser chrome and post the
FullScreenNotificationToast. - Race a prompt
The page (or a coincident OS/Firefox panel) simultaneously surfaces a prompt or panel that the OS renders above the low-priority
Toast. - Obscure the notice
The prompt covers the
Toastso the user never sees the “you are now in fullscreen” announcement. - Distract the user The sudden prompt draws the user’s attention away from the visual transition happening behind it, so they do not register that the chrome is gone.
- Spoof the UI With the true fullscreen state unannounced, the page draws fake browser UI or address bar content and the user trusts it as genuine.
Impact Assessment
Toast and divert attention. No memory corruption is involved; the exploit is purely a logic and UI-timing weakness.Changed Functions
| Function | Change | Notes |
|---|---|---|
FullScreenNotificationToastmobile/android/android-components/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/FullScreenNotification.kt |
modified | |
FullScreenNotificationDialogmobile/android/android-components/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/FullScreenNotificationDialog.kt |
modified | |
ifmobile/android/android-components/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/GestureNavUtils.kt |
modified | |
ifmobile/android/fenix/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt |
modified | |
ifmobile/android/focus-android/app/src/main/java/org/mozilla/focus/browser/integration/FullScreenIntegration.kt |
modified | |
FullScreenNotificationTestmobile/android/android-components/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/FullScreenNotificationTest.kt |
modified |
Files Changed
mobile/android/android-components/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/FullScreenNotification.ktmobile/android/android-components/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/FullScreenNotificationDialog.ktmobile/android/android-components/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/GestureNavUtils.ktmobile/android/android-components/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/FullScreenNotificationTest.ktmobile/android/android-components/docs/changelog.mdmobile/android/fenix/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.ktmobile/android/fenix/app/src/main/res/values/strings.xmlmobile/android/focus-android/app/src/main/java/org/mozilla/focus/browser/integration/FullScreenIntegration.ktmobile/android/focus-android/app/src/main/java/org/mozilla/focus/fragment/BrowserFragment.ktmobile/android/focus-android/app/src/main/res/values/strings.xmlmobile/android/focus-android/app/src/test/java/org/mozilla/focus/browser/integration/FullScreenIntegrationTest.kt
Audit Directions
- OS-owned overlays for security UIAudit any use of
Toastor other OS-controlled, non-z-ordered widgets to convey security-critical state, since the app cannot guarantee they stay visible above other windows. - Transition-timing racesLook for security announcements that fire alongside prompts or panels, where a coincident overlay can obscure the notice or divert attention during a state change.
- Focus and z-order guaranteesReview notification surfaces that omit flags like
FLAG_NOT_FOCUSABLEor explicit gravity/stacking control, as these can be overlaid, focus-stolen, or clickjacked.
Patch
diff --git a/mobile/android/android-components/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/FullScreenNotification.kt b/mobile/android/android-components/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/FullScreenNotification.kt
deleted file mode 100644
index ed20bf5234b..00000000000
--- a/mobile/android/android-components/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/FullScreenNotification.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-/* 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 mozilla.components.feature.prompts.dialog
-
-import android.app.Activity
-import android.widget.Toast
-
-/**
- * UI to show a 'full screen mode' notification.
- */
-interface FullScreenNotification {
- /**
- * Show the notification.
- */
- fun show()
-}
-
-/**
- * A [Toast] to show a full screen notification message
- * @property activity The activity to show the toast on.
- * @property gestureNavString The string to show when in gesture navigation mode.
- * @property backButtonString The string to show when in 3-button navigation mode.
- * @property gestureNavUtils Utility to detect gesture navigation.
- */
-class FullScreenNotificationToast(
- private val activity: Activity,
- private val gestureNavString: String,
- private val backButtonString: String,
- private val gestureNavUtils: GestureNavUtils,
-) : FullScreenNotification {
- override fun show() {
- val toastString =
- if (gestureNavUtils.isInGestureNavigationMode(activity.window)) {
- gestureNavString
- } else {
- backButtonString
- }
- Toast.makeText(activity, toastString, Toast.LENGTH_LONG).show()
- }
-}
diff --git a/mobile/android/android-components/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/FullScreenNotificationDialog.kt b/mobile/android/android-components/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/FullScreenNotificationDialog.kt
new file mode 100644
index 00000000000..3c403f3734e
--- /dev/null
+++ b/mobile/android/android-components/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/FullScreenNotificationDialog.kt
@@ -0,0 +1,70 @@
+/* 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 mozilla.components.feature.prompts.dialog
+
+import android.app.Dialog
+import android.os.Bundle
+import android.view.Gravity
+import android.view.WindowManager
+import androidx.annotation.LayoutRes
+import androidx.appcompat.app.AlertDialog
+import androidx.fragment.app.DialogFragment
+import androidx.fragment.app.FragmentManager
+import androidx.lifecycle.lifecycleScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+
+private const val TAG = "mozac_feature_prompts_full_screen_notification_dialog"
+private const val SNACKBAR_DURATION_LONG_MS = 3000L
+
+/**
+ * UI to show a 'full screen mode' notification.
+ */
+interface FullScreenNotification {
+ /**
+ * Show the notification.
+ *
+ * @param fragmentManager the [FragmentManager] to add this notification to.
+ */
+ fun show(fragmentManager: FragmentManager)
+}
+
+/**
+ * [DialogFragment] that is configured to match the style and behaviour of a Snackbar.
+ *
+ * @property layout the layout to use for the dialog.
+ */
+class FullScreenNotificationDialog(@LayoutRes val layout: Int) :
+ DialogFragment(), FullScreenNotification {
+ override fun show(fragmentManager: FragmentManager) = super.show(fragmentManager, TAG)
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = requireActivity().let {
+ val view = layoutInflater.inflate(layout, null)
+ AlertDialog.Builder(it).setView(view).create()
+ }
+
+ override fun onStart() {
+ super.onStart()
+
+ dialog?.let { dialog ->
+ dialog.window?.let { window ->
+ // Prevent any user input from key or other button events to it.
+ window.setFlags(
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ )
+
+ window.setGravity(Gravity.BOTTOM)
+ window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+ }
+ }
+
+ // Attempt to automatically dismiss the dialog after the given duration.
+ lifecycleScope.launch {
+ delay(SNACKBAR_DURATION_LONG_MS)
+ dialog?.dismiss()
+ }
+ }
+}
diff --git a/mobile/android/android-components/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/GestureNavUtils.kt b/mobile/android/android-components/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/GestureNavUtils.kt
deleted file mode 100644
index 203e0c45dbd..00000000000
--- a/mobile/android/android-components/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/GestureNavUtils.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-/* 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 mozilla.components.feature.prompts.dialog
-
-import android.os.Build
-import android.view.Window
-import androidx.core.view.WindowInsetsCompat
-
-/**
- * A place to keep utility functions for gesture navigation.
- */
-object GestureNavUtils {
- /**
- * Find out if the device supports gesture navigation and has gestures enabled.
- *
- * Gesture nav support was added in Android Q, but on recent devices it is possible to
- * switch navigation mode between gesture nav and 3-button navigation.
- * The system gesture inset needs to be greater than 0 at the left edge of the screen
- * for the back gesture to be picked up, so this seems to be the most reliable way of
- * detecting gesture navigation use.
- *
- * * See also: [Gesture Navigation: handling visual overlaps (II)](https://medium.com/androiddevelopers/gesture-navigation-handling-visual-overlaps-4aed565c134c)
- * * See also: [How to detect full screen gesture mode](https://stackoverflow.com/a/70514883)
- *
- * @param window The containing [Window] of the current `Activity`.
- * @return true if the device supports gesture navigation and gestures are enabled.
- */
- fun isInGestureNavigationMode(window: Window): Boolean =
- (
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- WindowInsetsCompat
- .toWindowInsetsCompat(
- window.decorView.rootWindowInsets,
- ).getInsets(WindowInsetsCompat.Type.systemGestures())
- .left
- } else {
- 0
- }
- ) > 0
-}
diff --git a/mobile/android/android-components/docs/changelog.md b/mobile/android/android-components/docs/changelog.md
index 7479607ff49..32f10d28b1f 100644
--- a/mobile/android/android-components/docs/changelog.md
+++ b/mobile/android/android-components/docs/changelog.md
@@ -5,8 +5,6 @@ permalink: /changelog/
---
# 130.0 (In Development)
-* **feature-prompts**:
- * ⚠️ **Breaking change**: `FullScreenNotification` interface is now implemented using a `FullScreenNotificationToast`. `FullScreenNotificationDialog` has been removed, see [Bug 1902996](https://bugzilla.mozilla.org/show_bug.cgi?id=1902996).
* **browser-engine-gecko**
* Added `fingerprintingProtection` and `fingerprintingProtectionPrivateBrowsing` to settings. This allows user to toggle `privacy.fingerprintingProtection` and `privacy.fingerprintingProtection.pbmode` pref. [bug #1878911](https://bugzilla.mozilla.org/show_bug.cgi?id=1878911)
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt
index f0ee95023bc..5c0e66800db 100644
--- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt
+++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt
@@ -85,8 +85,7 @@ import mozilla.components.feature.prompts.PromptFeature
import mozilla.components.feature.prompts.PromptFeature.Companion.PIN_REQUEST
import mozilla.components.feature.prompts.address.AddressDelegate
import mozilla.components.feature.prompts.creditcard.CreditCardDelegate
-import mozilla.components.feature.prompts.dialog.FullScreenNotificationToast
-import mozilla.components.feature.prompts.dialog.GestureNavUtils
+import mozilla.components.feature.prompts.dialog.FullScreenNotificationDialog
import mozilla.components.feature.prompts.file.AndroidPhotoPicker
import mozilla.components.feature.prompts.identitycredential.DialogColors
import mozilla.components.feature.prompts.identitycredential.DialogColorsProvider
@@ -2057,19 +2056,15 @@ abstract class BaseBrowserFragment :
@VisibleForTesting
internal fun fullScreenChanged(inFullScreen: Boolean) {
- val activity = activity ?: return
if (inFullScreen) {
// Close find in page bar if opened
findInPageIntegration.onBackPressed()
- FullScreenNotificationToast(
- activity = activity,
- gestureNavString = getString(R.string.exit_fullscreen_with_gesture),
- backButtonString = getString(R.string.exit_fullscreen_with_back_button),
- GestureNavUtils,
- ).show()
+ FullScreenNotificationDialog(R.layout.full_screen_notification_dialog).show(
+ parentFragmentManager,
+ )
- activity.enterImmersiveMode()
+ activity?.enterImmersiveMode()
(view as? SwipeGestureLayout)?.isSwipeEnabled = false
browserToolbarView.collapse()
browserToolbarView.gone()
@@ -2088,12 +2083,12 @@ abstract class BaseBrowserFragment :
MediaState.fullscreen.record(NoExtras())
} else {
- activity.exitImmersiveMode()
+ activity?.exitImmersiveMode()
(view as? SwipeGestureLayout)?.isSwipeEnabled = true
- (activity as? HomeActivity)?.let { homeActivity ->
+ (activity as? HomeActivity)?.let { activity ->
// ExternalAppBrowserActivity exclusively handles it's own theming unless in private mode.
- if (homeActivity !is ExternalAppBrowserActivity || homeActivity.browsingModeManager.mode.isPrivate) {
- homeActivity.themeManager.applyStatusBarTheme(homeActivity)
+ if (activity !is ExternalAppBrowserActivity || activity.browsingModeManager.mode.isPrivate) {
+ activity.themeManager.applyStatusBarTheme(activity)
}
}
if (webAppToolbarShouldBeVisible) {
diff --git a/mobile/android/fenix/app/src/main/res/values/strings.xml b/mobile/android/fenix/app/src/main/res/values/strings.xml
index 60241858b9b..dba271147e6 100644
--- a/mobile/android/fenix/app/src/main/res/values/strings.xml
+++ b/mobile/android/fenix/app/src/main/res/values/strings.xml
@@ -2668,10 +2668,4 @@
<string name="debug_drawer_cfr_tools_title">CFR Tools</string>
<!-- The title of the reset CFR section in CFR Tools -->
<string name="debug_drawer_cfr_tools_reset_cfr_title">Reset CFRs</string>
-
- <!-- Messages explaining how to exit fullscreen mode -->
- <!-- Message shown to explain how to exit fullscreen mode when gesture navigation is enabled -->
- <string name="exit_fullscreen_with_gesture">To exit fullscreen, drag from top and use back gesture</string>
- <!-- Message shown to explain how to exit fullscreen mode when using back button navigation -->
- <string name="exit_fullscreen_with_back_button">To exit fullscreen, drag from top and press back</string>
</resources>
diff --git a/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/browser/integration/FullScreenIntegration.kt b/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/browser/integration/FullScreenIntegration.kt
index a6b27ebc2c4..a003636912b 100644
--- a/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/browser/integration/FullScreenIntegration.kt
+++ b/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/browser/integration/FullScreenIntegration.kt
@@ -9,12 +9,12 @@ import android.os.Build
import android.view.View
import androidx.annotation.VisibleForTesting
import androidx.core.view.isVisible
+import androidx.fragment.app.FragmentManager
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.browser.toolbar.BrowserToolbar
import mozilla.components.concept.engine.EngineView
import mozilla.components.feature.prompts.dialog.FullScreenNotification
-import mozilla.components.feature.prompts.dialog.FullScreenNotificationToast
-import mozilla.components.feature.prompts.dialog.GestureNavUtils
+import mozilla.components.feature.prompts.dialog.FullScreenNotificationDialog
import mozilla.components.feature.session.FullScreenFeature
import mozilla.components.feature.session.SessionUseCases
import mozilla.components.support.base.feature.LifecycleAwareFeature
@@ -38,6 +38,7 @@ class FullScreenIntegration(
private val toolbarView: BrowserToolbar,
private val statusBar: View,
private val engineView: EngineView,
+ private val parentFragmentManager: FragmentManager,
) : LifecycleAwareFeature, UserInteractionHandler {
@VisibleForTesting
internal var feature = FullScreenFeature(
@@ -60,18 +61,13 @@ class FullScreenIntegration(
internal fun fullScreenChanged(
enabled: Boolean,
fullScreenNotification: FullScreenNotification =
- FullScreenNotificationToast(
- activity,
- activity.resources.getString(R.string.exit_fullscreen_with_gesture),
- activity.resources.getString(R.string.exit_fullscreen_with_back_button),
- GestureNavUtils,
- ),
+ FullScreenNotificationDialog(R.layout.dialog_full_screen_notification),
) {
if (enabled) {
enterBrowserFullscreen()
statusBar.isVisible = false
- fullScreenNotification.show()
+ fullScreenNotification.show(parentFragmentManager)
switchToImmersiveMode()
} else {
diff --git a/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/fragment/BrowserFragment.kt b/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/fragment/BrowserFragment.kt
index af80e2e0b59..5ef8c66ed76 100644
--- a/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/fragment/BrowserFragment.kt
+++ b/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/fragment/BrowserFragment.kt
@@ -295,6 +295,7 @@ class BrowserFragment :
binding.browserToolbar,
binding.statusBarBackground,
binding.engineView,
+ parentFragmentManager,
),
this,
view,
diff --git a/mobile/android/focus-android/app/src/main/res/values/strings.xml b/mobile/android/focus-android/app/src/main/res/values/strings.xml
index 80c86fabccf..cec6aa586aa 100644
--- a/mobile/android/focus-android/app/src/main/res/values/strings.xml
+++ b/mobile/android/focus-android/app/src/main/res/values/strings.xml
@@ -1066,10 +1066,4 @@
<!-- This is te text of the snackbar that appears after the search widget was added successfully
to the home screen. -->
<string name="promote_search_widget_snackbar_message">Widget added to home screen</string>
-
- <!-- Messages explaining how to exit fullscreen mode -->
- <!-- Message shown to explain how to exit fullscreen mode when gesture navigation is enabled -->
- <string name="exit_fullscreen_with_gesture">To exit fullscreen, drag from top and use back gesture</string>
- <!-- Message shown to explain how to exit fullscreen mode when using back button navigation -->
- <string name="exit_fullscreen_with_back_button">To exit fullscreen, drag from top and press back</string>
</resources>
diff --git a/mobile/android/android-components/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/FullScreenNotificationTest.kt b/mobile/android/android-components/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/FullScreenNotificationTest.kt
deleted file mode 100644
index a388ce24311..00000000000
--- a/mobile/android/android-components/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/FullScreenNotificationTest.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-/* 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 mozilla.components.feature.prompts.dialog
-
-import android.app.Activity
-import androidx.test.core.app.ActivityScenario
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import mozilla.components.support.test.any
-import mozilla.components.support.test.whenever
-import org.junit.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-import org.robolectric.Robolectric
-import org.robolectric.shadows.ShadowToast
-
-@RunWith(AndroidJUnit4::class)
-class FullScreenNotificationTest {
-
- @Mock
- lateinit var mockGestureNavUtils: GestureNavUtils
-
- @Before
- fun setup() {
- MockitoAnnotations.openMocks(this)
- val activity = Robolectric.buildActivity(EmptyActivity::class.java)
- activity.create()
- }
-
- @Test
- fun `when in 3 button navigation mode, full screen notification shows exit instructions for back button`() {
- whenever(mockGestureNavUtils.isInGestureNavigationMode(any())).thenReturn(false)
- ActivityScenario.launch(EmptyActivity::class.java).use { scenario ->
- scenario.onActivity { activity: EmptyActivity ->
- FullScreenNotificationToast(
- activity,
- "gesture",
- "button",
- mockGestureNavUtils,
- ).show()
- assertTrue(ShadowToast.showedToast("button"))
- }
- }
- }
-
- @Test
- fun `when in gesture navigation mode, full screen notification shows exit instructions for gesture nav`() {
- whenever(mockGestureNavUtils.isInGestureNavigationMode(any())).thenReturn(true)
- ActivityScenario.launch(EmptyActivity::class.java).use { scenario ->
- scenario.onActivity { activity: EmptyActivity ->
- FullScreenNotificationToast(
- activity,
- "gesture",
- "button",
- mockGestureNavUtils,
- ).show()
- assertTrue(ShadowToast.showedToast("gesture"))
- }
- }
- }
... (file diff truncated)
Regression Test / PoC
diff --git a/mobile/android/android-components/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/FullScreenNotificationTest.kt b/mobile/android/android-components/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/FullScreenNotificationTest.kt
deleted file mode 100644
index a388ce24311..00000000000
--- a/mobile/android/android-components/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/FullScreenNotificationTest.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-/* 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 mozilla.components.feature.prompts.dialog
-
-import android.app.Activity
-import androidx.test.core.app.ActivityScenario
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import mozilla.components.support.test.any
-import mozilla.components.support.test.whenever
-import org.junit.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-import org.robolectric.Robolectric
-import org.robolectric.shadows.ShadowToast
-
-@RunWith(AndroidJUnit4::class)
-class FullScreenNotificationTest {
-
- @Mock
- lateinit var mockGestureNavUtils: GestureNavUtils
-
- @Before
- fun setup() {
- MockitoAnnotations.openMocks(this)
- val activity = Robolectric.buildActivity(EmptyActivity::class.java)
- activity.create()
- }
-
- @Test
- fun `when in 3 button navigation mode, full screen notification shows exit instructions for back button`() {
- whenever(mockGestureNavUtils.isInGestureNavigationMode(any())).thenReturn(false)
- ActivityScenario.launch(EmptyActivity::class.java).use { scenario ->
- scenario.onActivity { activity: EmptyActivity ->
- FullScreenNotificationToast(
- activity,
- "gesture",
- "button",
- mockGestureNavUtils,
- ).show()
- assertTrue(ShadowToast.showedToast("button"))
- }
- }
- }
-
- @Test
- fun `when in gesture navigation mode, full screen notification shows exit instructions for gesture nav`() {
- whenever(mockGestureNavUtils.isInGestureNavigationMode(any())).thenReturn(true)
- ActivityScenario.launch(EmptyActivity::class.java).use { scenario ->
- scenario.onActivity { activity: EmptyActivity ->
- FullScreenNotificationToast(
- activity,
- "gesture",
- "button",
- mockGestureNavUtils,
- ).show()
- assertTrue(ShadowToast.showedToast("gesture"))
- }
- }
- }
-}
-
-internal class EmptyActivity : Activity()
diff --git a/mobile/android/focus-android/app/src/test/java/org/mozilla/focus/browser/integration/FullScreenIntegrationTest.kt b/mobile/android/focus-android/app/src/test/java/org/mozilla/focus/browser/integration/FullScreenIntegrationTest.kt
index 64e4a865ad5..69b7a031b4b 100644
--- a/mobile/android/focus-android/app/src/test/java/org/mozilla/focus/browser/integration/FullScreenIntegrationTest.kt
+++ b/mobile/android/focus-android/app/src/test/java/org/mozilla/focus/browser/integration/FullScreenIntegrationTest.kt
@@ -22,7 +22,6 @@ import mozilla.components.support.test.robolectric.testContext
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.runner.RunWith
-import org.mockito.Mockito.anyInt
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.inOrder
import org.mockito.Mockito.never
@@ -43,7 +42,17 @@ internal class FullScreenIntegrationTest {
@Test
fun `WHEN the integration is started THEN start FullScreenFeature`() {
val feature: FullScreenFeature = mock()
- val integration = createFullScreenIntegration().apply {
+ val integration = FullScreenIntegration(
+ mock(),
+ mock(),
+ null,
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ ).apply {
this.feature = feature
}
@@ -55,7 +64,17 @@ internal class FullScreenIntegrationTest {
@Test
fun `WHEN the integration is stopped THEN stop FullScreenFeature`() {
val feature: FullScreenFeature = mock()
- val integration = createFullScreenIntegration().apply {
+ val integration = FullScreenIntegration(
+ mock(),
+ mock(),
+ null,
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ ).apply {
this.feature = feature
}
@@ -67,7 +86,17 @@ internal class FullScreenIntegrationTest {
@Test
fun `WHEN back is pressed THEN send this to the feature`() {
val feature: FullScreenFeature = mock()
- val integration = createFullScreenIntegration().apply {
+ val integration = FullScreenIntegration(
+ mock(),
+ mock(),
+ null,
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ ).apply {
this.feature = feature
}
@@ -83,7 +112,17 @@ internal class FullScreenIntegrationTest {
val activity: Activity = mock()
doReturn(activityWindow).`when`(activity).window
doReturn(windowAttributes).`when`(activityWindow).attributes
- val integration = createFullScreenIntegration(activity = activity)
+ val integration = FullScreenIntegration(
+ activity,
+ mock(),
+ null,
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ )
integration.viewportFitChanged(33)
@@ -102,7 +141,17 @@ internal class FullScreenIntegrationTest {
doReturn(decorView).`when`(activityWindow).decorView
doReturn(layoutParams).`when`(activityWindow).attributes
- val integration = createFullScreenIntegration(activity = activity)
+ val integration = FullScreenIntegration(
+ activity,
+ mock(),
+ null,
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ )
integration.switchToImmersiveMode()
@@ -126,7 +175,17 @@ internal class FullScreenIntegrationTest {
doReturn(layoutParams).`when`(activityWindow).attributes
doReturn(insetsController).`when`(activityWindow).insetsController
- val integration = createFullScreenIntegration(activity = activity)
+ val integration = FullScreenIntegration(
+ activity,
+ mock(),
+ null,
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ )
integration.switchToImmersiveMode()
@@ -155,7 +214,17 @@ internal class FullScreenIntegrationTest {
doReturn(activityWindow).`when`(activity).window
doReturn(decorView).`when`(activityWindow).decorView
doReturn(layoutParams).`when`(activityWindow).attributes
- val integration = createFullScreenIntegration(activity = activity)
+ val integration = FullScreenIntegration(
+ activity,
+ mock(),
+ null,
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ )
integration.exitImmersiveMode()
// Hiding the system bar hides the status and navigation bars.
@@ -178,7 +247,17 @@ internal class FullScreenIntegrationTest {
doReturn(layoutParams).`when`(activityWindow).attributes
doReturn(insetsController).`when`(activityWindow).insetsController
- val integration = createFullScreenIntegration(activity = activity)
+ val integration = FullScreenIntegration(
+ activity,
+ mock(),
+ null,
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ )
integration.exitImmersiveMode()
@@ -199,10 +278,16 @@ internal class FullScreenIntegrationTest {
doReturn(mock<View>()).`when`(engineView).asView()
val settings: Settings = mock()
doReturn(true).`when`(settings).isAccessibilityEnabled()
- val integration = createFullScreenIntegration(
- settings = settings,
- toolbar = toolbar,
- engineView = engineView,
+ val integration = FullScreenIntegration(
+ mock(),
+ mock(),
+ null,
+ mock(),
+ settings,
+ toolbar,
+ mock(),
+ engineView,
+ mock(),
)
integration.enterBrowserFullscreen()
@@ -219,10 +304,16 @@ internal class FullScreenIntegrationTest {
doReturn(mock<View>()).`when`(engineView).asView()
val settings: Settings = mock()
doReturn(false).`when`(settings).isAccessibilityEnabled()
- val integration = createFullScreenIntegration(
- settings = settings,
- toolbar = toolbar,
- engineView = engineView,
+ val integration = FullScreenIntegration(
+ mock(),
+ mock(),
+ null,
+ mock(),
+ settings,
+ toolbar,
+ mock(),
+ engineView,
+ mock(),
)
integration.enterBrowserFullscreen()
@@ -244,13 +335,17 @@ internal class FullScreenIntegrationTest {
val resources: Resources = mock()
val activity: Activity = mock()
doReturn(resources).`when`(activity).resources
- val integration =
- createFullScreenIntegration(
- activity = activity,
- settings = settings,
- toolbar = toolbar,
- engineView = engineView,
- )
+ val integration = FullScreenIntegration(
+ activity,
+ mock(),
+ null,
+ mock(),
+ settings,
+ toolbar,
+ mock(),
+ engineView,
+ mock(),
... (truncated)