Skip to content

Commit d017817

Browse files
committed
Add delegate to simplify AddDocumentStartJavaScriptPlugin implementation
1 parent 384e6a2 commit d017817

File tree

3 files changed

+133
-46
lines changed

3 files changed

+133
-46
lines changed
Lines changed: 16 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023 DuckDuckGo
2+
* Copyright (c) 2025 DuckDuckGo
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,62 +16,32 @@
1616

1717
package com.duckduckgo.contentscopescripts.impl
1818

19-
import android.annotation.SuppressLint
20-
import android.webkit.WebView
21-
import androidx.webkit.ScriptHandler
22-
import com.duckduckgo.app.browser.api.WebViewCapabilityChecker
23-
import com.duckduckgo.browser.api.webviewcompat.WebViewCompatWrapper
24-
import com.duckduckgo.common.utils.DispatcherProvider
2519
import com.duckduckgo.contentscopescripts.api.contentscopeExperiments.ContentScopeExperiments
2620
import com.duckduckgo.di.scopes.FragmentScope
2721
import com.duckduckgo.js.messaging.api.AddDocumentStartJavaScriptPlugin
22+
import com.duckduckgo.js.messaging.api.AddDocumentStartJavaScriptScriptStrategy
23+
import com.duckduckgo.js.messaging.api.AddDocumentStartScriptDelegate
2824
import com.squareup.anvil.annotations.ContributesMultibinding
2925
import dagger.SingleInstanceIn
30-
import kotlinx.coroutines.withContext
3126
import javax.inject.Inject
3227

