Use new API for ETP Exceptions
parent
ca8ba57635
commit
18c0525ff6
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<Int, Int> {
|
||||
|
|
|
@ -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<Int, Int> {
|
||||
|
|
|
@ -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<String> {
|
||||
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<String>) {
|
||||
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<String>) {
|
||||
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<String>? = null
|
||||
}
|
||||
}
|
|
@ -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<ExceptionsItem> {
|
||||
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
|
||||
)
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
tools:text="mozilla.org" />
|
||||
|
||||
<ImageButton
|
||||
android:visibility="gone"
|
||||
android:id="@+id/delete_exception"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
|
@ -24,6 +24,7 @@ import kotlinx.coroutines.ObsoleteCoroutinesApi
|
|||
import kotlinx.coroutines.runBlocking
|
||||
import mozilla.components.browser.session.Session
|
||||
import mozilla.components.feature.session.SessionUseCases
|
||||
import mozilla.components.feature.session.TrackingProtectionUseCases
|
||||
import mozilla.components.feature.sitepermissions.SitePermissions
|
||||
import mozilla.components.feature.sitepermissions.SitePermissions.Status.NO_DECISION
|
||||
import mozilla.components.feature.tabs.TabsUseCases
|
||||
|
@ -33,7 +34,8 @@ import org.junit.runner.RunWith
|
|||
import org.mozilla.fenix.TestApplication
|
||||
import org.mozilla.fenix.browser.BrowserFragment
|
||||
import org.mozilla.fenix.components.PermissionStorage
|
||||
import org.mozilla.fenix.exceptions.ExceptionDomains
|
||||
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
|
||||
|
@ -53,7 +55,6 @@ class DefaultQuickSettingsControllerTest {
|
|||
private val sitePermissions = SitePermissions(origin = "", savedAt = 123)
|
||||
private val appSettings = mockk<Settings>(relaxed = true)
|
||||
private val permissionStorage = mockk<PermissionStorage>(relaxed = true)
|
||||
private val trackingExceptions = mockk<ExceptionDomains>(relaxed = true)
|
||||
private val reload = mockk<SessionUseCases.ReloadUrlUseCase>(relaxed = true)
|
||||
private val addNewTab = mockk<TabsUseCases.AddNewTabUseCase>(relaxed = true)
|
||||
private val requestPermissions = mockk<(Array<String>) -> 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<TrackingProtectionUseCases>(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<String>()
|
||||
val session = slot<Session>()
|
||||
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<String>()
|
||||
// `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<String>()
|
||||
// `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<QuickSettingsFragmentAction>()
|
||||
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<SitePermissions>()
|
||||
val permissions = slot<SitePermissions>()
|
||||
val session = slot<Session>()
|
||||
fun `handlePermissionsChange should store the updated permission and reload webpage`() =
|
||||
runBlocking {
|
||||
val testPermissions = mockk<SitePermissions>()
|
||||
val permissions = slot<SitePermissions>()
|
||||
val session = slot<Session>()
|
||||
|
||||
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`() {
|
||||
|
|
|
@ -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<String>()
|
||||
val trackingStatus = slot<Boolean>()
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -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)|<ul><li>etp_setting: The new setting for ETP: strict, standard</li></ul>|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)|<ul><li>etp_setting: The new setting for ETP: strict, standard</li></ul>|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 |
|
||||
|
|
Loading…
Reference in New Issue