Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • clairehurst/firefox-android
  • boklm/firefox-android
  • tpo/applications/firefox-android
  • dan/firefox-android
  • henry/firefox-android
  • pierov/firefox-android
  • morgan/firefox-android
  • cypherpunks1/firefox-android
  • ma1/firefox-android
  • astravox/firefox-android
10 results
Show changes
Commits on Source (4)
......@@ -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
}
......
......@@ -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
......
......@@ -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
}
......
......@@ -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("") }) { "" }
}
}
......@@ -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,
......
......@@ -71,6 +71,7 @@ class ShareFragment : AppCompatDialogFragment() {
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
requireComponents.useCases.sessionUseCases.exitFullscreen.invoke()
val binding = FragmentShareBinding.inflate(
inflater,
container,
......