Skip to content

Commit ec0954c

Browse files
authored
feat: Add function to inject external JS into WebView before document load (#7864)
1 parent 5f09297 commit ec0954c

File tree

6 files changed

+94
-4
lines changed

6 files changed

+94
-4
lines changed

android/capacitor/src/main/java/com/getcapacitor/Bridge.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ public class Bridge {
121121
private HostMask appAllowNavigationMask;
122122
private Set<String> allowedOriginRules = new HashSet<String>();
123123
private ArrayList<String> authorities = new ArrayList<>();
124+
private ArrayList<String> miscJSFileInjections = new ArrayList<String>();
125+
private Boolean canInjectJS = true;
124126
// A reference to the main WebView for the app
125127
private final WebView webView;
126128
public final MockCordovaInterfaceImpl cordovaInterface;
@@ -1017,14 +1019,28 @@ private JSInjector getJSInjector() {
10171019
String cordovaPluginsJS = JSExport.getCordovaPluginJS(context);
10181020
String cordovaPluginsFileJS = JSExport.getCordovaPluginsFileJS(context);
10191021
String localUrlJS = "window.WEBVIEW_SERVER_URL = '" + localUrl + "';";
1022+
String miscJS = JSExport.getMiscFileJS(miscJSFileInjections, context);
10201023

1021-
return new JSInjector(globalJS, bridgeJS, pluginJS, cordovaJS, cordovaPluginsJS, cordovaPluginsFileJS, localUrlJS);
1024+
miscJSFileInjections = new ArrayList<>();
1025+
canInjectJS = false;
1026+
1027+
return new JSInjector(globalJS, bridgeJS, pluginJS, cordovaJS, cordovaPluginsJS, cordovaPluginsFileJS, localUrlJS, miscJS);
10221028
} catch (Exception ex) {
10231029
Logger.error("Unable to export Capacitor JS. App will not function!", ex);
10241030
}
10251031
return null;
10261032
}
10271033

1034+
/**
1035+
* Inject JavaScript from an external file before the WebView loads.
1036+
* @param path relative to public folder
1037+
*/
1038+
public void injectScriptBeforeLoad(String path) {
1039+
if (canInjectJS) {
1040+
miscJSFileInjections.add(path);
1041+
}
1042+
}
1043+
10281044
/**
10291045
* Restore any saved bundle state data
10301046
* @param savedInstanceState

android/capacitor/src/main/java/com/getcapacitor/JSExport.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,21 @@ public static String getGlobalJS(Context context, boolean loggingEnabled, boolea
2121
return "window.Capacitor = { DEBUG: " + isDebug + ", isLoggingEnabled: " + loggingEnabled + ", Plugins: {} };";
2222
}
2323

24+
public static String getMiscFileJS(ArrayList<String> paths, Context context) {
25+
List<String> lines = new ArrayList<>();
26+
27+
for (String path : paths) {
28+
try {
29+
String fileContent = readFileFromAssets(context.getAssets(), "public/" + path);
30+
lines.add(fileContent);
31+
} catch (IOException ex) {
32+
Logger.error("Unable to read public/" + path);
33+
}
34+
}
35+
36+
return TextUtils.join("\n", lines);
37+
}
38+
2439
public static String getCordovaJS(Context context) {
2540
String fileContent = "";
2641
try {

android/capacitor/src/main/java/com/getcapacitor/JSInjector.java

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class JSInjector {
2020
private String cordovaPluginsJS;
2121
private String cordovaPluginsFileJS;
2222
private String localUrlJS;
23+
private String miscJS;
2324

2425
public JSInjector(
2526
String globalJS,
@@ -29,6 +30,19 @@ public JSInjector(
2930
String cordovaPluginsJS,
3031
String cordovaPluginsFileJS,
3132
String localUrlJS
33+
) {
34+
this(globalJS, bridgeJS, pluginJS, cordovaJS, cordovaPluginsJS, cordovaPluginsFileJS, localUrlJS, null);
35+
}
36+
37+
public JSInjector(
38+
String globalJS,
39+
String bridgeJS,
40+
String pluginJS,
41+
String cordovaJS,
42+
String cordovaPluginsJS,
43+
String cordovaPluginsFileJS,
44+
String localUrlJS,
45+
String miscJS
3246
) {
3347
this.globalJS = globalJS;
3448
this.bridgeJS = bridgeJS;
@@ -37,6 +51,7 @@ public JSInjector(
3751
this.cordovaPluginsJS = cordovaPluginsJS;
3852
this.cordovaPluginsFileJS = cordovaPluginsFileJS;
3953
this.localUrlJS = localUrlJS;
54+
this.miscJS = miscJS;
4055
}
4156

4257
/**
@@ -45,7 +60,7 @@ public JSInjector(
4560
* @return
4661
*/
4762
public String getScriptString() {
48-
return (
63+
String scriptString =
4964
globalJS +
5065
"\n\n" +
5166
localUrlJS +
@@ -58,8 +73,13 @@ public String getScriptString() {
5873
"\n\n" +
5974
cordovaPluginsFileJS +
6075
"\n\n" +
61-
cordovaPluginsJS
62-
);
76+
cordovaPluginsJS;
77+
78+
if (miscJS != null) {
79+
scriptString += "\n\n" + miscJS;
80+
}
81+
82+
return scriptString;
6383
}
6484

6585
/**

ios/Capacitor/Capacitor/CAPBridgeProtocol.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ import WebKit
5959
func eval(js: String)
6060
// swiftlint:enable identifier_name
6161

62+
@objc optional func injectScriptBeforeLoad(path: String)
63+
6264
func triggerJSEvent(eventName: String, target: String)
6365
func triggerJSEvent(eventName: String, target: String, data: String)
6466

ios/Capacitor/Capacitor/CapacitorBridge.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol {
119119
// Whether to inject the Cordova files
120120
private var injectCordovaFiles = false
121121
private var cordovaParser: CDVConfigParser?
122+
private var injectMiscFiles: [String] = []
123+
private var canInjectJS: Bool = true
122124

123125
// Background dispatch queue for plugin calls
124126
open private(set) var dispatchQueue = DispatchQueue(label: "bridge")
@@ -214,6 +216,8 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol {
214216
exportCoreJS(localUrl: configuration.localURL.absoluteString)
215217
registerPlugins()
216218
setupCordovaCompatibility()
219+
exportMiscJS()
220+
canInjectJS = false
217221
observers.append(NotificationCenter.default.addObserver(forName: type(of: self).tmpVCAppeared.name, object: .none, queue: .none) { [weak self] _ in
218222
self?.tmpWindow = nil
219223
})
@@ -245,6 +249,14 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol {
245249
}
246250
}
247251

252+
/**
253+
Export misc JavaScript to the webview
254+
*/
255+
func exportMiscJS() {
256+
JSExport.exportMiscFileJS(paths: injectMiscFiles, userContentController: webViewDelegationHandler.contentController)
257+
injectMiscFiles.removeAll()
258+
}
259+
248260
/**
249261
Set up our Cordova compat by loading all known Cordova plugins and injecting their JS.
250262
*/
@@ -590,6 +602,17 @@ open class CapacitorBridge: NSObject, CAPBridgeProtocol {
590602

591603
// MARK: - CAPBridgeProtocol: JavaScript Handling
592604

605+
/**
606+
Inject JavaScript from an external file before the WebView loads.
607+
608+
`path` is relative to the public folder
609+
*/
610+
public func injectScriptBeforeLoad(path: String) {
611+
if canInjectJS {
612+
injectMiscFiles.append(path)
613+
}
614+
}
615+
593616
/**
594617
Eval JS for a specific plugin.
595618

ios/Capacitor/Capacitor/JSExport.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,20 @@ internal class JSExport {
5353
}
5454
}
5555

56+
static func exportMiscFileJS(paths: [String], userContentController: WKUserContentController) {
57+
for path in paths {
58+
if let miscJSFilePath = Bundle.main.url(forResource: "public/\(path.replacingOccurrences(of: ".js", with: ""))", withExtension: "js") {
59+
do {
60+
try self.injectFile(fileURL: miscJSFilePath, userContentController: userContentController)
61+
} catch {
62+
CAPLog.print("WARNING: Unable to inject js from path \(miscJSFilePath)")
63+
}
64+
} else {
65+
CAPLog.print("WARNING: Unable to inject js from path \(path)")
66+
}
67+
}
68+
}
69+
5670
/**
5771
Export the JS required to implement the given plugin.
5872
*/

0 commit comments

Comments
 (0)