Bug 1859393 - Add CFR when the first cookie banner gets cleared.

fenix/121.0
Arturo Mejia 7 months ago committed by mergify[bot]
parent 6fca903727
commit 1cce998212

@ -8530,7 +8530,38 @@ cookie_banners:
metadata:
tags:
- Privacy&Security
cfr_shown:
type: event
description: The cookie banner cfr has been shown
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1859393
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1859393#c2
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: never
metadata:
tags:
- Privacy&Security
cfr_dismissal:
type: event
description: |
The cookie banners CFR was dismissed by the user by interacting
with the outside of the popup
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1859393
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1859393#c2
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: never
metadata:
tags:
- Privacy&Security
site_permissions:
prompt_shown:
type: event

@ -8,9 +8,17 @@ import android.content.Context
import android.view.View
import androidx.annotation.VisibleForTesting
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTag
@ -34,8 +42,11 @@ import mozilla.components.browser.toolbar.BrowserToolbar
import mozilla.components.compose.cfr.CFRPopup
import mozilla.components.compose.cfr.CFRPopup.PopupAlignment.INDICATOR_CENTERED_IN_ANCHOR
import mozilla.components.compose.cfr.CFRPopupProperties
import mozilla.components.concept.engine.EngineSession.CookieBannerHandlingStatus
import mozilla.components.lib.state.ext.flowScoped
import mozilla.components.service.glean.private.NoExtras
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifAnyChanged
import org.mozilla.fenix.GleanMetrics.CookieBanners
import org.mozilla.fenix.GleanMetrics.Shopping
import org.mozilla.fenix.GleanMetrics.TrackingProtection
import org.mozilla.fenix.R
@ -109,6 +120,24 @@ class BrowserToolbarCFRPresenter(
}
}
}
ToolbarCFR.COOKIE_BANNERS -> {
scope = browserStore.flowScoped { flow ->
flow.mapNotNull { it.findCustomTabOrSelectedTab(sessionId) }
.ifAnyChanged { tab ->
arrayOf(
tab.cookieBanner,
)
}
.filter {
it.content.private && it.cookieBanner == CookieBannerHandlingStatus.HANDLED
}
.collect {
scope?.cancel()
settings.shouldShowCookieBannersCFR = false
showCookieBannersCFR()
}
}
}
ToolbarCFR.SHOPPING, ToolbarCFR.SHOPPING_OPTED_IN -> {
scope = browserStore.flowScoped { flow ->
@ -190,6 +219,10 @@ class BrowserToolbarCFRPresenter(
settings.openTabsCount >= CFR_MINIMUM_NUMBER_OPENED_TABS
) -> ToolbarCFR.TCP
isPrivate && settings.shouldShowCookieBannersCFR && settings.shouldUseCookieBannerPrivateMode -> {
ToolbarCFR.COOKIE_BANNERS
}
shoppingExperienceFeature.isEnabled &&
settings.shouldShowReviewQualityCheckCFR -> whichShoppingCFR()
@ -325,6 +358,64 @@ class BrowserToolbarCFRPresenter(
}
}
@VisibleForTesting
@Suppress("LongMethod")
internal fun showCookieBannersCFR() {
CFRPopup(
anchor = toolbar.findViewById(
R.id.mozac_browser_toolbar_security_indicator,
),
properties = CFRPopupProperties(
popupAlignment = INDICATOR_CENTERED_IN_ANCHOR,
popupBodyColors = listOf(
getColor(context, R.color.fx_mobile_layer_color_gradient_end),
getColor(context, R.color.fx_mobile_layer_color_gradient_start),
),
popupVerticalOffset = CFR_TO_ANCHOR_VERTICAL_PADDING.dp,
dismissButtonColor = getColor(context, R.color.fx_mobile_icon_color_oncolor),
indicatorDirection = if (settings.toolbarPosition == ToolbarPosition.TOP) {
CFRPopup.IndicatorDirection.UP
} else {
CFRPopup.IndicatorDirection.DOWN
},
),
onDismiss = {
CookieBanners.cfrDismissal.record(NoExtras())
},
text = {
FirefoxTheme {
Column {
Row(
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
painter = painterResource(id = R.drawable.ic_cookies_disabled),
contentDescription = null,
tint = FirefoxTheme.colors.iconPrimary,
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = context.getString(R.string.cookie_banner_cfr_title),
color = FirefoxTheme.colors.textOnColorPrimary,
style = FirefoxTheme.typography.subtitle2,
)
}
Text(
text = context.getString(R.string.cookie_banner_cfr_message),
color = FirefoxTheme.colors.textOnColorPrimary,
style = FirefoxTheme.typography.body2,
modifier = Modifier.padding(top = 2.dp),
)
}
}
},
).run {
popup = this
show()
CookieBanners.cfrShown.record(NoExtras())
}
}
@VisibleForTesting
internal fun showShoppingCFR(shouldShowOptedInCFR: Boolean) {
CFRPopup(
@ -395,5 +486,5 @@ class BrowserToolbarCFRPresenter(
* The CFR to be shown in the toolbar.
*/
private enum class ToolbarCFR {
TCP, SHOPPING, SHOPPING_OPTED_IN, ERASE, NONE
TCP, SHOPPING, SHOPPING_OPTED_IN, ERASE, COOKIE_BANNERS, NONE
}

@ -778,6 +778,15 @@ class Settings(private val appContext: Context) : PreferencesHolder {
default = { feltPrivateBrowsingEnabled },
)
/**
* Indicates if the cookie banners CRF should be shown.
*/
var shouldShowCookieBannersCFR by lazyFeatureFlagPreference(
appContext.getPreferenceKey(R.string.pref_key_should_show_cookie_banners_action_popup),
featureFlag = true,
default = { shouldShowCookieBannerUI },
)
val blockCookiesSelectionInCustomTrackingProtection by stringPreference(
key = appContext.getPreferenceKey(R.string.pref_key_tracking_protection_custom_cookies_select),
default = if (enabledTotalCookieProtection) {

@ -275,6 +275,8 @@
<string name="pref_key_should_show_total_cookie_protection_popup" translatable="false">pref_key_should_show_total_cookie_protection_popup</string>
<!-- A value of `true` means the erase action popup has not been shown yet -->
<string name="pref_key_should_show_erase_action_popup" translatable="false">pref_key_should_show_erase_action_popup</string>
<!-- A value of `true` means the cookie banners handling action popup has not been shown yet -->
<string name="pref_key_should_show_cookie_banners_action_popup" translatable="false">pref_key_should_show_cookie_banners_action_popup</string>
<string name="pref_key_debug_settings" translatable="false">pref_key_debug_settings</string>

@ -498,6 +498,10 @@
<string name="reduce_cookie_banner_dialog_snackbar_text" moz:RemovedIn="121" tools:ignore="UnusedResources">Youll see fewer cookie requests</string>
<!-- Change setting text button, for the cookie banner re-engagement dialog -->
<string name="reduce_cookie_banner_dialog_change_setting_button" moz:RemovedIn="121" tools:ignore="UnusedResources">Allow</string>
<!--Title for the cookie banner re-engagement CFR -->
<string name="cookie_banner_cfr_title">Firefox just refused cookies for you</string>
<!--Message for the cookie banner re-engagement CFR -->
<string name="cookie_banner_cfr_message">Less distractions, less cookies tracking you on this site.</string>
<!-- Description of the preference to enable "HTTPS-Only" mode. -->
<string name="preferences_https_only_summary">Automatically attempts to connect to sites using HTTPS encryption protocol for increased security.</string>

@ -15,6 +15,7 @@ import io.mockk.verify
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancel
import mozilla.components.browser.state.action.ContentAction
import mozilla.components.browser.state.action.CookieBannerAction
import mozilla.components.browser.state.action.TabListAction
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.CustomTabSessionState
@ -23,6 +24,7 @@ import mozilla.components.browser.state.state.createCustomTab
import mozilla.components.browser.state.state.createTab
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.browser.toolbar.BrowserToolbar
import mozilla.components.concept.engine.EngineSession
import mozilla.components.support.test.ext.joinBlocking
import mozilla.components.support.test.robolectric.testContext
import mozilla.components.support.test.rule.MainCoroutineRule
@ -69,6 +71,40 @@ class BrowserToolbarCFRPresenterTest {
verify { presenter.showTcpCfr() }
}
@Test
fun `GIVEN the cookie banners handling CFR should be shown for a custom tab WHEN the custom tab is fully loaded THEN the TCP CFR is shown`() {
val privateTab = createTab(url = "", private = true)
val browserStore = createBrowserStore(tab = privateTab, selectedTabId = privateTab.id)
val settings: Settings = mockk(relaxed = true) {
every { shouldShowTotalCookieProtectionCFR } returns false
every { shouldShowReviewQualityCheckCFR } returns false
every { reviewQualityCheckOptInTimeInMillis } returns System.currentTimeMillis()
every { shouldShowEraseActionCFR } returns false
every { shouldShowCookieBannersCFR } returns true
every { shouldUseCookieBannerPrivateMode } returns true
every { reviewQualityCheckCfrDisplayTimeInMillis } returns 0L
}
val presenter = createPresenter(
isPrivate = true,
browserStore = browserStore,
settings = settings,
)
presenter.start()
assertNotNull(presenter.scope)
browserStore.dispatch(
CookieBannerAction.UpdateStatusAction(
privateTab.id,
EngineSession.CookieBannerHandlingStatus.HANDLED,
),
).joinBlocking()
verify { presenter.showCookieBannersCFR() }
verify { settings.shouldShowCookieBannersCFR = false }
}
@Test
fun `GIVEN the TCP CFR should be shown WHEN the current normal tab is fully loaded THEN the TCP CFR is shown`() {
val normalTab = createTab(url = "", private = false)
@ -457,6 +493,7 @@ class BrowserToolbarCFRPresenterTest {
every { shouldShowTotalCookieProtectionCFR } returns true
every { shouldShowEraseActionCFR } returns true
every { openTabsCount } returns 5
every { shouldShowCookieBannersCFR } returns true
every { shouldShowReviewQualityCheckCFR } returns true
},
toolbar: BrowserToolbar = mockk {

Loading…
Cancel
Save