ma1 pushed to branch firefox-android-115.2.1-13.5-1 at The Tor Project / Applications / firefox-android
Commits: ccabd9ad by Arturo Mejia at 2024-05-09T15:58:16+02:00 Bug 1846306 - Do not throw IllegalStateException when unable to find a session for given prompt request in onContentPermissionRequested
- - - - - 40cae60d by hackademix at 2024-05-09T15:58:20+02:00 Bug 1871217: Improve permission handling in Fullscreen - BP, tor-browser#42656
- - - - - 46475c73 by hackademix at 2024-05-09T15:58:20+02:00 Bug 1892296 - improve webauthn experience - BP, tor-browser#42656
- - - - -
5 changed files:
- android-components/components/feature/sitepermissions/src/main/java/mozilla/components/feature/sitepermissions/SitePermissionsFeature.kt - android-components/components/feature/sitepermissions/src/test/java/mozilla/components/feature/sitepermissions/SitePermissionsFeatureTest.kt - android-components/components/feature/webauthn/src/main/java/mozilla/components/feature/webauthn/WebAuthnFeature.kt - android-components/components/feature/webauthn/src/test/java/mozilla/components/feature/webauthn/WebAuthnFeatureTest.kt - fenix/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt
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 import mozilla.components.concept.engine.permission.SitePermissions.Status.ALLOWED import mozilla.components.concept.engine.permission.SitePermissions.Status.BLOCKED import mozilla.components.concept.engine.permission.SitePermissionsStorage +import mozilla.components.feature.session.SessionUseCases import mozilla.components.feature.sitepermissions.SitePermissionsFeature.DialogConfig import mozilla.components.feature.tabs.TabsUseCases.SelectOrAddUseCase import mozilla.components.lib.state.ext.flowScoped import mozilla.components.support.base.feature.LifecycleAwareFeature import mozilla.components.support.base.feature.OnNeedToRequestPermissions import mozilla.components.support.base.feature.PermissionsFeature +import mozilla.components.support.base.log.logger.Logger import mozilla.components.support.ktx.android.content.isPermissionGranted import mozilla.components.support.ktx.kotlin.getOrigin import mozilla.components.support.ktx.kotlin.stripDefaultPort @@ -72,8 +74,6 @@ import mozilla.components.ui.icons.R as iconsR
internal const val PROMPT_FRAGMENT_TAG = "mozac_feature_sitepermissions_prompt_dialog"
-private const val FULL_SCREEN_NOTIFICATION_TAG = "mozac_feature_prompts_full_screen_notification_dialog" - @VisibleForTesting internal const val STORAGE_ACCESS_DOCUMENTATION_URL = "https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API" @@ -94,13 +94,15 @@ internal const val STORAGE_ACCESS_DOCUMENTATION_URL = * need to be requested. Once the request is completed, [onPermissionsResult] needs to be invoked. * @property onShouldShowRequestPermissionRationale a callback that allows the feature to query * the ActivityCompat.shouldShowRequestPermissionRationale or the Fragment.shouldShowRequestPermissionRationale values. + * @property exitFullscreenUseCase optional the use case in charge of exiting fullscreen * @property shouldShowDoNotAskAgainCheckBox optional Visibility for Do not ask again Checkbox **/
@Suppress("TooManyFunctions", "LargeClass", "LongParameterList") class SitePermissionsFeature( private val context: Context, - private var sessionId: String? = null, + @set:VisibleForTesting + internal var sessionId: String? = null, private val storage: SitePermissionsStorage = OnDiskSitePermissionsStorage(context), var sitePermissionsRules: SitePermissionsRules? = null, private val fragmentManager: FragmentManager, @@ -109,6 +111,7 @@ class SitePermissionsFeature( override val onNeedToRequestPermissions: OnNeedToRequestPermissions, val onShouldShowRequestPermissionRationale: (permission: String) -> Boolean, private val store: BrowserStore, + private val exitFullscreenUseCase: SessionUseCases.ExitFullScreenUseCase = SessionUseCases(store).exitFullscreen, private val shouldShowDoNotAskAgainCheckBox: Boolean = true, ) : LifecycleAwareFeature, PermissionsFeature { @VisibleForTesting @@ -116,6 +119,8 @@ class SitePermissionsFeature( SelectOrAddUseCase(store) }
+ private val logger = Logger("SitePermissionsFeature") + internal val ioCoroutineScope by lazy { coroutineScopeInitializer() }
internal var coroutineScopeInitializer = { @@ -428,26 +433,29 @@ class SitePermissionsFeature( consumePermissionRequest(permissionRequest) return null } - - val private: Boolean = store.state.findTabOrCustomTabOrSelectedTab(sessionId)?.content?.private - ?: throw IllegalStateException("Unable to find session for $sessionId or selected session") + val tab = store.state.findTabOrCustomTabOrSelectedTab(sessionId) + if (tab == null) { + logger.error("Unable to find a tab for $sessionId rejecting the prompt request") + permissionRequest.reject() + consumePermissionRequest(permissionRequest) + return null + }
val permissionFromStorage = withContext(coroutineScope.coroutineContext) { - storage.findSitePermissionsBy(origin, private = private) + storage.findSitePermissionsBy(origin, private = tab.content.private) } - val prompt = if (shouldApplyRules(permissionFromStorage)) { handleRuledFlow(permissionRequest, origin) } else { handleNoRuledFlow(permissionFromStorage, permissionRequest, origin) }
- val fullScreenNotificationDisplayed = - fragmentManager.fragments.any { fragment -> fragment.tag == FULL_SCREEN_NOTIFICATION_TAG } - - return if (fullScreenNotificationDisplayed || prompt == null) { + return if (prompt == null) { null } else { + // If we are in fullscreen, then exit to show the permission prompt. + // This won't have any effect if we are not in fullscreen. + exitFullscreenUseCase.invoke(tab.id) prompt.show(fragmentManager, PROMPT_FRAGMENT_TAG) prompt }
===================================== android-components/components/feature/sitepermissions/src/test/java/mozilla/components/feature/sitepermissions/SitePermissionsFeatureTest.kt ===================================== @@ -600,6 +600,24 @@ class SitePermissionsFeatureTest { verify(sitePermissionFeature).consumePermissionRequest(mockPermissionRequest) }
+ @Test + fun `GIVEN sessionId which does not match a selected or custom tab WHEN onContentPermissionRequested() THEN reject, consumePermissionRequest are called `() { + val mockPermissionRequest: PermissionRequest = mock { + whenever(permissions).thenReturn(listOf(ContentVideoCamera(id = "permission"))) + } + + doNothing().`when`(mockPermissionRequest).reject() + + sitePermissionFeature.sessionId = null + + runTestOnMain { + sitePermissionFeature.onContentPermissionRequested(mockPermissionRequest, URL) + } + + verify(mockPermissionRequest).reject() + verify(sitePermissionFeature).consumePermissionRequest(mockPermissionRequest) + } + @Test fun `GIVEN location permissionRequest and shouldApplyRules is true WHEN onContentPermissionRequested() THEN handleRuledFlow is called`() = runTestOnMain { // 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 class WebAuthnFeature( private val engine: Engine, private val activity: Activity, + private val exitFullScreen: (String?) -> Unit, + private val currentTab: () -> String?, ) : LifecycleAwareFeature, ActivityResultHandler, ActivityDelegate { private val logger = Logger("WebAuthnFeature") private var requestCodeCounter = ACTIVITY_REQUEST_CODE @@ -53,6 +55,7 @@ class WebAuthnFeature(
override fun startIntentSenderForResult(intent: IntentSender, onResult: (Intent?) -> Unit) { logger.info("Received activity delegate request with code: $requestCodeCounter") + exitFullScreen(currentTab()) activity.startIntentSenderForResult(intent, requestCodeCounter, null, 0, 0, 0) callbackRef = onResult }
===================================== android-components/components/feature/webauthn/src/test/java/mozilla/components/feature/webauthn/WebAuthnFeatureTest.kt ===================================== @@ -22,6 +22,8 @@ import org.mockito.Mockito.verify class WebAuthnFeatureTest { private lateinit var engine: Engine private lateinit var activity: Activity + private val exitFullScreen: (String?) -> Unit = { _ -> exitFullScreenUseCaseCalled = true } + private var exitFullScreenUseCaseCalled = false
@Before fun setup() { @@ -31,7 +33,7 @@ class WebAuthnFeatureTest {
@Test fun `feature registers itself on start`() { - val feature = WebAuthnFeature(engine, activity) + val feature = webAuthnFeature()
feature.start()
@@ -40,7 +42,7 @@ class WebAuthnFeatureTest {
@Test fun `feature unregisters itself on stop`() { - val feature = WebAuthnFeature(engine, activity) + val feature = webAuthnFeature()
feature.stop()
@@ -49,7 +51,7 @@ class WebAuthnFeatureTest {
@Test fun `activity delegate starts intent sender`() { - val feature = WebAuthnFeature(engine, activity) + val feature = webAuthnFeature() val callback: ((Intent?) -> Unit) = { } val intentSender: IntentSender = mock()
@@ -60,7 +62,7 @@ class WebAuthnFeatureTest {
@Test fun `callback is invoked`() { - val feature = WebAuthnFeature(engine, activity) + val feature = webAuthnFeature() var callbackInvoked = false val callback: ((Intent?) -> Unit) = { callbackInvoked = true } val intentSender: IntentSender = mock() @@ -77,10 +79,14 @@ class WebAuthnFeatureTest {
@Test fun `feature won't process results with the wrong request code`() { - val feature = WebAuthnFeature(engine, activity) + val feature = webAuthnFeature()
val result = feature.onActivityResult(ACTIVITY_REQUEST_CODE - 5, Intent(), 0)
assertFalse(result) } + + private fun webAuthnFeature(): WebAuthnFeature { + return WebAuthnFeature(engine, activity, { exitFullScreen("") }) { "" } + } }
===================================== fenix/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt ===================================== @@ -830,6 +830,8 @@ abstract class BaseBrowserFragment : feature = WebAuthnFeature( engine = requireComponents.core.engine, activity = requireActivity(), + exitFullScreen = requireComponents.useCases.sessionUseCases.exitFullscreen::invoke, + currentTab = { store.state.selectedTabId }, ), owner = this, view = view,
View it on GitLab: https://gitlab.torproject.org/tpo/applications/firefox-android/-/compare/fe6...