3328
@SingleInstanceIn(FragmentScope::class)
3429
@ContributesMultibinding(FragmentScope::class)
3530
class ContentScopeScriptsAddDocumentStartJavaScriptPlugin @Inject constructor(
36-
private val webViewCompatContentScopeScripts: WebViewCompatContentScopeScripts,
37-
private val dispatcherProvider: DispatcherProvider,
38-
private val webViewCapabilityChecker: WebViewCapabilityChecker,
39-
private val webViewCompatWrapper: WebViewCompatWrapper,
40-
private val contentScopeExperiments: ContentScopeExperiments,
41-
) : AddDocumentStartJavaScriptPlugin {
42-
private var script: ScriptHandler? = null
43-
private var currentScriptString: String? = null
44-
45-
@SuppressLint("RequiresFeature")
46-
override suspend fun addDocumentStartJavaScript(webView: WebView) {
47-
if (!webViewCompatContentScopeScripts.isEnabled() ||
48-
!webViewCapabilityChecker.isSupported(
49-
WebViewCapabilityChecker.WebViewCapability.DocumentStartJavaScript,
50-
)
51-
) {
52-
return
31+
webViewCompatContentScopeScripts: WebViewCompatContentScopeScripts,
32+
contentScopeExperiments: ContentScopeExperiments,
33+
scriptInjectorDelegate: AddDocumentStartScriptDelegate,
34+
) : AddDocumentStartJavaScriptPlugin by scriptInjectorDelegate.createPlugin(
35+
object : AddDocumentStartJavaScriptScriptStrategy {
36+
override suspend fun canInject(): Boolean {
37+
return webViewCompatContentScopeScripts.isEnabled()
5338
}
5439

55-
val activeExperiments = contentScopeExperiments.getActiveExperiments()
56-
val scriptString = webViewCompatContentScopeScripts.getScript(activeExperiments)
57-
if (scriptString == currentScriptString) {
58-
return
59-
}
60-
script?.let {
61-
withContext(dispatcherProvider.main()) {
62-
it.remove()
63-
}
64-
script = null
40+
override suspend fun getScriptString(): String {
41+
val activeExperiments = contentScopeExperiments.getActiveExperiments()
42+
return webViewCompatContentScopeScripts.getScript(activeExperiments)
6543
}
6644

67-
webViewCompatWrapper
68-
.addDocumentStartJavaScript(
69-
webView,
70-
scriptString,
71-
setOf("*"),
72-
)?.let {
73-
script = it
74-
currentScriptString = scriptString
75-
}
76-
}
77-
}
45+
override val allowedOriginRules: Set<String> = setOf("*")
46+
},
47+
)
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
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.js.messaging.impl
18+
19+
import android.annotation.SuppressLint
20+
import android.webkit.WebView
21+
import androidx.webkit.ScriptHandler
22+
import com.duckduckgo.app.browser.api.WebViewCapabilityChecker
23+
import com.duckduckgo.app.di.AppCoroutineScope
24+
import com.duckduckgo.browser.api.webviewcompat.WebViewCompatWrapper
25+
import com.duckduckgo.common.utils.DispatcherProvider
26+
import com.duckduckgo.di.scopes.FragmentScope
27+
import com.duckduckgo.js.messaging.api.AddDocumentStartJavaScriptPlugin
28+
import com.duckduckgo.js.messaging.api.AddDocumentStartJavaScriptScriptStrategy
29+
import com.duckduckgo.js.messaging.api.AddDocumentStartScriptDelegate
30+
import com.squareup.anvil.annotations.ContributesBinding
31+
import javax.inject.Inject
32+
import kotlinx.coroutines.CoroutineScope
33+
import kotlinx.coroutines.launch
34+
import kotlinx.coroutines.withContext
35+
36+
@ContributesBinding(FragmentScope::class)
37+
/**
38+
* Delegate class that implements AddDocumentStartJavaScriptPlugin and handles
39+
* the common script injection logic
40+
*/
41+
class RealAddDocumentStartScriptDelegate @Inject constructor(
42+
private val webViewCapabilityChecker: WebViewCapabilityChecker,
43+
@AppCoroutineScope private val coroutineScope: CoroutineScope,
44+
private val dispatcherProvider: DispatcherProvider,
45+
private val webViewCompatWrapper: WebViewCompatWrapper,
46+
) : AddDocumentStartScriptDelegate {
47+
48+
private var script: ScriptHandler? = null
49+
private var currentScriptString: String? = null
50+
51+
override fun createPlugin(strategy: AddDocumentStartJavaScriptScriptStrategy): AddDocumentStartJavaScriptPlugin {
52+
return object : AddDocumentStartJavaScriptPlugin {
53+
@SuppressLint("RequiresFeature")
54+
override suspend fun addDocumentStartJavaScript(webView: WebView) {
55+
if (!strategy.canInject() || !webViewCapabilityChecker.isSupported(
56+
WebViewCapabilityChecker.WebViewCapability.DocumentStartJavaScript,
57+
)
58+
) {
59+
return
60+
}
61+
62+
val scriptString = strategy.getScriptString()
63+
if (scriptString == currentScriptString) {
64+
return
65+
}
66+
script?.let {
67+
withContext(dispatcherProvider.main()) {
68+
it.remove()
69+
}
70+
script = null
71+
}
72+
73+
webViewCompatWrapper.addDocumentStartJavaScript(
74+
webView,
75+
scriptString,
76+
strategy.allowedOriginRules,
77+
)?.let {
78+
script = it
79+
currentScriptString = scriptString
80+
}
81+
}
82+
}
83+
}
84+
}

js-messaging/js-messaging-api/src/main/java/com/duckduckgo/js/messaging/api/AddDocumentStartJavaScriptPlugin.kt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,36 @@ import android.webkit.WebView
2626
interface AddDocumentStartJavaScriptPlugin {
2727
suspend fun addDocumentStartJavaScript(webView: WebView)
2828
}
29+
30+
/**
31+
* Strategy interface for script injection logic.
32+
* Allows different implementations to provide their own injection behavior.
33+
*/
34+
interface AddDocumentStartJavaScriptScriptStrategy {
35+
/**
36+
* Determines whether script injection should proceed (i.e. by checking feature flags).
37+
* @return true if injection is allowed, false otherwise
38+
*/
39+
suspend fun canInject(): Boolean
40+
41+
/**
42+
* Provides the script string to be injected.
43+
* @return the JavaScript code to inject
44+
*/
45+
suspend fun getScriptString(): String
46+
47+
/**
48+
* Defines the allowed origin rules for script injection.
49+
* @return set of allowed origin patterns
50+
*/
51+
val allowedOriginRules: Set<String>
52+
}
53+
54+
interface AddDocumentStartScriptDelegate {
55+
/**
56+
* Creates an AddDocumentStartJavaScriptPlugin implementation with the given [AddDocumentStartJavaScriptScriptStrategy].
57+
* @param strategy the strategy to use for determining injection behavior
58+
* @return [AddDocumentStartJavaScriptPlugin] implementation
59+
*/
60+
fun createPlugin(strategy: AddDocumentStartJavaScriptScriptStrategy): AddDocumentStartJavaScriptPlugin
61+
}

0 commit comments

Comments
 (0)