ma1 pushed to branch firefox-android-115.2.1-13.5-1 at The Tor Project / Applications / firefox-android

Commits:

5 changed files:

Changes:

  • android-components/components/feature/sitepermissions/src/main/java/mozilla/components/feature/sitepermissions/SitePermissionsFeature.kt
    ... ... @@ -56,12 +56,14 @@ import mozilla.components.concept.engine.permission.SitePermissions
    56 56
     import mozilla.components.concept.engine.permission.SitePermissions.Status.ALLOWED
    
    57 57
     import mozilla.components.concept.engine.permission.SitePermissions.Status.BLOCKED
    
    58 58
     import mozilla.components.concept.engine.permission.SitePermissionsStorage
    
    59
    +import mozilla.components.feature.session.SessionUseCases
    
    59 60
     import mozilla.components.feature.sitepermissions.SitePermissionsFeature.DialogConfig
    
    60 61
     import mozilla.components.feature.tabs.TabsUseCases.SelectOrAddUseCase
    
    61 62
     import mozilla.components.lib.state.ext.flowScoped
    
    62 63
     import mozilla.components.support.base.feature.LifecycleAwareFeature
    
    63 64
     import mozilla.components.support.base.feature.OnNeedToRequestPermissions
    
    64 65
     import mozilla.components.support.base.feature.PermissionsFeature
    
    66
    +import mozilla.components.support.base.log.logger.Logger
    
    65 67
     import mozilla.components.support.ktx.android.content.isPermissionGranted
    
    66 68
     import mozilla.components.support.ktx.kotlin.getOrigin
    
    67 69
     import mozilla.components.support.ktx.kotlin.stripDefaultPort
    
    ... ... @@ -72,8 +74,6 @@ import mozilla.components.ui.icons.R as iconsR
    72 74
     
    
    73 75
     internal const val PROMPT_FRAGMENT_TAG = "mozac_feature_sitepermissions_prompt_dialog"
    
    74 76
     
    
    75
    -private const val FULL_SCREEN_NOTIFICATION_TAG = "mozac_feature_prompts_full_screen_notification_dialog"
    
    76
    -
    
    77 77
     @VisibleForTesting
    
    78 78
     internal const val STORAGE_ACCESS_DOCUMENTATION_URL =
    
    79 79
         "https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API"
    
    ... ... @@ -94,13 +94,15 @@ internal const val STORAGE_ACCESS_DOCUMENTATION_URL =
    94 94
      * need to be requested. Once the request is completed, [onPermissionsResult] needs to be invoked.
    
    95 95
      * @property onShouldShowRequestPermissionRationale a callback that allows the feature to query
    
    96 96
      * the ActivityCompat.shouldShowRequestPermissionRationale or the Fragment.shouldShowRequestPermissionRationale values.
    
    97
    + * @property exitFullscreenUseCase optional the use case in charge of exiting fullscreen
    
    97 98
      * @property shouldShowDoNotAskAgainCheckBox optional Visibility for Do not ask again Checkbox
    
    98 99
      **/
    
    99 100
     
    
    100 101
     @Suppress("TooManyFunctions", "LargeClass", "LongParameterList")
    
    101 102
     class SitePermissionsFeature(
    
    102 103
         private val context: Context,
    
    103
    -    private var sessionId: String? = null,
    
    104
    +    @set:VisibleForTesting
    
    105
    +    internal var sessionId: String? = null,
    
    104 106
         private val storage: SitePermissionsStorage = OnDiskSitePermissionsStorage(context),
    
    105 107
         var sitePermissionsRules: SitePermissionsRules? = null,
    
    106 108
         private val fragmentManager: FragmentManager,
    
    ... ... @@ -109,6 +111,7 @@ class SitePermissionsFeature(
    109 111
         override val onNeedToRequestPermissions: OnNeedToRequestPermissions,
    
    110 112
         val onShouldShowRequestPermissionRationale: (permission: String) -> Boolean,
    
    111 113
         private val store: BrowserStore,
    
    114
    +    private val exitFullscreenUseCase: SessionUseCases.ExitFullScreenUseCase = SessionUseCases(store).exitFullscreen,
    
    112 115
         private val shouldShowDoNotAskAgainCheckBox: Boolean = true,
    
    113 116
     ) : LifecycleAwareFeature, PermissionsFeature {
    
    114 117
         @VisibleForTesting
    
    ... ... @@ -116,6 +119,8 @@ class SitePermissionsFeature(
    116 119
             SelectOrAddUseCase(store)
    
    117 120
         }
    
    118 121
     
    
    122
    +    private val logger = Logger("SitePermissionsFeature")
    
    123
    +
    
    119 124
         internal val ioCoroutineScope by lazy { coroutineScopeInitializer() }
    
    120 125
     
    
    121 126
         internal var coroutineScopeInitializer = {
    
    ... ... @@ -428,26 +433,29 @@ class SitePermissionsFeature(
    428 433
                 consumePermissionRequest(permissionRequest)
    
    429 434
                 return null
    
    430 435
             }
    
    431
    -
    
    432
    -        val private: Boolean = store.state.findTabOrCustomTabOrSelectedTab(sessionId)?.content?.private
    
    433
    -            ?: throw IllegalStateException("Unable to find session for $sessionId or selected session")
    
    436
    +        val tab = store.state.findTabOrCustomTabOrSelectedTab(sessionId)
    
    437
    +        if (tab == null) {
    
    438
    +            logger.error("Unable to find a tab for $sessionId rejecting the prompt request")
    
    439
    +            permissionRequest.reject()
    
    440
    +            consumePermissionRequest(permissionRequest)
    
    441
    +            return null
    
    442
    +        }
    
    434 443
     
    
    435 444
             val permissionFromStorage = withContext(coroutineScope.coroutineContext) {
    
    436
    -            storage.findSitePermissionsBy(origin, private = private)
    
    445
    +            storage.findSitePermissionsBy(origin, private = tab.content.private)
    
    437 446
             }
    
    438
    -
    
    439 447
             val prompt = if (shouldApplyRules(permissionFromStorage)) {
    
    440 448
                 handleRuledFlow(permissionRequest, origin)
    
    441 449
             } else {
    
    442 450
                 handleNoRuledFlow(permissionFromStorage, permissionRequest, origin)
    
    443 451
             }
    
    444 452
     
    
    445
    -        val fullScreenNotificationDisplayed =
    
    446
    -            fragmentManager.fragments.any { fragment -> fragment.tag == FULL_SCREEN_NOTIFICATION_TAG }
    
    447
    -
    
    448
    -        return if (fullScreenNotificationDisplayed || prompt == null) {
    
    453
    +        return if (prompt == null) {
    
    449 454
                 null
    
    450 455
             } else {
    
    456
    +            // If we are in fullscreen, then exit to show the permission prompt.
    
    457
    +            // This won't have any effect if we are not in fullscreen.
    
    458
    +            exitFullscreenUseCase.invoke(tab.id)
    
    451 459
                 prompt.show(fragmentManager, PROMPT_FRAGMENT_TAG)
    
    452 460
                 prompt
    
    453 461
             }
    

  • android-components/components/feature/sitepermissions/src/test/java/mozilla/components/feature/sitepermissions/SitePermissionsFeatureTest.kt
    ... ... @@ -600,6 +600,24 @@ class SitePermissionsFeatureTest {
    600 600
             verify(sitePermissionFeature).consumePermissionRequest(mockPermissionRequest)
    
    601 601
         }
    
    602 602
     
    
    603
    +    @Test
    
    604
    +    fun `GIVEN sessionId which does not match a selected or custom tab WHEN onContentPermissionRequested() THEN reject, consumePermissionRequest are called `() {
    
    605
    +        val mockPermissionRequest: PermissionRequest = mock {
    
    606
    +            whenever(permissions).thenReturn(listOf(ContentVideoCamera(id = "permission")))
    
    607
    +        }
    
    608
    +
    
    609
    +        doNothing().`when`(mockPermissionRequest).reject()
    
    610
    +
    
    611
    +        sitePermissionFeature.sessionId = null
    
    612
    +
    
    613
    +        runTestOnMain {
    
    614
    +            sitePermissionFeature.onContentPermissionRequested(mockPermissionRequest, URL)
    
    615
    +        }
    
    616
    +
    
    617
    +        verify(mockPermissionRequest).reject()
    
    618
    +        verify(sitePermissionFeature).consumePermissionRequest(mockPermissionRequest)
    
    619
    +    }
    
    620
    +
    
    603 621
         @Test
    
    604 622
         fun `GIVEN location permissionRequest and shouldApplyRules is true WHEN onContentPermissionRequested() THEN handleRuledFlow is called`() = runTestOnMain {
    
    605 623
             // given
    

  • android-components/components/feature/webauthn/src/main/java/mozilla/components/feature/webauthn/WebAuthnFeature.kt
    ... ... @@ -20,6 +20,8 @@ import mozilla.components.support.base.log.logger.Logger
    20 20
     class WebAuthnFeature(
    
    21 21
         private val engine: Engine,
    
    22 22
         private val activity: Activity,
    
    23
    +    private val exitFullScreen: (String?) -> Unit,
    
    24
    +    private val currentTab: () -> String?,
    
    23 25
     ) : LifecycleAwareFeature, ActivityResultHandler, ActivityDelegate {
    
    24 26
         private val logger = Logger("WebAuthnFeature")
    
    25 27
         private var requestCodeCounter = ACTIVITY_REQUEST_CODE
    
    ... ... @@ -53,6 +55,7 @@ class WebAuthnFeature(
    53 55
     
    
    54 56
         override fun startIntentSenderForResult(intent: IntentSender, onResult: (Intent?) -> Unit) {
    
    55 57
             logger.info("Received activity delegate request with code: $requestCodeCounter")
    
    58
    +        exitFullScreen(currentTab())
    
    56 59
             activity.startIntentSenderForResult(intent, requestCodeCounter, null, 0, 0, 0)
    
    57 60
             callbackRef = onResult
    
    58 61
         }
    

  • android-components/components/feature/webauthn/src/test/java/mozilla/components/feature/webauthn/WebAuthnFeatureTest.kt
    ... ... @@ -22,6 +22,8 @@ import org.mockito.Mockito.verify
    22 22
     class WebAuthnFeatureTest {
    
    23 23
         private lateinit var engine: Engine
    
    24 24
         private lateinit var activity: Activity
    
    25
    +    private val exitFullScreen: (String?) -> Unit = { _ -> exitFullScreenUseCaseCalled = true }
    
    26
    +    private var exitFullScreenUseCaseCalled = false
    
    25 27
     
    
    26 28
         @Before
    
    27 29
         fun setup() {
    
    ... ... @@ -31,7 +33,7 @@ class WebAuthnFeatureTest {
    31 33
     
    
    32 34
         @Test
    
    33 35
         fun `feature registers itself on start`() {
    
    34
    -        val feature = WebAuthnFeature(engine, activity)
    
    36
    +        val feature = webAuthnFeature()
    
    35 37
     
    
    36 38
             feature.start()
    
    37 39
     
    
    ... ... @@ -40,7 +42,7 @@ class WebAuthnFeatureTest {
    40 42
     
    
    41 43
         @Test
    
    42 44
         fun `feature unregisters itself on stop`() {
    
    43
    -        val feature = WebAuthnFeature(engine, activity)
    
    45
    +        val feature = webAuthnFeature()
    
    44 46
     
    
    45 47
             feature.stop()
    
    46 48
     
    
    ... ... @@ -49,7 +51,7 @@ class WebAuthnFeatureTest {
    49 51
     
    
    50 52
         @Test
    
    51 53
         fun `activity delegate starts intent sender`() {
    
    52
    -        val feature = WebAuthnFeature(engine, activity)
    
    54
    +        val feature = webAuthnFeature()
    
    53 55
             val callback: ((Intent?) -> Unit) = { }
    
    54 56
             val intentSender: IntentSender = mock()
    
    55 57
     
    
    ... ... @@ -60,7 +62,7 @@ class WebAuthnFeatureTest {
    60 62
     
    
    61 63
         @Test
    
    62 64
         fun `callback is invoked`() {
    
    63
    -        val feature = WebAuthnFeature(engine, activity)
    
    65
    +        val feature = webAuthnFeature()
    
    64 66
             var callbackInvoked = false
    
    65 67
             val callback: ((Intent?) -> Unit) = { callbackInvoked = true }
    
    66 68
             val intentSender: IntentSender = mock()
    
    ... ... @@ -77,10 +79,14 @@ class WebAuthnFeatureTest {
    77 79
     
    
    78 80
         @Test
    
    79 81
         fun `feature won't process results with the wrong request code`() {
    
    80
    -        val feature = WebAuthnFeature(engine, activity)
    
    82
    +        val feature = webAuthnFeature()
    
    81 83
     
    
    82 84
             val result = feature.onActivityResult(ACTIVITY_REQUEST_CODE - 5, Intent(), 0)
    
    83 85
     
    
    84 86
             assertFalse(result)
    
    85 87
         }
    
    88
    +
    
    89
    +    private fun webAuthnFeature(): WebAuthnFeature {
    
    90
    +        return WebAuthnFeature(engine, activity, { exitFullScreen("") }) { "" }
    
    91
    +    }
    
    86 92
     }

  • fenix/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt
    ... ... @@ -830,6 +830,8 @@ abstract class BaseBrowserFragment :
    830 830
                     feature = WebAuthnFeature(
    
    831 831
                         engine = requireComponents.core.engine,
    
    832 832
                         activity = requireActivity(),
    
    833
    +                    exitFullScreen = requireComponents.useCases.sessionUseCases.exitFullscreen::invoke,
    
    834
    +                    currentTab = { store.state.selectedTabId },
    
    833 835
                     ),
    
    834 836
                     owner = this,
    
    835 837
                     view = view,