Skip to content

Commit cfb68de

Browse files
committed
Extract matcher
1 parent 3689974 commit cfb68de

5 files changed

Lines changed: 59 additions & 18 deletions

File tree

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (c) 2026 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.adblocking.impl
18+
19+
import androidx.core.net.toUri
20+
import com.duckduckgo.app.browser.Domain
21+
import com.duckduckgo.app.browser.UriString
22+
import com.duckduckgo.di.scopes.AppScope
23+
import dagger.SingleInstanceIn
24+
import javax.inject.Inject
25+
26+
/**
27+
* Single source of truth for the domains the ad-blocking extension operates on. A subdomain
28+
* match means `youtube.com` also covers `m.youtube.com`, `www.youtube.com`, etc.
29+
*/
30+
@SingleInstanceIn(AppScope::class)
31+
class AdBlockingExtensionDomainMatcher @Inject constructor() {
32+
33+
fun matches(url: String?): Boolean {
34+
val uri = url?.toUri() ?: return false
35+
return DOMAINS.any { UriString.sameOrSubdomain(uri, it) }
36+
}
37+
38+
private companion object {
39+
val DOMAINS = listOf(
40+
Domain("youtube.com"),
41+
Domain("youtube-nocookie.com"),
42+
)
43+
}
44+
}

ad-blocking/ad-blocking-impl/src/main/java/com/duckduckgo/adblocking/impl/AdBlockingExtensionJsInjectorPlugin.kt

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,7 @@ package com.duckduckgo.adblocking.impl
1818

