Medium CVSS 5.3 firefox Spoofing 🔧 Commit mapped

Overview

Medium
Severity
5.3
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionMultiple prompts and panels from both Firefox and the Android OS could be used to obscure the notification announcing the transition to fullscreen mode after the fix for CVE-2023-6870 in Firefox 121. This could lead to spoofing the browser UI if the sudden appearance of the prompt distracted the user from noticing the visual transition happening behind the prompt. These notifications now use the Android Toast feature. <br>*This bug only affects Firefox on Android. Other operating systems are unaffected.*
ComponentCore
Bug ClassSpoofing
Tracker1902996
Fix commit2bcbc3b41644 (firefox) +246/-257
CWECWE-1021
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
CISA KEVNot listed
CreditedShaheen Fazim, Raphael Saniyazov, Rifa&apos;i Rejal Maynando, James Lee, P Umar Farooq, Hafiizh
Disclosed2024-09-03

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 Dialog within 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.

Key insight
The core mistake was announcing a security-critical UI transition with an OS-owned 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

  1. Trigger fullscreen A malicious page calls the Fullscreen API, causing Firefox for Android to hide its browser chrome and post the FullScreenNotificationToast.
  2. 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.
  3. Obscure the notice The prompt covers the Toast so the user never sees the “you are now in fullscreen” announcement.
  4. 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.
  5. 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

An attacker gains the ability to spoof the browser UI in the Firefox for Android content/UI context by suppressing the fullscreen-entry notification and presenting fake chrome, which is rated moderate impact. Preconditions are that the user is on Firefox for Android (other operating systems are unaffected) and that a prompt or panel appears at the moment of the fullscreen transition to obscure the Toast and divert attention. No memory corruption is involved; the exploit is purely a logic and UI-timing weakness.

Changed Functions

FunctionChangeNotes
FullScreenNotificationToast
mobile/android/android-components/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/FullScreenNotification.kt
modified
FullScreenNotificationDialog
mobile/android/android-components/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/FullScreenNotificationDialog.kt
modified
if
mobile/android/android-components/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/GestureNavUtils.kt
modified
if
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt
modified
if
mobile/android/focus-android/app/src/main/java/org/mozilla/focus/browser/integration/FullScreenIntegration.kt
modified
FullScreenNotificationTest
mobile/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.kt
  • mobile/android/android-components/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/FullScreenNotificationDialog.kt
  • mobile/android/android-components/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/GestureNavUtils.kt
  • mobile/android/android-components/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/FullScreenNotificationTest.kt
  • mobile/android/android-components/docs/changelog.md
  • mobile/android/fenix/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt
  • mobile/android/fenix/app/src/main/res/values/strings.xml
  • mobile/android/focus-android/app/src/main/java/org/mozilla/focus/browser/integration/FullScreenIntegration.kt
  • mobile/android/focus-android/app/src/main/java/org/mozilla/focus/fragment/BrowserFragment.kt
  • mobile/android/focus-android/app/src/main/res/values/strings.xml
  • mobile/android/focus-android/app/src/test/java/org/mozilla/focus/browser/integration/FullScreenIntegrationTest.kt

Audit Directions

  • OS-owned overlays for security UI
    Audit any use of Toast or 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 races
    Look 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 guarantees
    Review notification surfaces that omit flags like FLAG_NOT_FOCUSABLE or explicit gravity/stacking control, as these can be overlaid, focus-stolen, or clickjacked.
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)
Loading diff…

Regression Test / PoC

shipped with the fix
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)
Loading diff…