Skip to content

Commit 14b6b86

Browse files
committed
Add plugins at the browser level
1 parent 9379e3c commit 14b6b86

File tree

11 files changed

+132
-65
lines changed

11 files changed

+132
-65
lines changed

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

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ import com.duckduckgo.brokensite.api.BrokenSitePrompt
210210
import com.duckduckgo.brokensite.api.RefreshPattern
211211
import com.duckduckgo.browser.api.AddDocumentStartJavaScriptBrowserPlugin
212212
import com.duckduckgo.browser.api.UserBrowserProperties
213+
import com.duckduckgo.browser.api.WebMessagingBrowserPlugin
213214
import com.duckduckgo.browser.api.autocomplete.AutoComplete
214215
import com.duckduckgo.browser.api.autocomplete.AutoComplete.AutoCompleteSuggestion.AutoCompleteDefaultSuggestion
215216
import com.duckduckgo.browser.api.autocomplete.AutoComplete.AutoCompleteSuggestion.AutoCompleteHistoryRelatedSuggestion.AutoCompleteHistorySearchSuggestion
@@ -260,7 +261,7 @@ import com.duckduckgo.js.messaging.api.AddDocumentStartJavaScript
260261
import com.duckduckgo.js.messaging.api.JsCallbackData
261262
import com.duckduckgo.js.messaging.api.PostMessageWrapperPlugin
262263
import com.duckduckgo.js.messaging.api.SubscriptionEventData
263-
import com.duckduckgo.js.messaging.api.WebMessagingPlugin
264+
import com.duckduckgo.js.messaging.api.WebMessaging
264265
import com.duckduckgo.js.messaging.api.WebViewCompatMessageCallback
265266
import com.duckduckgo.malicioussiteprotection.api.MaliciousSiteProtection.Feed
266267
import com.duckduckgo.malicioussiteprotection.api.MaliciousSiteProtection.Feed.MALWARE
@@ -613,7 +614,7 @@ class BrowserTabViewModelTest {
613614
private val mockWebView: WebView = mock()
614615

615616
private val fakeAddDocumentStartJavaScriptPlugins = FakeAddDocumentStartJavaScriptPluginPoint()
616-
private val fakeMessagingPlugins = FakeWebMessagingPluginPoint()
617+
private val fakeMessagingPlugins = FakeWebMessagingBrowserPluginPoint()
617618
private val fakePostMessageWrapperPlugins = FakePostMessageWrapperPluginPoint()
618619

619620
@Before
@@ -7526,11 +7527,11 @@ class BrowserTabViewModelTest {
75267527
runTest {
75277528
val mockCallback = mock<WebViewCompatMessageCallback>()
75287529
val webView = DuckDuckGoWebView(context)
7529-
assertFalse(fakeMessagingPlugins.plugin.registered)
7530+
assertFalse(fakeMessagingPlugins.plugin.webMessaging().registered)
75307531

75317532
testee.configureWebView(webView, mockCallback)
75327533

7533-
assertTrue(fakeMessagingPlugins.plugin.registered)
7534+
assertTrue(fakeMessagingPlugins.plugin.webMessaging().registered)
75347535
}
75357536

75367537
@UiThreadTest
@@ -7862,35 +7863,40 @@ class BrowserTabViewModelTest {
78627863
override fun getPlugins(): Collection<PostMessageWrapperPlugin> = listOf(plugin)
78637864
}
78647865

7865-
class FakeWebMessagingPlugin : WebMessagingPlugin {
7866+
class FakeWebMessaging : WebMessaging {
78667867
var registered = false
78677868
private set
78687869

78697870
override suspend fun unregister(webView: WebView) {
78707871
registered = false
78717872
}
7872-
78737873
override suspend fun register(
78747874
jsMessageCallback: WebViewCompatMessageCallback,
78757875
webView: WebView,
78767876
) {
78777877
registered = true
78787878
}
7879-
78807879
override suspend fun postMessage(
78817880
webView: WebView,
78827881
subscriptionEventData: SubscriptionEventData,
78837882
) {
78847883
}
7885-
78867884
override val context: String
78877885
get() = "test"
78887886
}
78897887

7890-
class FakeWebMessagingPluginPoint : PluginPoint<WebMessagingPlugin> {
7891-
val plugin = FakeWebMessagingPlugin()
7888+
class FakeWebMessagingBrowserPlugin : WebMessagingBrowserPlugin {
7889+
private val webMessaging = FakeWebMessaging()
78927890

7893-
override fun getPlugins(): Collection<WebMessagingPlugin> = listOf(plugin)
7891+
override fun webMessaging(): FakeWebMessaging = webMessaging
7892+
}
7893+
7894+
class FakeWebMessagingBrowserPluginPoint : PluginPoint<WebMessagingBrowserPlugin> {
7895+
val plugin = FakeWebMessagingBrowserPlugin()
7896+
7897+
override fun getPlugins(): Collection<WebMessagingBrowserPlugin> {
7898+
return listOf(plugin)
7899+
}
78947900
}
78957901

78967902
class FakePostMessageWrapperPlugin : PostMessageWrapperPlugin {

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ import com.duckduckgo.brokensite.api.BrokenSitePrompt
276276
import com.duckduckgo.brokensite.api.RefreshPattern
277277
import com.duckduckgo.browser.api.AddDocumentStartJavaScriptBrowserPlugin
278278
import com.duckduckgo.browser.api.UserBrowserProperties
279+
import com.duckduckgo.browser.api.WebMessagingBrowserPlugin
279280
import com.duckduckgo.browser.api.autocomplete.AutoComplete
280281
import com.duckduckgo.browser.api.autocomplete.AutoComplete.AutoCompleteResult
281282
import com.duckduckgo.browser.api.autocomplete.AutoComplete.AutoCompleteSuggestion
@@ -323,7 +324,6 @@ import com.duckduckgo.history.api.NavigationHistory
323324
import com.duckduckgo.js.messaging.api.JsCallbackData
324325
import com.duckduckgo.js.messaging.api.PostMessageWrapperPlugin
325326
import com.duckduckgo.js.messaging.api.SubscriptionEventData
326-
import com.duckduckgo.js.messaging.api.WebMessagingPlugin
327327
import com.duckduckgo.js.messaging.api.WebViewCompatMessageCallback
328328
import com.duckduckgo.malicioussiteprotection.api.MaliciousSiteProtection.Feed
329329
import com.duckduckgo.malicioussiteprotection.api.MaliciousSiteProtection.Feed.MALWARE
@@ -490,7 +490,7 @@ class BrowserTabViewModel @Inject constructor(
490490
private val vpnMenuStateProvider: VpnMenuStateProvider,
491491
private val webViewCompatWrapper: WebViewCompatWrapper,
492492
private val addDocumentStartJavascriptPlugins: PluginPoint<AddDocumentStartJavaScriptBrowserPlugin>,
493-
private val webMessagingPlugins: PluginPoint<WebMessagingPlugin>,
493+
private val webMessagingPlugins: PluginPoint<WebMessagingBrowserPlugin>,
494494
private val postMessageWrapperPlugins: PluginPoint<PostMessageWrapperPlugin>,
495495
) : ViewModel(),
496496
WebViewClientListener,
@@ -4228,7 +4228,7 @@ class BrowserTabViewModel @Inject constructor(
42284228

42294229
callback?.let {
42304230
webMessagingPlugins.getPlugins().forEach { plugin ->
4231-
plugin.register(callback, webView)
4231+
plugin.webMessaging().register(callback, webView)
42324232
}
42334233
}
42344234
}
@@ -4247,8 +4247,8 @@ class BrowserTabViewModel @Inject constructor(
42474247
}
42484248

42494249
private suspend fun addDocumentStartJavaScript(webView: WebView) {
4250-
addDocumentStartJavascriptPlugins.getPlugins().addDocumentStartJavaScript().forEach {
4251-
it.addDocumentStartJavaScript(
4250+
addDocumentStartJavascriptPlugins.getPlugins().forEach {
4251+
it.addDocumentStartJavaScript().addDocumentStartJavaScript(
42524252
webView,
42534253
)
42544254
}
@@ -4258,7 +4258,7 @@ class BrowserTabViewModel @Inject constructor(
42584258
if (withContext(dispatchers.io()) { !androidBrowserConfig.updateScriptOnPageFinished().isEnabled() }) {
42594259
addDocumentStartJavascriptPlugins
42604260
.getPlugins()
4261-
.addDocumentStartJavaScript()
4261+
.map { it.addDocumentStartJavaScript() }
42624262
.filter { plugin ->
42634263
(plugin.context == "contentScopeScripts")
42644264
}.forEach {
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: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ import com.duckduckgo.js.messaging.api.JsMessageHelper
2525
import com.duckduckgo.js.messaging.api.PostMessageWrapperPlugin
2626
import com.duckduckgo.js.messaging.api.SubscriptionEvent
2727
import com.duckduckgo.js.messaging.api.SubscriptionEventData
28-
import com.duckduckgo.js.messaging.api.WebMessagingPlugin
28+
import com.duckduckgo.js.messaging.api.WebMessaging
2929
import com.squareup.anvil.annotations.ContributesMultibinding
3030
import javax.inject.Inject
3131
import javax.inject.Named
3232

3333
@ContributesMultibinding(FragmentScope::class)
3434
class ContentScopeScriptsPostMessageWrapperPlugin @Inject constructor(
35-
@Named("contentScopeScripts") private val webMessagingPlugin: WebMessagingPlugin,
35+
@Named("contentScopeScripts") private val webMessaging: WebMessaging,
3636
private val jsMessageHelper: JsMessageHelper,
3737
private val coreContentScopeScripts: CoreContentScopeScripts,
3838
private val webViewCompatContentScopeScripts: WebViewCompatContentScopeScripts,
@@ -43,11 +43,11 @@ class ContentScopeScriptsPostMessageWrapperPlugin @Inject constructor(
4343
webView: WebView,
4444
) {
4545
if (webViewCompatContentScopeScripts.isEnabled()) {
46-
webMessagingPlugin.postMessage(webView, message)
46+
webMessaging.postMessage(webView, message)
4747
} else {
4848
jsMessageHelper.sendSubscriptionEvent(
4949
subscriptionEvent = SubscriptionEvent(
50-
context = webMessagingPlugin.context,
50+
context = webMessaging.context,
5151
featureName = message.featureName,
5252
subscriptionName = message.subscriptionName,
5353
params = message.params,
@@ -60,5 +60,5 @@ class ContentScopeScriptsPostMessageWrapperPlugin @Inject constructor(
6060
}
6161

6262
override val context: String
63-
get() = webMessagingPlugin.context
63+
get() = webMessaging.context
6464
}
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: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import com.duckduckgo.contentscopescripts.impl.WebViewCompatContentScopeScripts
66
import com.duckduckgo.js.messaging.api.JsMessageHelper
77
import com.duckduckgo.js.messaging.api.SubscriptionEvent
88
import com.duckduckgo.js.messaging.api.SubscriptionEventData
9-
import com.duckduckgo.js.messaging.api.WebMessagingPlugin
9+
import com.duckduckgo.js.messaging.api.WebMessaging
1010
import kotlinx.coroutines.test.runTest
1111
import org.json.JSONObject
1212
import org.junit.Before
@@ -17,7 +17,7 @@ import org.mockito.kotlin.verify
1717
import org.mockito.kotlin.whenever
1818

1919
class ContentScopeScriptsPostMessageWrapperPluginTest {
20-
private val mockWebMessagingPlugin: WebMessagingPlugin = mock()
20+
private val mockWebMessaging: WebMessaging = mock()
2121
private val mockJsHelper: JsMessageHelper = mock()
2222
private val mockCoreContentScopeScripts: CoreContentScopeScripts = mock()
2323
private val mockWebViewCompatContentScopeScripts: WebViewCompatContentScopeScripts = mock()
@@ -39,7 +39,7 @@ class ContentScopeScriptsPostMessageWrapperPluginTest {
3939

4040
val testee =
4141
ContentScopeScriptsPostMessageWrapperPlugin(
42-
webMessagingPlugin = mockWebMessagingPlugin,
42+
webMessaging = mockWebMessaging,
4343
jsMessageHelper = mockJsHelper,
4444
coreContentScopeScripts = mockCoreContentScopeScripts,
4545
webViewCompatContentScopeScripts = mockWebViewCompatContentScopeScripts,
@@ -49,7 +49,7 @@ class ContentScopeScriptsPostMessageWrapperPluginTest {
4949
fun setup() {
5050
whenever(mockCoreContentScopeScripts.callbackName).thenReturn("callbackName")
5151
whenever(mockCoreContentScopeScripts.secret).thenReturn("secret")
52-
whenever(mockWebMessagingPlugin.context).thenReturn("contentScopeScripts")
52+
whenever(mockWebMessaging.context).thenReturn("contentScopeScripts")
5353
whenever(mockJsonObject.toString()).thenReturn("{}")
5454
}
5555

@@ -60,8 +60,8 @@ class ContentScopeScriptsPostMessageWrapperPluginTest {
6060

6161
testee.postMessage(subscriptionEventData, mockWebView)
6262

63-
verify(mockWebMessagingPlugin).postMessage(mockWebView, subscriptionEventData)
64-
}
63+
verify(mockWebMessaging).postMessage(mockWebView, subscriptionEventData)
64+
}
6565

6666
@Test
6767
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`() =

0 commit comments

Comments
 (0)