diff --git a/app/metrics.yaml b/app/metrics.yaml index bc42ea6e1..cb5fd6a35 100644 --- a/app/metrics.yaml +++ b/app/metrics.yaml @@ -1378,7 +1378,7 @@ tracking_protection: etp_setting_changed: type: event description: > - A user added a tracking protection exception through the TP toggle in the panel. + A user changed their tracking protection level setting to either strict or standard. extra_keys: etp_setting: description: "The new setting for ETP: strict, standard" diff --git a/app/src/main/java/org/mozilla/fenix/AppRequestInterceptor.kt b/app/src/main/java/org/mozilla/fenix/AppRequestInterceptor.kt index 4cb9e90c9..6819edfd8 100644 --- a/app/src/main/java/org/mozilla/fenix/AppRequestInterceptor.kt +++ b/app/src/main/java/org/mozilla/fenix/AppRequestInterceptor.kt @@ -11,17 +11,15 @@ import mozilla.components.browser.errorpages.ErrorType import mozilla.components.concept.engine.EngineSession import mozilla.components.concept.engine.request.RequestInterceptor import org.mozilla.fenix.components.metrics.Event -import org.mozilla.fenix.exceptions.ExceptionDomains import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.settings -import org.mozilla.fenix.ext.tryGetHostFromUrl class AppRequestInterceptor(private val context: Context) : RequestInterceptor { - override fun onLoadRequest(session: EngineSession, uri: String): RequestInterceptor.InterceptionResponse? { - val host = uri.tryGetHostFromUrl() - - adjustTrackingProtection(host, context, session) - + override fun onLoadRequest( + session: EngineSession, + uri: String + ): RequestInterceptor.InterceptionResponse? { + adjustTrackingProtection(context, session) // WebChannel-driven authentication does not require a separate redirect interceptor. return if (context.isInExperiment(Experiments.asFeatureWebChannelsDisabled)) { context.components.services.accountsAuthFeature.interceptor.onLoadRequest(session, uri) @@ -30,10 +28,9 @@ class AppRequestInterceptor(private val context: Context) : RequestInterceptor { } } - private fun adjustTrackingProtection(host: String, context: Context, session: EngineSession) { - val trackingProtectionException = ExceptionDomains(context).load().contains(host) + private fun adjustTrackingProtection(context: Context, session: EngineSession) { val trackingProtectionEnabled = context.settings().shouldUseTrackingProtection - if (trackingProtectionException || !trackingProtectionEnabled) { + if (!trackingProtectionEnabled) { session.disableTrackingProtection() } else { val core = context.components.core diff --git a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt index bff152e98..f58d05744 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt @@ -36,6 +36,7 @@ import mozilla.appservices.places.BookmarkRoot import mozilla.components.browser.session.Session import mozilla.components.feature.contextmenu.ContextMenuCandidate import mozilla.components.feature.readerview.ReaderViewFeature +import mozilla.components.feature.session.TrackingProtectionUseCases import mozilla.components.feature.sitepermissions.SitePermissions import mozilla.components.lib.state.ext.consumeFrom import mozilla.components.support.base.feature.BackHandler @@ -224,14 +225,21 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler { } override fun navToTrackingProtectionPanel(session: Session) { - val directions = - BrowserFragmentDirections.actionBrowserFragmentToTrackingProtectionPanelDialogFragment( - sessionId = session.id, - url = session.url, - trackingProtectionEnabled = session.trackerBlockingEnabled, - gravity = getAppropriateLayoutGravity() - ) - nav(R.id.browserFragment, directions) + val useCase = TrackingProtectionUseCases( + sessionManager = requireComponents.core.sessionManager, + engine = requireComponents.core.engine + ) + useCase.containsException(session) { contains -> + val isEnabled = session.trackerBlockingEnabled && !contains + val directions = + BrowserFragmentDirections.actionBrowserFragmentToTrackingProtectionPanelDialogFragment( + sessionId = session.id, + url = session.url, + trackingProtectionEnabled = isEnabled, + gravity = getAppropriateLayoutGravity() + ) + nav(R.id.browserFragment, directions) + } } override fun getEngineMargins(): Pair { diff --git a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt index 6ad59dd71..96715d638 100644 --- a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt @@ -21,6 +21,7 @@ import mozilla.components.feature.pwa.ext.trustedOrigins import mozilla.components.feature.pwa.feature.WebAppActivityFeature import mozilla.components.feature.pwa.feature.WebAppHideToolbarFeature import mozilla.components.feature.pwa.feature.WebAppSiteControlsFeature +import mozilla.components.feature.session.TrackingProtectionUseCases import mozilla.components.feature.sitepermissions.SitePermissions import mozilla.components.lib.state.ext.consumeFrom import mozilla.components.support.base.feature.BackHandler @@ -154,15 +155,22 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), BackHandler { } override fun navToTrackingProtectionPanel(session: Session) { - val directions = - ExternalAppBrowserFragmentDirections - .actionExternalAppBrowserFragmentToTrackingProtectionPanelDialogFragment( - sessionId = session.id, - url = session.url, - trackingProtectionEnabled = session.trackerBlockingEnabled, - gravity = getAppropriateLayoutGravity() - ) - nav(R.id.externalAppBrowserFragment, directions) + val useCase = TrackingProtectionUseCases( + sessionManager = requireComponents.core.sessionManager, + engine = requireComponents.core.engine + ) + useCase.containsException(session) { contains -> + val isEnabled = session.trackerBlockingEnabled && !contains + val directions = + ExternalAppBrowserFragmentDirections + .actionExternalAppBrowserFragmentToTrackingProtectionPanelDialogFragment( + sessionId = session.id, + url = session.url, + trackingProtectionEnabled = isEnabled, + gravity = getAppropriateLayoutGravity() + ) + nav(R.id.externalAppBrowserFragment, directions) + } } override fun getEngineMargins(): Pair { diff --git a/app/src/main/java/org/mozilla/fenix/exceptions/ExceptionDomains.kt b/app/src/main/java/org/mozilla/fenix/exceptions/ExceptionDomains.kt deleted file mode 100644 index ac544b425..000000000 --- a/app/src/main/java/org/mozilla/fenix/exceptions/ExceptionDomains.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.fenix.exceptions - -import android.content.Context -import android.content.SharedPreferences -import mozilla.components.support.ktx.android.content.PreferencesHolder -import mozilla.components.support.ktx.android.content.stringPreference -import org.mozilla.fenix.components.metrics.Event -import org.mozilla.fenix.ext.metrics - -/** - * Contains functionality to manage custom domains for allow-list. - */ -class ExceptionDomains(val context: Context) : PreferencesHolder { - - override val preferences: SharedPreferences = - context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE) - - private var domains by stringPreference(KEY_DOMAINS, default = "") - - /** - * Loads the previously added/saved custom domains from preferences. - * - * @return list of custom domains - */ - fun load(): List { - if (exceptions == null) { - exceptions = domains.split(SEPARATOR).filter { it.isNotEmpty() } - } - - return exceptions.orEmpty() - } - - /** - * Saves the provided domains to preferences. - * - * @param domains list of domains - */ - fun save(domains: List) { - exceptions = domains - - this.domains = domains.joinToString(separator = SEPARATOR) - } - - /** - * Adds the provided domain to preferences. - * - * @param domain the domain to add - */ - fun add(domain: String) { - save(domains = load() + domain) - } - - /** - * Removes the provided domain from preferences. - * - * @param domains the domain to remove - */ - fun remove(domains: List) { - save(domains = load() - domains) - } - - /** - * Adds or removes the provided domain from preferences. - * - * If present, the domain will be removed. Otherwise, it will be added. - */ - fun toggle(domain: String) { - if (domain in load()) { - remove(listOf(domain)) - } else { - context.metrics.track(Event.TrackingProtectionException) - add(domain) - } - } - - companion object { - private const val PREFERENCE_NAME = "exceptions" - private const val KEY_DOMAINS = "exceptions_domains" - private const val SEPARATOR = "@<;>@" - - private var exceptions: List? = null - } -} diff --git a/app/src/main/java/org/mozilla/fenix/exceptions/ExceptionsFragment.kt b/app/src/main/java/org/mozilla/fenix/exceptions/ExceptionsFragment.kt index b56c67dd6..2a4e19525 100644 --- a/app/src/main/java/org/mozilla/fenix/exceptions/ExceptionsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/exceptions/ExceptionsFragment.kt @@ -5,29 +5,28 @@ package org.mozilla.fenix.exceptions import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment -import androidx.lifecycle.lifecycleScope import kotlinx.android.synthetic.main.fragment_exceptions.view.* -import kotlinx.coroutines.Dispatchers.IO -import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.launch +import mozilla.components.feature.session.TrackingProtectionUseCases import mozilla.components.lib.state.ext.consumeFrom import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.components.StoreProvider +import org.mozilla.fenix.ext.components import org.mozilla.fenix.settings.SupportUtils class ExceptionsFragment : Fragment() { private lateinit var exceptionsStore: ExceptionsFragmentStore private lateinit var exceptionsView: ExceptionsView private lateinit var exceptionsInteractor: ExceptionsInteractor + private lateinit var trackingProtectionUseCases: TrackingProtectionUseCases override fun onResume() { super.onResume() @@ -41,16 +40,21 @@ class ExceptionsFragment : Fragment() { savedInstanceState: Bundle? ): View? { val view = inflater.inflate(R.layout.fragment_exceptions, container, false) + trackingProtectionUseCases = TrackingProtectionUseCases( + sessionManager = view.context.components.core.sessionManager, + engine = view.context.components.core.engine + ) exceptionsStore = StoreProvider.get(this) { ExceptionsFragmentStore( ExceptionsFragmentState( - items = loadAndMapExceptions() + items = listOf() ) ) } exceptionsInteractor = ExceptionsInteractor(::openLearnMore, ::deleteOneItem, ::deleteAllItems) exceptionsView = ExceptionsView(view.exceptionsLayout, exceptionsInteractor) + reloadExceptions() return view } @@ -63,20 +67,15 @@ class ExceptionsFragment : Fragment() { } private fun deleteAllItems() { - viewLifecycleOwner.lifecycleScope.launch(IO) { - ExceptionDomains(requireContext()).run { - val domains = load() - remove(domains) - } - reloadData() - } + trackingProtectionUseCases.removeAllExceptions() + reloadExceptions() } private fun deleteOneItem(item: ExceptionsItem) { - viewLifecycleOwner.lifecycleScope.launch(IO) { - ExceptionDomains(requireContext()).remove(listOf(item.url)) - reloadData() - } + // We can't currently delete one item in this Exceptions list with a URL with the GV API + // See https://github.com/mozilla-mobile/android-components/issues/4699 + Log.e("Remove one exception", "$item") + reloadExceptions() } private fun openLearnMore() { @@ -88,18 +87,13 @@ class ExceptionsFragment : Fragment() { ) } - private fun loadAndMapExceptions(): List { - return ExceptionDomains(requireContext()).load() - .map { item -> ExceptionsItem(item) } - } - - private suspend fun reloadData() { - val items = loadAndMapExceptions() - - coroutineScope { - launch(Main) { - exceptionsStore.dispatch(ExceptionsFragmentAction.Change(items)) - } + private fun reloadExceptions() { + trackingProtectionUseCases.fetchExceptions { resultList -> + exceptionsStore.dispatch(ExceptionsFragmentAction.Change(resultList.map { + ExceptionsItem( + it + ) + })) } } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsController.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsController.kt index bcf27132a..30b8070f5 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsController.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsController.kt @@ -14,13 +14,14 @@ import kotlinx.coroutines.ObsoleteCoroutinesApi import kotlinx.coroutines.launch import mozilla.components.browser.session.Session import mozilla.components.feature.session.SessionUseCases.ReloadUrlUseCase +import mozilla.components.feature.session.TrackingProtectionUseCases import mozilla.components.feature.sitepermissions.SitePermissions import mozilla.components.feature.tabs.TabsUseCases.AddNewTabUseCase import mozilla.components.support.base.feature.OnNeedToRequestPermissions import org.mozilla.fenix.browser.BrowserFragment import org.mozilla.fenix.components.PermissionStorage -import org.mozilla.fenix.exceptions.ExceptionDomains -import org.mozilla.fenix.ext.tryGetHostFromUrl +import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.ext.metrics import org.mozilla.fenix.settings.PhoneFeature import org.mozilla.fenix.settings.quicksettings.ext.shouldBeEnabled import org.mozilla.fenix.settings.toggle @@ -36,9 +37,8 @@ interface QuickSettingsController { /** * Handles turning on/off tracking protection. * - * @param websiteUrl [String] the website URL for which to toggle tracking protection. - */ - fun handleTrackingProtectionToggled(websiteUrl: String, trackingEnabled: Boolean) + * */ + fun handleTrackingProtectionToggled(trackingEnabled: Boolean) /** * Handles showing the tracking protection settings. @@ -89,7 +89,6 @@ interface QuickSettingsController { * @param sitePermissions [SitePermissions]? list of website permissions and their status. * @param settings [Settings] application settings. * @param permissionStorage [PermissionStorage] app state for website permissions exception. - * @param trackingExceptions [ExceptionDomains] allows setting whether to allow trackers or not. * @param reload [ReloadUrlUseCase] callback allowing for reloading the current web page. * @param addNewTab [AddNewTabUseCase] callback allowing for loading a URL in a new tab. * @param requestRuntimePermissions [OnNeedToRequestPermissions] callback allowing for requesting @@ -98,6 +97,7 @@ interface QuickSettingsController { * @param displayTrackingProtection callback for when the [TrackingProtectionView] needs to be displayed. * @param displayPermissions callback for when [WebsitePermissionsView] needs to be displayed. * @param dismiss callback allowing to request this entire Fragment to be dismissed. + * @param trackingProtectionUseCases usecase allowing us to add or remove tracking protection exceptions */ @Suppress("TooManyFunctions") class DefaultQuickSettingsController( @@ -109,22 +109,28 @@ class DefaultQuickSettingsController( private var sitePermissions: SitePermissions?, private val settings: Settings, private val permissionStorage: PermissionStorage, - private val trackingExceptions: ExceptionDomains, private val reload: ReloadUrlUseCase, private val addNewTab: AddNewTabUseCase, private val requestRuntimePermissions: OnNeedToRequestPermissions = { }, private val reportSiteIssue: () -> Unit, private val displayTrackingProtection: () -> Unit, private val displayPermissions: () -> Unit, - private val dismiss: () -> Unit + private val dismiss: () -> Unit, + private val trackingProtectionUseCases: TrackingProtectionUseCases ) : QuickSettingsController { override fun handleTrackingProtectionToggled( - websiteUrl: String, trackingEnabled: Boolean ) { - val host = websiteUrl.tryGetHostFromUrl() - trackingExceptions.toggle(host) + session?.let { + if (trackingEnabled) { + trackingProtectionUseCases.removeException(it) + } else { + context.metrics.track(Event.TrackingProtectionException) + trackingProtectionUseCases.addException(it) + } + } + reload(session) quickSettingsStore.dispatch( @@ -246,13 +252,17 @@ class DefaultQuickSettingsController( return when (this) { PhoneFeature.CAMERA -> WebsitePermission.Camera( - defaultStatus, defaultVisible, defaultEnabled, defaultBlockedByAndroid) + defaultStatus, defaultVisible, defaultEnabled, defaultBlockedByAndroid + ) PhoneFeature.LOCATION -> WebsitePermission.Location( - defaultStatus, defaultVisible, defaultEnabled, defaultBlockedByAndroid) + defaultStatus, defaultVisible, defaultEnabled, defaultBlockedByAndroid + ) PhoneFeature.MICROPHONE -> WebsitePermission.Microphone( - defaultStatus, defaultVisible, defaultEnabled, defaultBlockedByAndroid) + defaultStatus, defaultVisible, defaultEnabled, defaultBlockedByAndroid + ) PhoneFeature.NOTIFICATION -> WebsitePermission.Notification( - defaultStatus, defaultVisible, defaultEnabled, defaultBlockedByAndroid) + defaultStatus, defaultVisible, defaultEnabled, defaultBlockedByAndroid + ) PhoneFeature.AUTOPLAY -> defaultWebsitePermission!! // fail-fast } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsInteractor.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsInteractor.kt index 6dc4db233..f4facb320 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsInteractor.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsInteractor.kt @@ -21,8 +21,8 @@ class QuickSettingsInteractor( controller.handleReportTrackingProblem(websiteUrl) } - override fun onProtectionToggled(websiteUrl: String, trackingEnabled: Boolean) { - controller.handleTrackingProtectionToggled(websiteUrl, trackingEnabled) + override fun onProtectionToggled(trackingEnabled: Boolean) { + controller.handleTrackingProtectionToggled(trackingEnabled) } override fun onProtectionSettingsSelected() { diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsSheetDialogFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsSheetDialogFragment.kt index 4f6978968..1e031e53e 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsSheetDialogFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsSheetDialogFragment.kt @@ -26,11 +26,11 @@ import com.google.android.material.bottomsheet.BottomSheetDialog import kotlinx.android.synthetic.main.fragment_quick_settings_dialog_sheet.* import kotlinx.android.synthetic.main.fragment_quick_settings_dialog_sheet.view.* import kotlinx.coroutines.ExperimentalCoroutinesApi +import mozilla.components.feature.session.TrackingProtectionUseCases import mozilla.components.lib.state.ext.consumeFrom import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.IntentReceiverActivity import org.mozilla.fenix.R -import org.mozilla.fenix.exceptions.ExceptionDomains import org.mozilla.fenix.ext.components import org.mozilla.fenix.settings.PhoneFeature import org.mozilla.fenix.utils.Settings @@ -50,7 +50,11 @@ class QuickSettingsSheetDialogFragment : AppCompatDialogFragment() { private lateinit var websiteTrackingProtectionView: TrackingProtectionView private lateinit var interactor: QuickSettingsInteractor private val safeArguments get() = requireNotNull(arguments) - private val promptGravity: Int by lazy { QuickSettingsSheetDialogFragmentArgs.fromBundle(safeArguments).gravity } + private val promptGravity: Int by lazy { + QuickSettingsSheetDialogFragmentArgs.fromBundle( + safeArguments + ).gravity + } override fun onCreateView( inflater: LayoutInflater, @@ -80,7 +84,6 @@ class QuickSettingsSheetDialogFragment : AppCompatDialogFragment() { sitePermissions = args.sitePermissions, settings = Settings.getInstance(context), permissionStorage = context.components.core.permissionStorage, - trackingExceptions = ExceptionDomains(context), reload = context.components.useCases.sessionUseCases.reload, addNewTab = context.components.useCases.tabsUseCases.addTab, requestRuntimePermissions = { permissions -> @@ -89,13 +92,20 @@ class QuickSettingsSheetDialogFragment : AppCompatDialogFragment() { reportSiteIssue = ::launchIntentReceiver, displayTrackingProtection = ::showTrackingProtectionView, displayPermissions = ::showPermissionsView, - dismiss = ::dismiss + dismiss = ::dismiss, + trackingProtectionUseCases = TrackingProtectionUseCases( + context.components.core.sessionManager, + context.components.core.engine + ) ) + interactor = QuickSettingsInteractor(quickSettingsController) - websiteTrackingProtectionView = TrackingProtectionView(rootView.trackingProtectionLayout, interactor) + websiteTrackingProtectionView = + TrackingProtectionView(rootView.trackingProtectionLayout, interactor) websiteInfoView = WebsiteInfoView(rootView.websiteInfoLayout) - websitePermissionsView = WebsitePermissionsView(rootView.websitePermissionsLayout, interactor) + websitePermissionsView = + WebsitePermissionsView(rootView.websitePermissionsLayout, interactor) return rootView } diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/TrackingProtectionView.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/TrackingProtectionView.kt index b169d2db7..c1e555fd8 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/TrackingProtectionView.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/TrackingProtectionView.kt @@ -26,7 +26,7 @@ interface TrackingProtectionInteractor { /** * Indicates the user want to toggle the tracking protection on / off. */ - fun onProtectionToggled(websiteUrl: String, trackingEnabled: Boolean) + fun onProtectionToggled(trackingEnabled: Boolean) /** * Indicates the user want to see all tracking protection settings. @@ -84,7 +84,7 @@ class TrackingProtectionView( trackingProtectionSwitch.isEnabled = state.isTrackingProtectionEnabledPerApp if (state.isTrackingProtectionEnabledPerApp) { trackingProtectionSwitch.setOnCheckedChangeListener { _, isChecked -> - interactor.onProtectionToggled(state.websiteUrl, isChecked) + interactor.onProtectionToggled(isChecked) } } } diff --git a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelDialogFragment.kt b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelDialogFragment.kt index 8264630ba..77b244c2f 100644 --- a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelDialogFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelDialogFragment.kt @@ -32,12 +32,10 @@ import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.components.metrics.Event -import org.mozilla.fenix.exceptions.ExceptionDomains import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.metrics import org.mozilla.fenix.ext.nav import org.mozilla.fenix.ext.requireComponents -import org.mozilla.fenix.ext.tryGetHostFromUrl class TrackingProtectionPanelDialogFragment : AppCompatDialogFragment(), BackHandler { @@ -172,12 +170,22 @@ class TrackingProtectionPanelDialogFragment : AppCompatDialogFragment(), BackHan private fun toggleTrackingProtection(isEnabled: Boolean) { context?.let { context -> - val host = url.tryGetHostFromUrl() - lifecycleScope.launch { - ExceptionDomains(context).toggle(host) - } - with(context.components) { - useCases.sessionUseCases.reload.invoke(core.sessionManager.findSessionById(sessionId)) + val useCase = TrackingProtectionUseCases( + sessionManager = context.components.core.sessionManager, + engine = context.components.core.engine + ) + val session = context.components.core.sessionManager.findSessionById(sessionId) + session?.let { + if (isEnabled) { + useCase.removeException(it) + } else { + context.metrics.track(Event.TrackingProtectionException) + useCase.addException(it) + } + + with(context.components) { + useCases.sessionUseCases.reload.invoke(session) + } } } trackingProtectionStore.dispatch(TrackingProtectionAction.TrackerBlockingChanged(isEnabled)) diff --git a/app/src/main/res/layout/exception_item.xml b/app/src/main/res/layout/exception_item.xml index 3b04d569c..9f4627325 100644 --- a/app/src/main/res/layout/exception_item.xml +++ b/app/src/main/res/layout/exception_item.xml @@ -47,6 +47,7 @@ tools:text="mozilla.org" /> (relaxed = true) private val permissionStorage = mockk(relaxed = true) - private val trackingExceptions = mockk(relaxed = true) private val reload = mockk(relaxed = true) private val addNewTab = mockk(relaxed = true) private val requestPermissions = mockk<(Array) -> Unit>(relaxed = true) @@ -61,6 +62,7 @@ class DefaultQuickSettingsControllerTest { private val displayTrackingProtection = mockk<() -> Unit>(relaxed = true) private val displayPermissions = mockk<() -> Unit>(relaxed = true) private val dismiss = mockk<() -> Unit>(relaxed = true) + private val trackingProtectionUseCases = mockk(relaxed = true) private val controller = DefaultQuickSettingsController( context = context, quickSettingsStore = store, @@ -70,32 +72,30 @@ class DefaultQuickSettingsControllerTest { sitePermissions = sitePermissions, settings = appSettings, permissionStorage = permissionStorage, - trackingExceptions = trackingExceptions, reload = reload, addNewTab = addNewTab, requestRuntimePermissions = requestPermissions, reportSiteIssue = reportIssue, displayTrackingProtection = displayTrackingProtection, displayPermissions = displayPermissions, - dismiss = dismiss + dismiss = dismiss, + trackingProtectionUseCases = trackingProtectionUseCases ) @Test fun `handleTrackingProtectionToggled should toggle tracking and reload website`() { - val testWebsiteHost = "host.com" - val websiteHost = slot() val session = slot() every { store.dispatch(any()) } returns mockk() - controller.handleTrackingProtectionToggled("https://$testWebsiteHost/page1", false) + controller.handleTrackingProtectionToggled(false) verifyOrder { - trackingExceptions.toggle(capture(websiteHost)) + trackingProtectionUseCases.addException(capture(session)) + context.metrics.track(Event.TrackingProtectionException) reload(capture(session)) } + assertAll { - assertThat(websiteHost.isCaptured).isTrue() - assertThat(websiteHost.captured).isEqualTo(testWebsiteHost) assertThat(session.isCaptured).isTrue() assertThat(session.captured).isEqualTo(browserSession) } @@ -118,7 +118,8 @@ class DefaultQuickSettingsControllerTest { @ExperimentalCoroutinesApi fun `handleReportTrackingProblem should open a report issue webpage and dismiss when in normal mode`() { val websiteWithIssuesUrl = "https://host.com/page1" - val testReportUrl = String.format(BrowserFragment.REPORT_SITE_ISSUE_URL, websiteWithIssuesUrl) + val testReportUrl = + String.format(BrowserFragment.REPORT_SITE_ISSUE_URL, websiteWithIssuesUrl) val reportUrl = slot() // `handleReportTrackingProblem` will behave differently depending on `isCustomTabSession` every { browserSession.isCustomTabSession() } returns false @@ -140,7 +141,8 @@ class DefaultQuickSettingsControllerTest { @ExperimentalCoroutinesApi fun `handleReportTrackingProblem should open a report issue in browser from custom tab and dismiss`() { val websiteWithIssuesUrl = "https://host.com/page1" - val testReportUrl = String.format(BrowserFragment.REPORT_SITE_ISSUE_URL, websiteWithIssuesUrl) + val testReportUrl = + String.format(BrowserFragment.REPORT_SITE_ISSUE_URL, websiteWithIssuesUrl) val reportUrl = slot() // `handleReportTrackingProblem` will behave differently depending on `isCustomTabSession` every { browserSession.isCustomTabSession() } returns true @@ -237,7 +239,8 @@ class DefaultQuickSettingsControllerTest { featureGranted.getCorrespondingPermission() } val permissionStatus = featureGranted.getActionLabel(context, sitePermissions, appSettings) - val permissionEnabled = featureGranted.shouldBeEnabled(context, sitePermissions, appSettings) + val permissionEnabled = + featureGranted.shouldBeEnabled(context, sitePermissions, appSettings) val action = slot() every { store.dispatch(any()) } returns mockk() @@ -249,9 +252,15 @@ class DefaultQuickSettingsControllerTest { assertAll { assertThat(action.isCaptured).isTrue() assertThat(action.captured).isInstanceOf(WebsitePermissionAction.TogglePermission::class) - assertThat((action.captured as WebsitePermissionAction.TogglePermission).websitePermission).isEqualTo(permission) - assertThat((action.captured as WebsitePermissionAction.TogglePermission).updatedStatus).isEqualTo(permissionStatus) - assertThat((action.captured as WebsitePermissionAction.TogglePermission).updatedEnabledStatus).isEqualTo(permissionEnabled) + assertThat((action.captured as WebsitePermissionAction.TogglePermission).websitePermission).isEqualTo( + permission + ) + assertThat((action.captured as WebsitePermissionAction.TogglePermission).updatedStatus).isEqualTo( + permissionStatus + ) + assertThat((action.captured as WebsitePermissionAction.TogglePermission).updatedEnabledStatus).isEqualTo( + permissionEnabled + ) } } @@ -272,24 +281,25 @@ class DefaultQuickSettingsControllerTest { @Test @ExperimentalCoroutinesApi - fun `handlePermissionsChange should store the updated permission and reload webpage`() = runBlocking { - val testPermissions = mockk() - val permissions = slot() - val session = slot() + fun `handlePermissionsChange should store the updated permission and reload webpage`() = + runBlocking { + val testPermissions = mockk() + val permissions = slot() + val session = slot() - controller.handlePermissionsChange(testPermissions) + controller.handlePermissionsChange(testPermissions) - verifyOrder { - permissionStorage.updateSitePermissions(capture(permissions)) - reload(capture(session)) + verifyOrder { + permissionStorage.updateSitePermissions(capture(permissions)) + reload(capture(session)) + } + assertAll { + assertThat(permissions.isCaptured).isTrue() + assertThat(permissions.captured).isEqualTo(testPermissions) + assertThat(session.isCaptured).isTrue() + assertThat(session.captured).isEqualTo(browserSession) + } } - assertAll { - assertThat(permissions.isCaptured).isTrue() - assertThat(permissions.captured).isEqualTo(testPermissions) - assertThat(session.isCaptured).isTrue() - assertThat(session.captured).isEqualTo(browserSession) - } - } @Test fun `WebsitePermission#getBackingFeature should return the PhoneFeature this permission is mapped from`() { diff --git a/app/src/test/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsInteractorTest.kt b/app/src/test/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsInteractorTest.kt index 9da7ea11b..ebe32e0f7 100644 --- a/app/src/test/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsInteractorTest.kt +++ b/app/src/test/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsInteractorTest.kt @@ -36,20 +36,15 @@ class QuickSettingsInteractorTest { @Test fun `onProtectionToggled should delegate the controller`() { - val websiteUrl = "https://host.com/page1" val trackingEnabled = true - val url = slot() val trackingStatus = slot() - interactor.onProtectionToggled(websiteUrl, trackingEnabled) + interactor.onProtectionToggled(trackingEnabled) verifyAll { - controller.handleTrackingProtectionToggled(capture(url), capture(trackingStatus)) + controller.handleTrackingProtectionToggled(capture(trackingStatus)) } assertAll { - assertThat(url.isCaptured).isTrue() - assertThat(url.captured).isEqualTo(websiteUrl) - assertThat(trackingStatus.isCaptured).isTrue() assertThat(trackingStatus.captured).isEqualTo(trackingEnabled) } diff --git a/docs/metrics.md b/docs/metrics.md index 50a69467a..864d4a388 100644 --- a/docs/metrics.md +++ b/docs/metrics.md @@ -141,7 +141,7 @@ The following metrics are added to the ping: | sync_auth.sign_up |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |User registered a new Firefox Account, and was signed into it |[1](https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300)||2020-03-01 | | tab.media_pause |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the pause icon on a tab from the home screen |[1](https://github.com/mozilla-mobile/fenix/pull/5266)||2020-03-01 | | tab.media_play |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the play icon on a tab from the home screen |[1](https://github.com/mozilla-mobile/fenix/pull/5266)||2020-03-01 | -| tracking_protection.etp_setting_changed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user added a tracking protection exception through the TP toggle in the panel. |[1](https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188)|
  • etp_setting: The new setting for ETP: strict, standard
|2020-03-01 | +| tracking_protection.etp_setting_changed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user changed their tracking protection level setting to either strict or standard. |[1](https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188)|
  • etp_setting: The new setting for ETP: strict, standard
|2020-03-01 | | tracking_protection.etp_settings |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened tracking protection settings through settings. |[1](https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188)||2020-03-01 | | tracking_protection.etp_shield |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the tracking protection shield icon in toolbar. |[1](https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188)||2020-03-01 | | tracking_protection.etp_tracker_list |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed into a list of categorized trackers in tracking protection panel. |[1](https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188)||2020-03-01 |