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

Commit

Permalink
Bug 1797577 - Add cookie banner handling panel to the toolbar.
Browse files Browse the repository at this point in the history
  • Loading branch information
Amejia481 authored and mergify[bot] committed Dec 8, 2022
1 parent e7a7712 commit cc666c8
Show file tree
Hide file tree
Showing 51 changed files with 1,857 additions and 449 deletions.
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
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(
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]
*/
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

0 comments on commit cc666c8

Please sign in to comment.