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

For #5281 - Use TrackerLog for reporting ETP categories #5556

Merged
merged 1 commit into from
Sep 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,78 +4,115 @@

package org.mozilla.fenix.trackingprotection

import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.TrackingCategory.AD
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.TrackingCategory.ANALYTICS
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.TrackingCategory
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.TrackingCategory.CRYPTOMINING
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.TrackingCategory.FINGERPRINTING
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.TrackingCategory.SOCIAL
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.TrackingCategory.MOZILLA_SOCIAL
import mozilla.components.concept.engine.content.blocking.Tracker
import mozilla.components.concept.engine.content.blocking.TrackerLog
import org.mozilla.fenix.ext.tryGetHostFromUrl
import org.mozilla.fenix.trackingprotection.TrackingProtectionCategory.CROSS_SITE_TRACKING_COOKIES
import org.mozilla.fenix.trackingprotection.TrackingProtectionCategory.CRYPTOMINERS
import org.mozilla.fenix.trackingprotection.TrackingProtectionCategory.FINGERPRINTERS
import org.mozilla.fenix.trackingprotection.TrackingProtectionCategory.SOCIAL_MEDIA_TRACKERS
import org.mozilla.fenix.trackingprotection.TrackingProtectionCategory.TRACKING_CONTENT
import java.util.EnumMap

typealias BucketMap = Map<TrackingProtectionCategory, List<String>>

