Skip to content
This repository has been archived by the owner on Feb 20, 2023. It is now read-only.

Bug 1797577 - Add cookie banner handling panel to the toolbar. #28044

Merged
merged 1 commit into from
Dec 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion app/metrics.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6883,7 +6883,55 @@ cookie_banners:
metadata:
tags:
- Privacy&Security

exception_added:
type: event
description: |
A user added a cookie banner handling exception through
the toggle in the protections panel.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1797577
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/28044#issuecomment-1334548056
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: 118
metadata:
tags:
- Privacy&Security
exception_removed:
type: event
description: |
A user removed a cookie banner handling
exception through the toggle in the protections panel.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1797577
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/28044#issuecomment-1334548056
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: 118
metadata:
tags:
- Privacy&Security
visited_panel:
type: event
description: A user visited the cookie banner toolbar panel
Amejia481 marked this conversation as resolved.
Show resolved Hide resolved
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1797577
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/28044#issuecomment-1334548056
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: 118
metadata:
tags:
- Privacy&Security
site_permissions:
prompt_shown:
type: event
Expand Down
48 changes: 33 additions & 15 deletions app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@ import android.view.ViewGroup
import androidx.annotation.VisibleForTesting
import androidx.appcompat.content.res.AppCompatResources
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import mozilla.components.browser.state.selector.findTab
import mozilla.components.browser.state.state.SessionState
import mozilla.components.browser.state.state.TabSessionState
Expand All @@ -38,6 +42,7 @@ import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.runIfFragmentIsAttached
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.nimbus.FxNimbus
import org.mozilla.fenix.shortcut.PwaOnboardingObserver
import org.mozilla.fenix.theme.ThemeManager

Expand Down Expand Up @@ -360,22 +365,35 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
}

