From 69688b8b9c33723f6c60ca9fb6349314c4c7adf4 Mon Sep 17 00:00:00 2001 From: Alexandru2909 Date: Tue, 30 Aug 2022 16:54:04 +0300 Subject: [PATCH] [fenix] For https://github.com/mozilla-mobile/fenix/issues/26706 - Add sponsored urls filter in BlocklistHandler --- .../java/org/mozilla/fenix/ext/AppState.kt | 6 +- .../fenix/home/blocklist/BlocklistHandler.kt | 39 +++++++++++ .../home/blocklist/BlocklistMiddleware.kt | 18 ++--- .../home/blocklist/BlocklistHandlerTest.kt | 67 +++++++++++++++++++ .../home/blocklist/BlocklistMiddlewareTest.kt | 6 ++ 5 files changed, 124 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/ext/AppState.kt b/app/src/main/java/org/mozilla/fenix/ext/AppState.kt index a7a5774b9..e97188449 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/AppState.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/AppState.kt @@ -170,9 +170,9 @@ fun AppState.filterState(blocklistHandler: BlocklistHandler): AppState = with(blocklistHandler) { copy( recentBookmarks = recentBookmarks.filteredByBlocklist(), - recentTabs = recentTabs.filteredByBlocklist(), - recentHistory = recentHistory.filteredByBlocklist(), - recentSyncedTabState = recentSyncedTabState.filteredByBlocklist() + recentTabs = recentTabs.filteredByBlocklist().filterContile(), + recentHistory = recentHistory.filteredByBlocklist().filterContile(), + recentSyncedTabState = recentSyncedTabState.filteredByBlocklist().filterContile() ) } diff --git a/app/src/main/java/org/mozilla/fenix/home/blocklist/BlocklistHandler.kt b/app/src/main/java/org/mozilla/fenix/home/blocklist/BlocklistHandler.kt index 04c9fa6bc..c19b7816f 100644 --- a/app/src/main/java/org/mozilla/fenix/home/blocklist/BlocklistHandler.kt +++ b/app/src/main/java/org/mozilla/fenix/home/blocklist/BlocklistHandler.kt @@ -4,8 +4,10 @@ package org.mozilla.fenix.home.blocklist +import android.net.Uri import androidx.annotation.VisibleForTesting import mozilla.components.support.ktx.kotlin.sha1 +import org.mozilla.fenix.ext.containsQueryParameters import org.mozilla.fenix.home.recentbookmarks.RecentBookmark import org.mozilla.fenix.home.recentsyncedtabs.RecentSyncedTabState import org.mozilla.fenix.home.recenttabs.RecentTab @@ -16,6 +18,8 @@ import org.mozilla.fenix.utils.Settings * Class for interacting with the a blocklist stored in [settings]. * The blocklist is a set of SHA1 hashed URLs, which are stripped * of protocols and common subdomains like "www" or "mobile". + * + * Also used for filtering the sponsored URLs. */ class BlocklistHandler(private val settings: Settings) { @@ -52,6 +56,14 @@ class BlocklistHandler(private val settings: Settings) { } } + /** + * Filter a list of recent tabs from sponsored urls. + */ + @JvmName("filterContileRecentTab") + fun List.filterContile(): List = filterNot { + it is RecentTab.Tab && + Uri.parse(it.state.content.url).containsQueryParameters(settings.frecencyFilterQuery) + } /** * If the state is set to [RecentSyncedTabState.Success], filter the list of recently synced * tabs by the blocklist. If the filtered list of tabs is empty, change the state to @@ -74,6 +86,24 @@ class BlocklistHandler(private val settings: Settings) { this } + /** + * Filter a list of recently synced tabs of sponsored urls if the state is + * [RecentSyncedTabState.Success]. + */ + @JvmName("filterContileRecentSyncedTab") + fun RecentSyncedTabState.filterContile() = if (this is RecentSyncedTabState.Success) { + val filteredTabs = this.tabs.filterNot { + Uri.parse(it.url).containsQueryParameters(settings.frecencyFilterQuery) + } + if (filteredTabs.isEmpty()) { + RecentSyncedTabState.None + } else { + RecentSyncedTabState.Success(filteredTabs) + } + } else { + this + } + /** * Filter a list of recent history items by the blocklist. Requires this class to be contextually * in a scope. @@ -87,6 +117,15 @@ class BlocklistHandler(private val settings: Settings) { } } + /** + * Filter a list of recently visited history items of sponsored urls. + */ + @JvmName("filterContileRecentlyVisited") + fun List.filterContile(): List = filterNot { + it is RecentlyVisitedItem.RecentHistoryHighlight && + Uri.parse(it.url).containsQueryParameters(settings.frecencyFilterQuery) + } + private fun blocklistContainsUrl(blocklist: Set, url: String): Boolean = blocklist.any { it == url.stripAndHash() } } diff --git a/app/src/main/java/org/mozilla/fenix/home/blocklist/BlocklistMiddleware.kt b/app/src/main/java/org/mozilla/fenix/home/blocklist/BlocklistMiddleware.kt index 0249918f7..97fbbeb35 100644 --- a/app/src/main/java/org/mozilla/fenix/home/blocklist/BlocklistMiddleware.kt +++ b/app/src/main/java/org/mozilla/fenix/home/blocklist/BlocklistMiddleware.kt @@ -41,14 +41,14 @@ class BlocklistMiddleware( is AppAction.Change -> { action.copy( recentBookmarks = action.recentBookmarks.filteredByBlocklist(), - recentTabs = action.recentTabs.filteredByBlocklist(), - recentHistory = action.recentHistory.filteredByBlocklist(), - recentSyncedTabState = action.recentSyncedTabState.filteredByBlocklist() + recentTabs = action.recentTabs.filteredByBlocklist().filterContile(), + recentHistory = action.recentHistory.filteredByBlocklist().filterContile(), + recentSyncedTabState = action.recentSyncedTabState.filteredByBlocklist().filterContile() ) } is AppAction.RecentTabsChange -> { action.copy( - recentTabs = action.recentTabs.filteredByBlocklist() + recentTabs = action.recentTabs.filteredByBlocklist().filterContile() ) } is AppAction.RecentBookmarksChange -> { @@ -57,11 +57,11 @@ class BlocklistMiddleware( ) } is AppAction.RecentHistoryChange -> { - action.copy(recentHistory = action.recentHistory.filteredByBlocklist()) + action.copy(recentHistory = action.recentHistory.filteredByBlocklist().filterContile()) } is AppAction.RecentSyncedTabStateChange -> { action.copy( - state = action.state.filteredByBlocklist() + state = action.state.filteredByBlocklist().filterContile() ) } is AppAction.RemoveRecentTab -> { @@ -97,14 +97,14 @@ class BlocklistMiddleware( private fun AppState.toActionFilteringAllState(blocklistHandler: BlocklistHandler) = with(blocklistHandler) { AppAction.Change( - recentTabs = recentTabs.filteredByBlocklist(), + recentTabs = recentTabs.filteredByBlocklist().filterContile(), recentBookmarks = recentBookmarks.filteredByBlocklist(), - recentHistory = recentHistory.filteredByBlocklist(), + recentHistory = recentHistory.filteredByBlocklist().filterContile(), topSites = topSites, mode = mode, collections = collections, showCollectionPlaceholder = showCollectionPlaceholder, - recentSyncedTabState = recentSyncedTabState.filteredByBlocklist() + recentSyncedTabState = recentSyncedTabState.filteredByBlocklist().filterContile() ) } } diff --git a/app/src/test/java/org/mozilla/fenix/home/blocklist/BlocklistHandlerTest.kt b/app/src/test/java/org/mozilla/fenix/home/blocklist/BlocklistHandlerTest.kt index 66adfa5f6..ec087d31d 100644 --- a/app/src/test/java/org/mozilla/fenix/home/blocklist/BlocklistHandlerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/blocklist/BlocklistHandlerTest.kt @@ -5,14 +5,20 @@ import io.mockk.mockk import io.mockk.slot import mozilla.components.browser.state.state.ContentState import mozilla.components.browser.state.state.TabSessionState +import mozilla.components.concept.sync.DeviceType import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test +import org.junit.runner.RunWith +import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.home.recentbookmarks.RecentBookmark +import org.mozilla.fenix.home.recentsyncedtabs.RecentSyncedTab +import org.mozilla.fenix.home.recentsyncedtabs.RecentSyncedTabState import org.mozilla.fenix.home.recenttabs.RecentTab import org.mozilla.fenix.home.recentvisits.RecentlyVisitedItem import org.mozilla.fenix.utils.Settings +@RunWith(FenixRobolectricTestRunner::class) class BlocklistHandlerTest { private val mockSettings: Settings = mockk() @@ -117,4 +123,65 @@ class BlocklistHandlerTest { assertEquals(listOf(), filtered) } + + @Test + fun `GIVEN recently synced tab is a sponsored url WHEN the tabs are filtered THEN the sponsored url be filtered`() { + val blockedUrl = "test.com/?query=value" + val mockSessionState: TabSessionState = mockk() + val mockContent: ContentState = mockk() + val tabs = RecentSyncedTabState.Success( + listOf( + RecentSyncedTab( + "", + DeviceType.DESKTOP, + "title", + blockedUrl, + null + ) + ) + ) + every { mockSessionState.content } returns mockContent + every { mockContent.url } returns blockedUrl + every { mockSettings.frecencyFilterQuery } returns "query=value" + + val filtered = with(blocklistHandler) { + tabs.filterContile() + } + + assertEquals(RecentSyncedTabState.None, filtered) + } + + @Test + fun `GIVEN recently visited item is a sponsored url WHEN the tabs are filtered THEN the sponsored url be filtered`() { + val blockedUrl = "test.com/?query=value" + val mockSessionState: TabSessionState = mockk() + val mockContent: ContentState = mockk() + val tabs = listOf(RecentlyVisitedItem.RecentHistoryHighlight("title", blockedUrl)) + every { mockSessionState.content } returns mockContent + every { mockContent.url } returns blockedUrl + every { mockSettings.frecencyFilterQuery } returns "query=value" + + val filtered = with(blocklistHandler) { + tabs.filterContile() + } + + assertEquals(listOf(), filtered) + } + + @Test + fun `GIVEN recent tab is a sponsored url WHEN the tabs are filtered THEN the sponsored url be filtered`() { + val blockedUrl = "test.com/?query=value" + val mockSessionState: TabSessionState = mockk() + val mockContent: ContentState = mockk() + val tabs = listOf(RecentTab.Tab(mockSessionState)) + every { mockSessionState.content } returns mockContent + every { mockContent.url } returns blockedUrl + every { mockSettings.frecencyFilterQuery } returns "query=value" + + val filtered = with(blocklistHandler) { + tabs.filterContile() + } + + assertEquals(listOf(), filtered) + } } diff --git a/app/src/test/java/org/mozilla/fenix/home/blocklist/BlocklistMiddlewareTest.kt b/app/src/test/java/org/mozilla/fenix/home/blocklist/BlocklistMiddlewareTest.kt index 305eb93fb..212a3931c 100644 --- a/app/src/test/java/org/mozilla/fenix/home/blocklist/BlocklistMiddlewareTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/blocklist/BlocklistMiddlewareTest.kt @@ -14,15 +14,18 @@ import mozilla.components.support.test.mock import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Test +import org.junit.runner.RunWith import org.mozilla.fenix.components.AppStore import org.mozilla.fenix.components.appstate.AppAction import org.mozilla.fenix.components.appstate.AppState +import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.home.recentbookmarks.RecentBookmark import org.mozilla.fenix.home.recentsyncedtabs.RecentSyncedTab import org.mozilla.fenix.home.recentsyncedtabs.RecentSyncedTabState import org.mozilla.fenix.home.recenttabs.RecentTab import org.mozilla.fenix.utils.Settings +@RunWith(FenixRobolectricTestRunner::class) class BlocklistMiddlewareTest { private val mockSettings: Settings = mockk() private val blocklistHandler = BlocklistHandler(mockSettings) @@ -178,6 +181,7 @@ class BlocklistMiddlewareTest { listOf(RecentTab.Tab(createTab(url = blockedUrl)), unblockedRecentTab) every { mockSettings.homescreenBlocklist } returns setOf(blockedUrl.stripAndHash()) + every { mockSettings.frecencyFilterQuery } returns "" val middleware = BlocklistMiddleware(blocklistHandler) val store = AppStore( AppState(), @@ -328,6 +332,7 @@ class BlocklistMiddlewareTest { ) every { mockSettings.homescreenBlocklist } returns setOf(blockedHost.stripAndHash()) + every { mockSettings.frecencyFilterQuery } returns "" val middleware = BlocklistMiddleware(blocklistHandler) val store = AppStore( AppState(), @@ -435,6 +440,7 @@ class BlocklistMiddlewareTest { val updateSlot = slot>() every { mockSettings.homescreenBlocklist = capture(updateSlot) } returns Unit every { mockSettings.homescreenBlocklist } returns setOf(tabUrls[0].stripAndHash()) + every { mockSettings.frecencyFilterQuery } returns "" store.dispatch( AppAction.RemoveRecentSyncedTab(