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

Commit

Permalink
For #5281 - Use TrackerLog for reporting ETP categories
Browse files Browse the repository at this point in the history
  • Loading branch information
ekager committed Sep 25, 2019
1 parent 1c9c531 commit 76ee16d
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 126 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@

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
Expand All @@ -22,60 +23,92 @@ import java.util.EnumMap
*/
class TrackerBuckets {

private var trackers = emptyList<Tracker>()
var buckets = emptyMap<TrackingProtectionCategory, List<String>>()
private var trackers = emptyList<TrackerLog>()
var buckets: Pair<Map<TrackingProtectionCategory, List<String>>, Map<TrackingProtectionCategory, List<String>>> =
Pair(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.first.isEmpty()

/**
* Returns true if there are no trackers loaded.
*/
fun allowedIsEmpty() = buckets.second.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.first[key].orEmpty() else buckets.second[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>
): Pair<Map<TrackingProtectionCategory, List<String>>, Map<TrackingProtectionCategory, List<String>>> {
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.trackingCategories -> {
map[FINGERPRINTERS] = map[FINGERPRINTERS].orEmpty() +
FINGERPRINTING in item.blockedCategories -> {
blockedMap[FINGERPRINTERS] = blockedMap[FINGERPRINTERS].orEmpty() +
item.url.tryGetHostFromUrl()
}
SOCIAL in item.trackingCategories -> {
map[SOCIAL_MEDIA_TRACKERS] = map[SOCIAL_MEDIA_TRACKERS].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()
}
AD in item.trackingCategories ||
SOCIAL in item.trackingCategories ||
ANALYTICS in item.trackingCategories -> {
map[TRACKING_CONTENT] = map[TRACKING_CONTENT].orEmpty() +
// Loaded categories
CRYPTOMINING in item.loadedCategories -> {
loadedMap[CRYPTOMINERS] = loadedMap[CRYPTOMINERS].orEmpty() +
item.url.tryGetHostFromUrl()
}
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 Pair(blockedMap, loadedMap)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ 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 org.mozilla.fenix.HomeActivity
Expand Down Expand Up @@ -92,8 +93,7 @@ class TrackingProtectionPanelDialogFragment : AppCompatDialogFragment(), BackHan
session,
url,
trackingProtectionEnabled,
session?.trackersBlocked ?: listOf(),
session?.trackersLoaded ?: listOf(),
listOf(),
TrackingProtectionState.Mode.Normal
)
)
Expand All @@ -105,6 +105,7 @@ class TrackingProtectionPanelDialogFragment : AppCompatDialogFragment(), BackHan
)
trackingProtectionView =
TrackingProtectionPanelView(view.fragment_tp, trackingProtectionInteractor)
updateTrackers()
return view
}

Expand All @@ -116,20 +117,31 @@ 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 = {}
)
}
}
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,33 @@ 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.allowedIsEmpty()
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.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()

cross_site_tracking_loaded.isGone =
bucketedLoadedTrackers[CROSS_SITE_TRACKING_COOKIES].isEmpty()
bucketedTrackers.get(CROSS_SITE_TRACKING_COOKIES, false).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 Down Expand Up @@ -148,7 +147,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
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,50 +4,35 @@

package org.mozilla.fenix.trackingprotection

import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.TrackingCategory.AD
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.TrackingCategory.FINGERPRINTING
import mozilla.components.concept.engine.content.blocking.Tracker
import mozilla.components.concept.engine.content.blocking.TrackerLog
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import org.mozilla.fenix.trackingprotection.TrackingProtectionCategory.CRYPTOMINERS
import org.mozilla.fenix.trackingprotection.TrackingProtectionCategory.FINGERPRINTERS
import org.mozilla.fenix.trackingprotection.TrackingProtectionCategory.TRACKING_CONTENT

class TrackerBucketsTest {

@Test
fun `initializes with empty map`() {
assertTrue(TrackerBuckets().isEmpty())
assertTrue(TrackerBuckets().buckets.isEmpty())
assertTrue(TrackerBuckets().buckets.first.isEmpty())
}

@Test
fun `getter accesses corresponding bucket`() {
val buckets = TrackerBuckets()
buckets.updateIfNeeded(listOf(
Tracker("http://facebook.com", listOf(FINGERPRINTING, AD)),
Tracker("https://google.com", listOf(AD)),
Tracker("https://mozilla.com")
))

assertEquals(listOf("google.com"), buckets[TRACKING_CONTENT])
assertEquals(listOf("facebook.com"), buckets[FINGERPRINTERS])
assertEquals(emptyList<String>(), buckets[CRYPTOMINERS])
}

@Test
fun `sorts trackers into bucket`() {
val buckets = TrackerBuckets()
buckets.updateIfNeeded(listOf(
Tracker("http://facebook.com", listOf(FINGERPRINTING, AD)),
Tracker("https://google.com", listOf(AD)),
Tracker("https://mozilla.com")
))

assertEquals(mapOf(
TRACKING_CONTENT to listOf("google.com"),
FINGERPRINTERS to listOf("facebook.com")
), buckets.buckets)
buckets.updateIfNeeded(
listOf(
TrackerLog("http://facebook.com", listOf(FINGERPRINTING)),
TrackerLog("https://google.com", listOf(), listOf(FINGERPRINTING)),
TrackerLog("https://mozilla.com")
)
)

assertEquals(listOf("google.com"), buckets.buckets.second[FINGERPRINTERS])
assertEquals(listOf("facebook.com"), buckets.buckets.first[FINGERPRINTERS])
assertEquals(emptyList<String>(), buckets.buckets.first[CRYPTOMINERS])
assertEquals(emptyList<String>(), buckets.buckets.second[CRYPTOMINERS])
}
}
Loading

0 comments on commit 76ee16d

Please sign in to comment.