1919
import android.webkit.WebView
2020
import androidx.annotation.UiThread
21-
import androidx.core.net.toUri
2221
import com.duckduckgo.adblocking.impl.domain.AdBlockingStatusChecker
23-
import com.duckduckgo.app.browser.Domain
24-
import com.duckduckgo.app.browser.UriString
2522
import com.duckduckgo.app.di.AppCoroutineScope
2623
import com.duckduckgo.app.global.model.Site
2724
import com.duckduckgo.browser.api.JsInjectorPlugin
@@ -43,6 +40,7 @@ class AdBlockingExtensionJsInjectorPlugin @Inject constructor(
4340
private val statusChecker: AdBlockingStatusChecker,
4441
repository: AdBlockingExtensionRepository,
4542
private val contingencyMessageHandler: ContingencyMessageHandler,
43+
private val domainMatcher: AdBlockingExtensionDomainMatcher,
4644
@AppCoroutineScope appScope: CoroutineScope,
4745
) : JsInjectorPlugin {
4846

@@ -62,8 +60,7 @@ class AdBlockingExtensionJsInjectorPlugin @Inject constructor(
6260
logcat { "Status checker rejected injection, skipping" }
6361
return
6462
}
65-
val uri = url?.toUri() ?: return
66-
if (domains.none { UriString.sameOrSubdomain(uri, it) }) {
63+
if (!domainMatcher.matches(url)) {
6764
logcat { "No domains matching, skipping" }
6865
return
6966
}
@@ -86,9 +83,4 @@ class AdBlockingExtensionJsInjectorPlugin @Inject constructor(
8683
.takeUnless { it.isEmpty() }
8784
?.sortedBy { it.name }
8885
?.joinToString(separator = "\n") { it.content }
89-
90-
private val domains = listOf(
91-
Domain("youtube.com"),
92-
Domain("youtube-nocookie.com"),
93-
)
9486
}

ad-blocking/ad-blocking-impl/src/main/java/com/duckduckgo/adblocking/impl/ContingencyMessageHandler.kt

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,9 @@ package com.duckduckgo.adblocking.impl
1818

1919
import android.webkit.WebView
2020
import androidx.annotation.UiThread
21-
import androidx.core.net.toUri
2221
import androidx.lifecycle.LifecycleOwner
23-
import com.duckduckgo.adblocking.api.duckplayer.YOUTUBE_HOST
24-
import com.duckduckgo.adblocking.api.duckplayer.YOUTUBE_MOBILE_HOST
2522
import com.duckduckgo.adblocking.impl.remoteconfig.AdBlockingExtensionFeature
2623
import com.duckduckgo.adblocking.impl.remoteconfig.ContingencyMessageStore
27-
import com.duckduckgo.app.browser.UriString
2824
import com.duckduckgo.app.di.AppCoroutineScope
2925
import com.duckduckgo.app.lifecycle.MainProcessLifecycleObserver
3026
import com.duckduckgo.common.utils.DispatcherProvider
@@ -50,6 +46,7 @@ class RealContingencyMessageHandler @Inject constructor(
5046
private val feature: AdBlockingExtensionFeature,
5147
private val store: ContingencyMessageStore,
5248
private val view: ContingencyMessageView,
49+
private val domainMatcher: AdBlockingExtensionDomainMatcher,
5350
@AppCoroutineScope private val appScope: CoroutineScope,
5451
private val dispatchers: DispatcherProvider,
5552
) : ContingencyMessageHandler, MainProcessLifecycleObserver {
@@ -86,10 +83,8 @@ class RealContingencyMessageHandler @Inject constructor(
8683
val uxImprovements = feature.adBlockingUXImprovements().isEnabled()
8784
val contingency = feature.enableContingencyMode().isEnabled()
8885
val shown = shownInSession || store.shown.value
89-
val uri = url?.toUri()
90-
val isYouTube = uri != null &&
91-
(UriString.sameOrSubdomain(uri, YOUTUBE_HOST) || UriString.sameOrSubdomain(uri, YOUTUBE_MOBILE_HOST))
92-
val result = uxImprovements && contingency && isYouTube && !shown
86+
val isExtensionDomain = domainMatcher.matches(url)
87+
val result = uxImprovements && contingency && isExtensionDomain && !shown
9388
return result
9489
}
9590
}

ad-blocking/ad-blocking-impl/src/test/java/com/duckduckgo/adblocking/impl/AdBlockingExtensionJsInjectorPluginTest.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class AdBlockingExtensionJsInjectorPluginTest {
6666
statusChecker = statusChecker,
6767
repository = repository,
6868
contingencyMessageHandler = contingencyMessageHandler,
69+
domainMatcher = AdBlockingExtensionDomainMatcher(),
6970
appScope = testScope,
7071
)
7172
}

ad-blocking/ad-blocking-impl/src/test/java/com/duckduckgo/adblocking/impl/RealContingencyMessageHandlerTest.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ class RealContingencyMessageHandlerTest {
6565
feature = feature,
6666
store = store,
6767
view = view,
68+
domainMatcher = AdBlockingExtensionDomainMatcher(),
6869
appScope = coroutineRule.testScope,
6970
dispatchers = coroutineRule.testDispatcherProvider,
7071
)
@@ -88,6 +89,13 @@ class RealContingencyMessageHandlerTest {
8889
assertTrue(handler.shouldShow("https://m.youtube.com/watch?v=abc"))
8990
}
9091

92+
@Test
93+
fun whenYouTubeNoCookieThenShouldShow() {
94+
setToggles(uxImprovements = true, contingency = true)
95+
96+
assertTrue(handler.shouldShow("https://youtube-nocookie.com/watch?v=abc"))
97+
}
98+
9199
@Test
92100
fun whenUxImprovementsDisabledThenShouldNotShow() {
93101
setToggles(uxImprovements = false, contingency = true)
@@ -207,6 +215,7 @@ class RealContingencyMessageHandlerTest {
207215
feature = feature,
208216
store = store,
209217
view = view,
218+
domainMatcher = AdBlockingExtensionDomainMatcher(),
210219
appScope = coroutineRule.testScope,
211220
dispatchers = coroutineRule.testDispatcherProvider,
212221
)

0 commit comments

Comments
 (0)