/**
* Sorts [Tracker]s into different buckets and exposes them as a map.
*/
class TrackerBuckets {

private var trackers = emptyList<Tracker>()
var buckets = emptyMap<TrackingProtectionCategory, List<String>>()
private var trackers = emptyList<TrackerLog>()

data class BucketedTrackerLog(var blockedBucketMap: BucketMap, var loadedBucketMap: BucketMap)

var buckets: BucketedTrackerLog = BucketedTrackerLog(emptyMap(), emptyMap())
private set

/**
* If [newTrackers] has changed since the last call,
* update [buckets] based on the new trackers list.
* update [buckets] based on the new tracker log list.
*/
fun updateIfNeeded(newTrackers: List<Tracker>) {
fun updateIfNeeded(newTrackers: List<TrackerLog>) {
if (newTrackers != trackers) {
trackers = newTrackers
buckets = putTrackersInBuckets(newTrackers)
}
}

/**
* Returns true if there are no trackers.
* Returns true if there are no trackers being blocked.
*/
fun isEmpty() = buckets.isEmpty()
fun blockedIsEmpty() = buckets.blockedBucketMap.isEmpty()

/**
* Returns true if there are no trackers loaded.
*/
fun loadedIsEmpty() = buckets.loadedBucketMap.isEmpty()

/**
* Gets the tracker URLs for a given category.
*/
operator fun get(key: TrackingProtectionCategory) = buckets[key].orEmpty()
operator fun get(key: TrackingProtectionCategory, blocked: Boolean) =
if (blocked) buckets.blockedBucketMap[key].orEmpty() else buckets.loadedBucketMap[key].orEmpty()

companion object {

@Suppress("ComplexMethod")
private fun putTrackersInBuckets(
list: List<Tracker>
): Map<TrackingProtectionCategory, List<String>> {
val map = EnumMap<TrackingProtectionCategory, List<String>>(TrackingProtectionCategory::class.java)
list: List<TrackerLog>
): BucketedTrackerLog {
val blockedMap =
EnumMap<TrackingProtectionCategory, List<String>>(TrackingProtectionCategory::class.java)
val loadedMap =
EnumMap<TrackingProtectionCategory, List<String>>(TrackingProtectionCategory::class.java)
for (item in list) {
when {
CRYPTOMINING in item.trackingCategories -> {
map[CRYPTOMINERS] = map[CRYPTOMINERS].orEmpty() +
// Blocked categories
item.cookiesHasBeenBlocked -> {
blockedMap[CROSS_SITE_TRACKING_COOKIES] =
blockedMap[CROSS_SITE_TRACKING_COOKIES].orEmpty() + item.url.tryGetHostFromUrl()
}
CRYPTOMINING in item.blockedCategories -> {
blockedMap[CRYPTOMINERS] = blockedMap[CRYPTOMINERS].orEmpty() +
item.url.tryGetHostFromUrl()
}
FINGERPRINTING in item.blockedCategories -> {
blockedMap[FINGERPRINTERS] = blockedMap[FINGERPRINTERS].orEmpty() +
item.url.tryGetHostFromUrl()
}
FINGERPRINTING in item.trackingCategories -> {
map[FINGERPRINTERS] = map[FINGERPRINTERS].orEmpty() +
MOZILLA_SOCIAL in item.blockedCategories -> {
blockedMap[SOCIAL_MEDIA_TRACKERS] =
blockedMap[SOCIAL_MEDIA_TRACKERS].orEmpty() +
item.url.tryGetHostFromUrl()
}
TrackingCategory.SCRIPTS_AND_SUB_RESOURCES in item.blockedCategories -> {
blockedMap[TRACKING_CONTENT] = blockedMap[TRACKING_CONTENT].orEmpty() +
item.url.tryGetHostFromUrl()
}
SOCIAL in item.trackingCategories -> {
map[SOCIAL_MEDIA_TRACKERS] = map[SOCIAL_MEDIA_TRACKERS].orEmpty() +
// Loaded categories
CRYPTOMINING in item.loadedCategories -> {
loadedMap[CRYPTOMINERS] = loadedMap[CRYPTOMINERS].orEmpty() +
item.url.tryGetHostFromUrl()
}
AD in item.trackingCategories ||
SOCIAL in item.trackingCategories ||
ANALYTICS in item.trackingCategories -> {
map[TRACKING_CONTENT] = map[TRACKING_CONTENT].orEmpty() +
FINGERPRINTING in item.loadedCategories -> {
loadedMap[FINGERPRINTERS] = loadedMap[FINGERPRINTERS].orEmpty() +
item.url.tryGetHostFromUrl()
}
MOZILLA_SOCIAL in item.loadedCategories -> {
loadedMap[SOCIAL_MEDIA_TRACKERS] =
loadedMap[SOCIAL_MEDIA_TRACKERS].orEmpty() +
item.url.tryGetHostFromUrl()
}
TrackingCategory.SCRIPTS_AND_SUB_RESOURCES in item.loadedCategories -> {
loadedMap[TRACKING_CONTENT] = loadedMap[TRACKING_CONTENT].orEmpty() +
item.url.tryGetHostFromUrl()
}
}
}
return map
return BucketedTrackerLog(blockedMap, loadedMap)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ import kotlinx.android.synthetic.main.fragment_tracking_protection.view.*
import kotlinx.coroutines.launch
import mozilla.components.browser.session.Session
import mozilla.components.concept.engine.content.blocking.Tracker
import mozilla.components.feature.session.TrackingProtectionUseCases
import mozilla.components.lib.state.ext.observe
import mozilla.components.support.base.feature.BackHandler
import mozilla.components.support.base.log.logger.Logger
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.components.StoreProvider
Expand Down Expand Up @@ -92,8 +94,7 @@ class TrackingProtectionPanelDialogFragment : AppCompatDialogFragment(), BackHan
session,
url,
trackingProtectionEnabled,
session?.trackersBlocked ?: listOf(),
session?.trackersLoaded ?: listOf(),
listOf(),
TrackingProtectionState.Mode.Normal
)
)
Expand All @@ -105,6 +106,7 @@ class TrackingProtectionPanelDialogFragment : AppCompatDialogFragment(), BackHan
)
trackingProtectionView =
TrackingProtectionPanelView(view.fragment_tp, trackingProtectionInteractor)
updateTrackers()
return view
}

Expand All @@ -116,20 +118,33 @@ class TrackingProtectionPanelDialogFragment : AppCompatDialogFragment(), BackHan
}

override fun onTrackerBlocked(session: Session, tracker: Tracker, all: List<Tracker>) {
trackingProtectionStore.dispatch(
TrackingProtectionAction.TrackerListChange(all)
)
trackingProtectionStore.dispatch(
TrackingProtectionAction.TrackerLoadedListChange(session.trackersLoaded)
)
updateTrackers()
}

override fun onTrackerLoaded(session: Session, tracker: Tracker, all: List<Tracker>) {
trackingProtectionStore.dispatch(
TrackingProtectionAction.TrackerListChange(session.trackersBlocked)
updateTrackers()
}
}

private fun updateTrackers() {
context?.let { context ->
val session =
context.components.core.sessionManager.findSessionById(sessionId) ?: return
val useCase = TrackingProtectionUseCases(
sessionManager = context.components.core.sessionManager,
engine = context.components.core.engine
)
trackingProtectionStore.dispatch(
TrackingProtectionAction.TrackerLoadedListChange(all)

useCase.fetchTrackingLogs(
session,
onSuccess = {
trackingProtectionStore.dispatch(
TrackingProtectionAction.TrackerLogChange(it)
)
},
onError = {
Logger.error("TrackingProtectionUseCases - fetchTrackingLogs onError", it)
}
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,13 @@ class TrackingProtectionPanelView(
private var mode: TrackingProtectionState.Mode = TrackingProtectionState.Mode.Normal

private var bucketedTrackers = TrackerBuckets()
private var bucketedLoadedTrackers = TrackerBuckets()

fun update(state: TrackingProtectionState) {
if (state.mode != mode) {
mode = state.mode
}

bucketedTrackers.updateIfNeeded(state.listTrackers)
bucketedLoadedTrackers.updateIfNeeded(state.listTrackersLoaded)

when (val mode = state.mode) {
is TrackingProtectionState.Mode.Normal -> setUIForNormalMode(state)
Expand All @@ -92,32 +90,31 @@ class TrackingProtectionPanelView(
normal_mode.visibility = View.VISIBLE
protection_settings.isGone = state.session?.customTabConfig != null

not_blocking_header.isGone = bucketedLoadedTrackers.isEmpty()
not_blocking_header.isGone = bucketedTrackers.loadedIsEmpty()
bindUrl(state.url)
bindTrackingProtectionInfo(state.isTrackingProtectionEnabled)
protection_settings.setOnClickListener {
interactor.selectTrackingProtectionSettings()
}

blocking_header.isGone = bucketedTrackers.isEmpty()
blocking_header.isGone = bucketedTrackers.blockedIsEmpty()
updateCategoryVisibility()
setCategoryClickListeners()
}

private fun updateCategoryVisibility() {
cross_site_tracking.isGone = bucketedTrackers[CROSS_SITE_TRACKING_COOKIES].isEmpty()
social_media_trackers.isGone = bucketedTrackers[SOCIAL_MEDIA_TRACKERS].isEmpty()
fingerprinters.isGone = bucketedTrackers[FINGERPRINTERS].isEmpty()
tracking_content.isGone = bucketedTrackers[TRACKING_CONTENT].isEmpty()
cryptominers.isGone = bucketedTrackers[CRYPTOMINERS].isEmpty()

cross_site_tracking_loaded.isGone =
bucketedLoadedTrackers[CROSS_SITE_TRACKING_COOKIES].isEmpty()
cross_site_tracking.isGone =
bucketedTrackers.get(CROSS_SITE_TRACKING_COOKIES, true).isEmpty()
social_media_trackers.isGone = bucketedTrackers.get(SOCIAL_MEDIA_TRACKERS, true).isEmpty()
fingerprinters.isGone = bucketedTrackers.get(FINGERPRINTERS, true).isEmpty()
tracking_content.isGone = bucketedTrackers.get(TRACKING_CONTENT, true).isEmpty()
cryptominers.isGone = bucketedTrackers.get(CRYPTOMINERS, true).isEmpty()

social_media_trackers_loaded.isGone =
bucketedLoadedTrackers[SOCIAL_MEDIA_TRACKERS].isEmpty()
fingerprinters_loaded.isGone = bucketedLoadedTrackers[FINGERPRINTERS].isEmpty()
tracking_content_loaded.isGone = bucketedLoadedTrackers[TRACKING_CONTENT].isEmpty()
cryptominers_loaded.isGone = bucketedLoadedTrackers[CRYPTOMINERS].isEmpty()
bucketedTrackers.get(SOCIAL_MEDIA_TRACKERS, false).isEmpty()
fingerprinters_loaded.isGone = bucketedTrackers.get(FINGERPRINTERS, false).isEmpty()
tracking_content_loaded.isGone = bucketedTrackers.get(TRACKING_CONTENT, false).isEmpty()
cryptominers_loaded.isGone = bucketedTrackers.get(CRYPTOMINERS, false).isEmpty()
}

private fun setCategoryClickListeners() {
Expand All @@ -128,7 +125,6 @@ class TrackingProtectionPanelView(
cryptominers.setOnClickListener(this)
social_media_trackers_loaded.setOnClickListener(this)
fingerprinters_loaded.setOnClickListener(this)
cross_site_tracking_loaded.setOnClickListener(this)
tracking_content_loaded.setOnClickListener(this)
cryptominers_loaded.setOnClickListener(this)
}
Expand All @@ -148,7 +144,7 @@ class TrackingProtectionPanelView(
normal_mode.visibility = View.GONE
details_mode.visibility = View.VISIBLE
category_title.text = context.getString(category.title)
blocking_text_list.text = bucketedTrackers[category].joinToString("\n")
blocking_text_list.text = bucketedTrackers.get(category, categoryBlocked).joinToString("\n")
category_description.text = context.getString(category.description)
details_blocking_header.text = context.getString(
if (categoryBlocked) {
Expand Down Expand Up @@ -195,7 +191,7 @@ class TrackingProtectionPanelView(
private fun getCategory(v: View) = when (v.id) {
R.id.social_media_trackers, R.id.social_media_trackers_loaded -> SOCIAL_MEDIA_TRACKERS
R.id.fingerprinters, R.id.fingerprinters_loaded -> FINGERPRINTERS
R.id.cross_site_tracking, R.id.cross_site_tracking_loaded -> CROSS_SITE_TRACKING_COOKIES
R.id.cross_site_tracking -> CROSS_SITE_TRACKING_COOKIES
R.id.tracking_content, R.id.tracking_content_loaded -> TRACKING_CONTENT
R.id.cryptominers, R.id.cryptominers_loaded -> CRYPTOMINERS
else -> null
Expand All @@ -207,7 +203,6 @@ class TrackingProtectionPanelView(
private fun isLoaded(v: View) = when (v.id) {
R.id.social_media_trackers_loaded,
R.id.fingerprinters_loaded,
R.id.cross_site_tracking_loaded,
R.id.tracking_content_loaded,
R.id.cryptominers_loaded -> true

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
package org.mozilla.fenix.trackingprotection

import mozilla.components.browser.session.Session
import mozilla.components.concept.engine.content.blocking.Tracker
import mozilla.components.concept.engine.content.blocking.TrackerLog
import mozilla.components.lib.state.Action
import mozilla.components.lib.state.State
import mozilla.components.lib.state.Store
Expand All @@ -27,16 +27,12 @@ sealed class TrackingProtectionAction : Action {
data class Change(
val url: String,
val isTrackingProtectionEnabled: Boolean,
val listTrackers: List<Tracker>,
val listTrackersLoaded: List<Tracker>,
val listTrackers: List<TrackerLog>,
val mode: TrackingProtectionState.Mode
) : TrackingProtectionAction()

data class UrlChange(val url: String) : TrackingProtectionAction()
data class TrackerListChange(val listTrackers: List<Tracker>) : TrackingProtectionAction()
data class TrackerLoadedListChange(val listTrackersLoaded: List<Tracker>) :
TrackingProtectionAction()

data class TrackerLogChange(val listTrackers: List<TrackerLog>) : TrackingProtectionAction()
data class TrackerBlockingChanged(val isTrackingProtectionEnabled: Boolean) :
TrackingProtectionAction()

Expand All @@ -52,16 +48,14 @@ sealed class TrackingProtectionAction : Action {
* The state for the Tracking Protection Panel
* @property url Current URL to display
* @property isTrackingProtectionEnabled Current status of tracking protection for this session (ie is an exception)
* @property listTrackers List of currently blocked Trackers
* @property listTrackersLoaded List of currently not blocked Trackers
* @property listTrackers Current Tracker Log list of blocked and loaded tracker categories
* @property mode Current Mode of TrackingProtection
*/
data class TrackingProtectionState(
val session: Session?,
val url: String,
val isTrackingProtectionEnabled: Boolean,
val listTrackers: List<Tracker>,
val listTrackersLoaded: List<Tracker>,
val listTrackers: List<TrackerLog>,
val mode: Mode
) : State {
sealed class Mode {
Expand Down Expand Up @@ -110,12 +104,7 @@ fun trackingProtectionStateReducer(
is TrackingProtectionAction.UrlChange -> state.copy(
url = action.url
)
is TrackingProtectionAction.TrackerListChange -> state.copy(
listTrackers = action.listTrackers
)
is TrackingProtectionAction.TrackerLoadedListChange -> state.copy(
listTrackersLoaded = action.listTrackersLoaded
)
is TrackingProtectionAction.TrackerLogChange -> state.copy(listTrackers = action.listTrackers)
TrackingProtectionAction.ExitDetailsMode -> state.copy(
mode = TrackingProtectionState.Mode.Normal
)
Expand Down
12 changes: 1 addition & 11 deletions app/src/main/res/layout/component_tracking_protection_panel.xml
Original file line number Diff line number Diff line change
Expand Up @@ -111,16 +111,6 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tracking_content" />

<TextView
android:id="@+id/cross_site_tracking_loaded"
style="@style/QuickSettingsText.Icon"
android:layout_width="match_parent"
android:layout_height="@dimen/tracking_protection_item_height"
android:drawableStart="@drawable/ic_cookies"
android:text="@string/etp_cookies_title"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/not_blocking_header" />

<TextView
android:id="@+id/fingerprinters_loaded"
style="@style/QuickSettingsText.Icon"
Expand All @@ -129,7 +119,7 @@
android:drawableStart="@drawable/ic_fingerprinters"
android:text="@string/etp_fingerprinters_title"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/cross_site_tracking_loaded" />
app:layout_constraintTop_toBottomOf="@id/not_blocking_header" />

<TextView
android:id="@+id/cryptominers_loaded"
Expand Down
Loading