override fun navToQuickSettingsSheet(tab: SessionState, sitePermissions: SitePermissions?) {
requireComponents.useCases.trackingProtectionUseCases.containsException(tab.id) { contains ->
runIfFragmentIsAttached {
val isTrackingProtectionEnabled = tab.trackingProtection.enabled && !contains
val directions =
BrowserFragmentDirections.actionBrowserFragmentToQuickSettingsSheetDialogFragment(
sessionId = tab.id,
url = tab.content.url,
title = tab.content.title,
isSecured = tab.content.securityInfo.secure,
sitePermissions = sitePermissions,
gravity = getAppropriateLayoutGravity(),
certificateName = tab.content.securityInfo.issuer,
permissionHighlights = tab.content.permissionHighlights,
isTrackingProtectionEnabled = isTrackingProtectionEnabled,
val useCase = requireComponents.useCases.trackingProtectionUseCases
FxNimbus.features.cookieBanners.recordExposure()
useCase.containsException(tab.id) { hasTrackingProtectionException ->
lifecycleScope.launch(Dispatchers.Main) {
val cookieBannersStorage = requireComponents.core.cookieBannersStorage
val hasCookieBannerException = withContext(Dispatchers.IO) {
cookieBannersStorage.hasException(
Amejia481 marked this conversation as resolved.
Show resolved Hide resolved
tab.content.url,
tab.content.private,
)
nav(R.id.browserFragment, directions)
}
runIfFragmentIsAttached {
val isTrackingProtectionEnabled =
tab.trackingProtection.enabled && !hasTrackingProtectionException
val directions =
BrowserFragmentDirections.actionBrowserFragmentToQuickSettingsSheetDialogFragment(
sessionId = tab.id,
url = tab.content.url,
title = tab.content.title,
isSecured = tab.content.securityInfo.secure,
sitePermissions = sitePermissions,
gravity = getAppropriateLayoutGravity(),
certificateName = tab.content.securityInfo.issuer,
permissionHighlights = tab.content.permissionHighlights,
isTrackingProtectionEnabled = isTrackingProtectionEnabled,
isCookieHandlingEnabled = !hasCookieBannerException,
)
nav(R.id.browserFragment, directions)
}
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/java/org/mozilla/fenix/components/Core.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import androidx.appcompat.content.res.AppCompatResources.getDrawable
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.toBitmap
import mozilla.components.browser.engine.gecko.GeckoEngine
import mozilla.components.browser.engine.gecko.cookiebanners.GeckoCookieBannersStorage
import mozilla.components.browser.engine.gecko.fetch.GeckoViewFetchClient
import mozilla.components.browser.engine.gecko.permission.GeckoSitePermissionsStorage
import mozilla.components.browser.icons.BrowserIcons
Expand Down Expand Up @@ -183,6 +184,8 @@ class Core(
)
}

val cookieBannersStorage by lazyMonitored { GeckoCookieBannersStorage(geckoRuntime) }

val geckoSitePermissionsStorage by lazyMonitored {
GeckoSitePermissionsStorage(geckoRuntime, OnDiskSitePermissionsStorage(context))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import android.content.Intent
import android.view.View
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.navArgs
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import mozilla.components.browser.state.state.SessionState
import mozilla.components.browser.toolbar.BrowserToolbar
import mozilla.components.concept.engine.manifest.WebAppManifestParser
Expand All @@ -29,6 +33,7 @@ import org.mozilla.fenix.R
import org.mozilla.fenix.browser.BaseBrowserFragment
import org.mozilla.fenix.browser.CustomTabContextMenuCandidate
import org.mozilla.fenix.browser.FenixSnackbarDelegate
import org.mozilla.fenix.components.components
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.requireComponents
Expand Down Expand Up @@ -159,21 +164,29 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), UserInteractionHandler
}

override fun navToQuickSettingsSheet(tab: SessionState, sitePermissions: SitePermissions?) {
val cookieBannersStorage = requireComponents.core.cookieBannersStorage
requireComponents.useCases.trackingProtectionUseCases.containsException(tab.id) { contains ->
runIfFragmentIsAttached {
val directions = ExternalAppBrowserFragmentDirections
.actionGlobalQuickSettingsSheetDialogFragment(
sessionId = tab.id,
url = tab.content.url,
title = tab.content.title,
isSecured = tab.content.securityInfo.secure,
sitePermissions = sitePermissions,
gravity = getAppropriateLayoutGravity(),
certificateName = tab.content.securityInfo.issuer,
permissionHighlights = tab.content.permissionHighlights,
isTrackingProtectionEnabled = tab.trackingProtection.enabled && !contains,
)
nav(R.id.externalAppBrowserFragment, directions)
lifecycleScope.launch(Dispatchers.IO) {
val hasException =
cookieBannersStorage.hasException(tab.content.url, tab.content.private)
withContext(Dispatchers.Main) {
runIfFragmentIsAttached {
val directions = ExternalAppBrowserFragmentDirections
.actionGlobalQuickSettingsSheetDialogFragment(
sessionId = tab.id,
url = tab.content.url,
title = tab.content.title,
isSecured = tab.content.securityInfo.secure,
sitePermissions = sitePermissions,
gravity = getAppropriateLayoutGravity(),
certificateName = tab.content.securityInfo.issuer,
permissionHighlights = tab.content.permissionHighlights,
isTrackingProtectionEnabled = tab.trackingProtection.enabled && !contains,
isCookieHandlingEnabled = !hasException,
)
nav(R.id.externalAppBrowserFragment, directions)
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
SettingsFragmentDirections.actionSettingsFragmentToHttpsOnlyFragment()
}
resources.getString(R.string.pref_key_cookie_banner_settings) -> {
FxNimbus.features.cookieBanners.recordExposure()
CookieBanners.visitedSetting.record(mozilla.components.service.glean.private.NoExtras())
SettingsFragmentDirections.actionSettingsFragmentToCookieBannerFragment()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ package org.mozilla.fenix.settings.quicksettings
import android.content.Context
import androidx.fragment.app.Fragment
import androidx.navigation.NavController
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import mozilla.components.browser.state.state.SessionState
import mozilla.components.concept.engine.cookiehandling.CookieBannersStorage
import mozilla.components.concept.engine.permission.SitePermissions
import org.mozilla.fenix.browser.BrowserFragmentDirections
import org.mozilla.fenix.ext.components
Expand All @@ -29,9 +34,12 @@ interface ConnectionDetailsController {
/**
* Default behavior of [ConnectionDetailsController].
*/
@Suppress("LongParameterList")
class DefaultConnectionDetailsController(
private val context: Context,
private val fragment: Fragment,
private val ioScope: CoroutineScope,
private val cookieBannersStorage: CookieBannersStorage,
private val navController: () -> NavController,
internal var sitePermissions: SitePermissions?,
private val gravity: Int,
Expand All @@ -41,22 +49,30 @@ class DefaultConnectionDetailsController(
override fun handleBackPressed() {
getCurrentTab()?.let { tab ->
context.components.useCases.trackingProtectionUseCases.containsException(tab.id) { contains ->
fragment.runIfFragmentIsAttached {
navController().popBackStack()
val isTrackingProtectionEnabled = tab.trackingProtection.enabled && !contains
val directions =
BrowserFragmentDirections.actionGlobalQuickSettingsSheetDialogFragment(
sessionId = tab.id,
url = tab.content.url,
title = tab.content.title,
isSecured = tab.content.securityInfo.secure,
sitePermissions = sitePermissions,
gravity = gravity,
certificateName = tab.content.securityInfo.issuer,
permissionHighlights = tab.content.permissionHighlights,
isTrackingProtectionEnabled = isTrackingProtectionEnabled,
)
navController().navigate(directions)
ioScope.launch {
val hasException =
cookieBannersStorage.hasException(tab.content.url, tab.content.private)
withContext(Dispatchers.Main) {
fragment.runIfFragmentIsAttached {
navController().popBackStack()
val isTrackingProtectionEnabled =
tab.trackingProtection.enabled && !contains
val directions =
BrowserFragmentDirections.actionGlobalQuickSettingsSheetDialogFragment(
sessionId = tab.id,
url = tab.content.url,
title = tab.content.title,
isSecured = tab.content.securityInfo.secure,
sitePermissions = sitePermissions,
gravity = gravity,
certificateName = tab.content.securityInfo.issuer,
permissionHighlights = tab.content.permissionHighlights,
isTrackingProtectionEnabled = isTrackingProtectionEnabled,
isCookieHandlingEnabled = !hasException,
)
navController().navigate(directions)
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.plus
import mozilla.components.browser.state.selector.findTabOrCustomTab
import mozilla.components.browser.state.state.SessionState
import org.mozilla.fenix.R
Expand Down Expand Up @@ -39,6 +42,8 @@ class ConnectionPanelDialogFragment : FenixDialogFragment() {

val controller = DefaultConnectionDetailsController(
context = requireContext(),
ioScope = viewLifecycleOwner.lifecycleScope + Dispatchers.IO,
cookieBannersStorage = requireComponents.core.cookieBannersStorage,
fragment = this,
navController = { findNavController() },
sitePermissions = args.sitePermissions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import mozilla.components.feature.session.SessionUseCases.ReloadUrlUseCase
import mozilla.components.support.base.feature.OnNeedToRequestPermissions
import mozilla.components.support.ktx.kotlin.getOrigin
import mozilla.telemetry.glean.private.NoExtras
import org.mozilla.fenix.GleanMetrics.CookieBanners
import org.mozilla.fenix.GleanMetrics.TrackingProtection
import org.mozilla.fenix.NavGraphDirections
import org.mozilla.fenix.components.PermissionStorage
Expand Down Expand Up @@ -60,14 +61,19 @@ interface QuickSettingsController {
fun handleAndroidPermissionGranted(feature: PhoneFeature)

/**
* @see [TrackingProtectionInteractor.onTrackingProtectionToggled]
* @see [ProtectionsInteractor.onTrackingProtectionToggled]
Amejia481 marked this conversation as resolved.
Show resolved Hide resolved
*/
fun handleTrackingProtectionToggled(isEnabled: Boolean)

/**
* @see [TrackingProtectionInteractor.onDetailsClicked]
* Navigates to the cookie banners details panel.
*/
fun handleDetailsClicked()
fun handleCookieBannerHandlingDetailsClicked()

/**
* Navigates to the tracking protection details panel.
*/
fun handleTrackingProtectionDetailsClicked()

/**
* Navigates to the connection details. Called when a user clicks on the
Expand Down Expand Up @@ -201,15 +207,34 @@ class DefaultQuickSettingsController(
)
}

override fun handleDetailsClicked() {
override fun handleCookieBannerHandlingDetailsClicked() {
CookieBanners.visitedPanel.record(NoExtras())

navController.popBackStack()

val state = quickSettingsStore.state.protectionsState
val directions = NavGraphDirections
.actionGlobalCookieBannerProtectionPanelDialogFragment(
sessionId = sessionId,
url = state.url,
trackingProtectionEnabled = state.isTrackingProtectionEnabled,
cookieBannerHandlingEnabled = state.isCookieBannerHandlingEnabled,
gravity = context.components.settings.toolbarPosition.androidGravity,
sitePermissions = sitePermissions,
)
navController.navigate(directions)
}

override fun handleTrackingProtectionDetailsClicked() {
navController.popBackStack()

val state = quickSettingsStore.state.trackingProtectionState
val state = quickSettingsStore.state.protectionsState
val directions = NavGraphDirections
.actionGlobalTrackingProtectionPanelDialogFragment(
sessionId = sessionId,
url = state.url,
trackingProtectionEnabled = state.isTrackingProtectionEnabled,
cookieBannerHandlingEnabled = state.isCookieBannerHandlingEnabled,
gravity = context.components.settings.toolbarPosition.androidGravity,
sitePermissions = sitePermissions,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ package org.mozilla.fenix.settings.quicksettings

import mozilla.components.lib.state.Action
import org.mozilla.fenix.settings.PhoneFeature
import org.mozilla.fenix.trackingprotection.TrackingProtectionState
import org.mozilla.fenix.trackingprotection.ProtectionsState

/**
* Parent [Action] for all the [QuickSettingsFragmentState] changes.
Expand Down Expand Up @@ -49,7 +49,7 @@ sealed class WebsitePermissionAction(open val updatedFeature: PhoneFeature) : Qu
}

/**
* All possible [TrackingProtectionState] changes as a result oof user / system interactions.
* All possible [ProtectionsState] changes in the quick setting panel.
*/
sealed class TrackingProtectionAction : QuickSettingsFragmentAction() {
/**
Expand Down
Loading