Skip to content

Commit bc1408f

Browse files
committed
Add plugins at the browser level
1 parent f2c44d9 commit bc1408f

File tree

11 files changed

+128
-67
lines changed

11 files changed

+128
-67
lines changed

app/src/androidTest/java/com/duckduckgo/app/browser/BrowserWebViewClientTest.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,7 +1342,7 @@ class BrowserWebViewClientTest {
13421342
const val EXAMPLE_URL = "https://example.com"
13431343
}
13441344

1345-
class FakeWebMessagingPlugin : WebMessagingPlugin {
1345+
class FakeWebMessagingPlugin : WebMessagingPlugin.WebMessaging {
13461346
var registered = false
13471347
private set
13481348

@@ -1367,10 +1367,12 @@ class BrowserWebViewClientTest {
13671367
get() = "test"
13681368
}
13691369

1370-
class FakeWebMessagingPluginPoint : PluginPoint<WebMessagingPlugin> {
1370+
class FakeWebMessagingPluginPoint : PluginPoint<WebMessaging> {
13711371
val plugin = FakeWebMessagingPlugin()
13721372

1373-
override fun getPlugins(): Collection<WebMessagingPlugin> = listOf(plugin)
1373+
override fun getPlugins(): Collection<WebMessaging> {
1374+
return listOf(plugin)
1375+
}
13741376
}
13751377

13761378
class FakePostMessageWrapperPlugin : PostMessageWrapperPlugin {

app/src/main/java/com/duckduckgo/app/browser/BrowserWebViewClient.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ import com.duckduckgo.autoconsent.api.Autoconsent
6666
import com.duckduckgo.autofill.api.BrowserAutofill
6767
import com.duckduckgo.autofill.api.InternalTestUserChecker
6868
import com.duckduckgo.browser.api.JsInjectorPlugin
69+
import com.duckduckgo.browser.api.WebMessagingBrowserPlugin
6970
import com.duckduckgo.common.utils.AppUrl.ParamKey.QUERY
7071
import com.duckduckgo.common.utils.CurrentTimeProvider
7172
import com.duckduckgo.common.utils.DispatcherProvider
@@ -81,7 +82,6 @@ import com.duckduckgo.duckplayer.impl.DUCK_PLAYER_OPEN_IN_YOUTUBE_PATH
8182
import com.duckduckgo.history.api.NavigationHistory
8283
import com.duckduckgo.js.messaging.api.PostMessageWrapperPlugin
8384
import com.duckduckgo.js.messaging.api.SubscriptionEventData
84-
import com.duckduckgo.js.messaging.api.WebMessagingPlugin
8585
import com.duckduckgo.js.messaging.api.WebViewCompatMessageCallback
8686
import com.duckduckgo.malicioussiteprotection.api.MaliciousSiteProtection.Feed
8787
import com.duckduckgo.privacy.config.api.AmpLinks
@@ -130,7 +130,7 @@ class BrowserWebViewClient @Inject constructor(
130130
private val androidFeaturesHeaderPlugin: AndroidFeaturesHeaderPlugin,
131131
private val duckChat: DuckChat,
132132
private val contentScopeExperiments: ContentScopeExperiments,
133-
private val webMessagingPlugins: PluginPoint<WebMessagingPlugin>,
133+
private val webMessagingPlugins: PluginPoint<WebMessagingBrowserPlugin>,
134134
private val postMessageWrapperPlugins: PluginPoint<PostMessageWrapperPlugin>,
135135
) : WebViewClient() {
136136
var webViewClientListener: WebViewClientListener? = null
@@ -479,7 +479,7 @@ class BrowserWebViewClient @Inject constructor(
479479

480480
callback?.let {
481481
webMessagingPlugins.getPlugins().forEach { plugin ->
482-
plugin.register(callback, webView)
482+
plugin.webMessaging().register(callback, webView)
483483
}
484484
}
485485
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright (c) 2025 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.app.browser.messaging
18+
19+
import com.duckduckgo.browser.api.WebMessagingBrowserPlugin
20+
import com.duckduckgo.contentscopescripts.impl.messaging.ContentScopeScriptsWebMessaging
21+
import com.duckduckgo.di.scopes.FragmentScope
22+
import com.duckduckgo.js.messaging.api.WebMessaging
23+
import com.squareup.anvil.annotations.ContributesBinding
24+
import com.squareup.anvil.annotations.ContributesMultibinding
25+
import dagger.SingleInstanceIn
26+
import javax.inject.Inject
27+
import javax.inject.Named
28+
29+
@Named("contentScopeScripts")
30+
@SingleInstanceIn(FragmentScope::class)
31+
@ContributesBinding(FragmentScope::class)
32+
@ContributesMultibinding(scope = FragmentScope::class, ignoreQualifier = true)
33+
class ContentScopeScriptsWebMessagingBrowserPlugin @Inject constructor(
34+
private val contentScopeScriptsWebMessaging: ContentScopeScriptsWebMessaging,
35+
) : WebMessagingBrowserPlugin {
36+
override fun webMessaging(): WebMessaging {
37+
return contentScopeScriptsWebMessaging
38+
}
39+
}

app/src/main/java/com/duckduckgo/app/plugins/WebMessagingPluginPoint.kt renamed to app/src/main/java/com/duckduckgo/app/plugins/WebMessagingBrowserPluginPoint.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@
1717
package com.duckduckgo.app.plugins
1818

1919
import com.duckduckgo.anvil.annotations.ContributesPluginPoint
20+
import com.duckduckgo.browser.api.WebMessagingBrowserPlugin
2021
import com.duckduckgo.di.scopes.AppScope
21-
import com.duckduckgo.js.messaging.api.WebMessagingPlugin
2222

2323
@ContributesPluginPoint(
2424
scope = AppScope::class,
25-
boundType = WebMessagingPlugin::class,
25+
boundType = WebMessagingBrowserPlugin::class,
2626
)
2727
@Suppress("unused")
28-
interface UnusedWebMessagingPluginPoint
28+
interface UnusedWebMessagingBrowserPluginPoint
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright (c) 2025 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.browser.api
18+
19+
import com.duckduckgo.js.messaging.api.WebMessaging
20+
21+
interface WebMessagingBrowserPlugin {
22+
fun webMessaging(): WebMessaging
23+
}

content-scope-scripts/content-scope-scripts-impl/src/main/java/com/duckduckgo/contentscopescripts/impl/messaging/ContentScopeScriptsPostMessageWrapperPlugin.kt

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import com.duckduckgo.js.messaging.api.JsMessageHelper
2626
import com.duckduckgo.js.messaging.api.PostMessageWrapperPlugin
2727
import com.duckduckgo.js.messaging.api.SubscriptionEvent
2828
import com.duckduckgo.js.messaging.api.SubscriptionEventData
29-
import com.duckduckgo.js.messaging.api.WebMessagingPlugin
29+
import com.duckduckgo.js.messaging.api.WebMessaging
3030
import com.squareup.anvil.annotations.ContributesMultibinding
3131
import kotlinx.coroutines.CoroutineScope
3232
import kotlinx.coroutines.launch
@@ -35,7 +35,7 @@ import javax.inject.Named
3535

3636
@ContributesMultibinding(FragmentScope::class)
3737
class ContentScopeScriptsPostMessageWrapperPlugin @Inject constructor(
38-
@Named("contentScopeScripts") private val webMessagingPlugin: WebMessagingPlugin,
38+
@Named("contentScopeScripts") private val webMessaging: WebMessaging,
3939
private val jsMessageHelper: JsMessageHelper,
4040
private val coreContentScopeScripts: CoreContentScopeScripts,
4141
private val webViewCompatContentScopeScripts: WebViewCompatContentScopeScripts,
@@ -48,16 +48,15 @@ class ContentScopeScriptsPostMessageWrapperPlugin @Inject constructor(
4848
) {
4949
coroutineScope.launch {
5050
if (webViewCompatContentScopeScripts.isEnabled()) {
51-
webMessagingPlugin.postMessage(webView, message)
51+
webMessaging.postMessage(webView, message)
5252
} else {
5353
jsMessageHelper.sendSubscriptionEvent(
54-
subscriptionEvent =
55-
SubscriptionEvent(
56-
context = webMessagingPlugin.context,
57-
featureName = message.featureName,
58-
subscriptionName = message.subscriptionName,
59-
params = message.params,
60-
),
54+
subscriptionEvent = SubscriptionEvent(
55+
context = webMessaging.context,
56+
featureName = message.featureName,
57+
subscriptionName = message.subscriptionName,
58+
params = message.params,
59+
),
6160
callbackName = coreContentScopeScripts.callbackName,
6261
secret = coreContentScopeScripts.secret,
6362
webView = webView,
@@ -67,5 +66,5 @@ class ContentScopeScriptsPostMessageWrapperPlugin @Inject constructor(
6766
}
6867

6968
override val context: String
70-
get() = webMessagingPlugin.context
69+
get() = webMessaging.context
7170
}
Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,27 +21,25 @@ import com.duckduckgo.contentscopescripts.api.WebViewCompatContentScopeJsMessage
2121
import com.duckduckgo.contentscopescripts.impl.WebViewCompatContentScopeScripts
2222
import com.duckduckgo.di.scopes.FragmentScope
2323
import com.duckduckgo.js.messaging.api.GlobalJsMessageHandler
24-
import com.duckduckgo.js.messaging.api.WebMessagingPlugin
25-
import com.duckduckgo.js.messaging.api.WebMessagingPluginDelegate
26-
import com.duckduckgo.js.messaging.api.WebMessagingPluginStrategy
24+
import com.duckduckgo.js.messaging.api.WebMessaging
25+
import com.duckduckgo.js.messaging.api.WebMessagingDelegate
26+
import com.duckduckgo.js.messaging.api.WebMessagingStrategy
2727
import com.duckduckgo.js.messaging.api.WebViewCompatMessageHandler
2828
import com.squareup.anvil.annotations.ContributesBinding
29-
import com.squareup.anvil.annotations.ContributesMultibinding
3029
import dagger.SingleInstanceIn
3130
import javax.inject.Inject
3231
import javax.inject.Named
3332

3433
@Named("contentScopeScripts")
3534
@SingleInstanceIn(FragmentScope::class)
3635
@ContributesBinding(FragmentScope::class)
37-
@ContributesMultibinding(scope = FragmentScope::class, ignoreQualifier = true)
38-
class ContentScopeScriptsWebMessagingPlugin @Inject constructor(
36+
class ContentScopeScriptsWebMessaging @Inject constructor(
3937
handlers: PluginPoint<WebViewCompatContentScopeJsMessageHandlersPlugin>,
4038
globalHandlers: PluginPoint<GlobalContentScopeJsMessageHandlersPlugin>,
4139
webViewCompatContentScopeScripts: WebViewCompatContentScopeScripts,
42-
webMessagingPluginDelegate: WebMessagingPluginDelegate,
43-
) : WebMessagingPlugin by webMessagingPluginDelegate.createPlugin(
44-
object : WebMessagingPluginStrategy {
40+
webMessagingDelegate: WebMessagingDelegate,
41+
) : WebMessaging by webMessagingDelegate.createPlugin(
42+
object : WebMessagingStrategy {
4543
override val context: String = "contentScopeScripts"
4644
override val allowedDomains: Set<String> = setOf("*")
4745
override val objectName: String

content-scope-scripts/content-scope-scripts-impl/src/test/java/com/duckduckgo/contentscopescripts/impl/messaging/ContentScopeScriptsPostMessageWrapperPluginTest.kt

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import com.duckduckgo.contentscopescripts.impl.WebViewCompatContentScopeScripts
77
import com.duckduckgo.js.messaging.api.JsMessageHelper
88
import com.duckduckgo.js.messaging.api.SubscriptionEvent
99
import com.duckduckgo.js.messaging.api.SubscriptionEventData
10-
import com.duckduckgo.js.messaging.api.WebMessagingPlugin
10+
import com.duckduckgo.js.messaging.api.WebMessaging
1111
import kotlinx.coroutines.test.runTest
1212
import org.json.JSONObject
1313
import org.junit.Before
@@ -22,7 +22,7 @@ class ContentScopeScriptsPostMessageWrapperPluginTest {
2222
@get:Rule
2323
var coroutineRule = CoroutineTestRule()
2424

25-
private val mockWebMessagingPlugin: WebMessagingPlugin = mock()
25+
private val mockWebMessaging: WebMessaging = mock()
2626
private val mockJsHelper: JsMessageHelper = mock()
2727
private val mockCoreContentScopeScripts: CoreContentScopeScripts = mock()
2828
private val mockWebViewCompatContentScopeScripts: WebViewCompatContentScopeScripts = mock()
@@ -42,20 +42,19 @@ class ContentScopeScriptsPostMessageWrapperPluginTest {
4242
params = mockJsonObject,
4343
)
4444

45-
val testee =
46-
ContentScopeScriptsPostMessageWrapperPlugin(
47-
webMessagingPlugin = mockWebMessagingPlugin,
48-
jsMessageHelper = mockJsHelper,
49-
coreContentScopeScripts = mockCoreContentScopeScripts,
50-
webViewCompatContentScopeScripts = mockWebViewCompatContentScopeScripts,
51-
coroutineScope = coroutineRule.testScope,
52-
)
45+
val testee = ContentScopeScriptsPostMessageWrapperPlugin(
46+
webMessaging = mockWebMessaging,
47+
jsMessageHelper = mockJsHelper,
48+
coreContentScopeScripts = mockCoreContentScopeScripts,
49+
webViewCompatContentScopeScripts = mockWebViewCompatContentScopeScripts,
50+
coroutineScope = coroutineRule.testScope,
51+
)
5352

5453
@Before
5554
fun setup() {
5655
whenever(mockCoreContentScopeScripts.callbackName).thenReturn("callbackName")
5756
whenever(mockCoreContentScopeScripts.secret).thenReturn("secret")
58-
whenever(mockWebMessagingPlugin.context).thenReturn("contentScopeScripts")
57+
whenever(mockWebMessaging.context).thenReturn("contentScopeScripts")
5958
whenever(mockJsonObject.toString()).thenReturn("{}")
6059
}
6160

@@ -66,8 +65,8 @@ class ContentScopeScriptsPostMessageWrapperPluginTest {
6665

6766
testee.postMessage(subscriptionEventData, mockWebView)
6867

69-
verify(mockWebMessagingPlugin).postMessage(mockWebView, subscriptionEventData)
70-
}
68+
verify(mockWebMessaging).postMessage(mockWebView, subscriptionEventData)
69+
}
7170

7271
@Test
7372
fun whenWebViewCompatContentScopeScriptsIsNotEnabledThenPostMessageToContentScopeScriptsJsMessaging() =
Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ import org.mockito.kotlin.verify
4646
import org.mockito.kotlin.whenever
4747

4848
@RunWith(AndroidJUnit4::class)
49-
class ContentScopeScriptsWebMessagingPluginTest {
49+
class ContentScopeScriptsWebMessagingTest {
50+
5051
@get:Rule
5152
val coroutineRule = CoroutineTestRule()
5253

@@ -56,7 +57,7 @@ class ContentScopeScriptsWebMessagingPluginTest {
5657
private val globalHandlers: PluginPoint<GlobalContentScopeJsMessageHandlersPlugin> = FakeGlobalHandlersPluginPoint()
5758
private val mockWebViewCompatWrapper: WebViewCompatWrapper = mock()
5859
private val mockWebView: WebView = mock()
59-
private lateinit var testee: ContentScopeScriptsWebMessagingPlugin
60+
private lateinit var testee: ContentScopeScriptsWebMessaging
6061

6162
private class FakePluginPoint : PluginPoint<WebViewCompatContentScopeJsMessageHandlersPlugin> {
6263
override fun getPlugins(): Collection<WebViewCompatContentScopeJsMessageHandlersPlugin> = listOf(FakePlugin())
@@ -86,17 +87,15 @@ class ContentScopeScriptsWebMessagingPluginTest {
8687
}
8788

8889
@Before
89-
fun setUp() =
90-
runTest {
91-
whenever(webViewCompatContentScopeScripts.isEnabled()).thenReturn(true)
92-
testee =
93-
ContentScopeScriptsWebMessagingPlugin(
94-
handlers = handlers,
95-
globalHandlers = globalHandlers,
96-
webViewCompatContentScopeScripts = webViewCompatContentScopeScripts,
97-
webViewCompatWrapper = mockWebViewCompatWrapper,
98-
)
99-
}
90+
fun setUp() = runTest {
91+
whenever(webViewCompatContentScopeScripts.isEnabled()).thenReturn(true)
92+
testee = ContentScopeScriptsWebMessaging(
93+
handlers = handlers,
94+
globalHandlers = globalHandlers,
95+
webViewCompatContentScopeScripts = webViewCompatContentScopeScripts,
96+
webViewCompatWrapper = mockWebViewCompatWrapper,
97+
)
98+
}
10099

101100
@Test
102101
fun `when process and message can be handled then execute callback`() =
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ package com.duckduckgo.js.messaging.api
1818

1919
import android.webkit.WebView
2020

21-
interface WebMessagingPlugin {
21+
interface WebMessaging {
2222
suspend fun register(
2323
jsMessageCallback: WebViewCompatMessageCallback,
2424
webView: WebView,
@@ -34,21 +34,21 @@ interface WebMessagingPlugin {
3434
val context: String
3535
}
3636

37-
interface WebMessagingPluginDelegate {
37+
interface WebMessagingDelegate {
3838

3939
/**
40-
* Creates a [WebMessagingPlugin] implementation with the given [WebMessagingPluginStrategy].
40+
* Creates a [WebMessaging] implementation with the given [WebMessagingStrategy].
4141
* @param strategy the strategy to use for web messaging behavior
42-
* @return [WebMessagingPlugin] implementation
42+
* @return [WebMessaging] implementation
4343
*/
44-
fun createPlugin(strategy: WebMessagingPluginStrategy): WebMessagingPlugin
44+
fun createPlugin(strategy: WebMessagingStrategy): WebMessaging
4545
}
4646

4747
/**
4848
* Strategy interface for web messaging logic.
4949
* Allows different implementations to provide their own behavior.
5050
*/
51-
interface WebMessagingPluginStrategy {
51+
interface WebMessagingStrategy {
5252
val context: String
5353
val allowedDomains: Set<String>
5454
val objectName: String

0 commit comments

Comments
 (0)