瀏覽代碼

feat(android): Add support for injectedJavaScriptBeforeContentLoaded on Android (#1099 by @SRandazzo and @ @shirakaba)

Salvatore Randazzo 3 年之前
父節點
當前提交
ac4e05e0f2
No account linked to committer's email address

+ 48
- 0
android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java 查看文件

@@ -400,6 +400,21 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
400 400
     ((RNCWebView) view).setInjectedJavaScript(injectedJavaScript);
401 401
   }
402 402
 
403
+  @ReactProp(name = "injectedJavaScriptBeforeContentLoaded")
404
+  public void setInjectedJavaScriptBeforeContentLoaded(WebView view, @Nullable String injectedJavaScriptBeforeContentLoaded) {
405
+    ((RNCWebView) view).setInjectedJavaScriptBeforeContentLoaded(injectedJavaScriptBeforeContentLoaded);
406
+  }
407
+
408
+  @ReactProp(name = "injectedJavaScriptForMainFrameOnly")
409
+  public void setInjectedJavaScriptForMainFrameOnly(WebView view, boolean enabled) {
410
+    ((RNCWebView) view).setInjectedJavaScriptForMainFrameOnly(enabled);
411
+  }
412
+
413
+  @ReactProp(name = "injectedJavaScriptBeforeContentLoadedForMainFrameOnly")
414
+  public void setInjectedJavaScriptBeforeContentLoadedForMainFrameOnly(WebView view, boolean enabled) {
415
+    ((RNCWebView) view).setInjectedJavaScriptBeforeContentLoadedForMainFrameOnly(enabled);
416
+  }
417
+
403 418
   @ReactProp(name = "messagingEnabled")
404 419
   public void setMessagingEnabled(WebView view, boolean enabled) {
405 420
     ((RNCWebView) view).setMessagingEnabled(enabled);
@@ -753,6 +768,9 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
753 768
       super.onPageStarted(webView, url, favicon);
754 769
       mLastLoadFailed = false;
755 770
 
771
+      RNCWebView reactWebView = (RNCWebView) webView;
772
+      reactWebView.callInjectedJavaScriptBeforeContentLoaded();       
773
+
756 774
       dispatchEvent(
757 775
         webView,
758 776
         new TopLoadingStartEvent(
@@ -1011,6 +1029,16 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
1011 1029
   protected static class RNCWebView extends WebView implements LifecycleEventListener {
1012 1030
     protected @Nullable
1013 1031
     String injectedJS;
1032
+    protected @Nullable
1033
+    String injectedJSBeforeContentLoaded;
1034
+
1035
+    /**
1036
+     * android.webkit.WebChromeClient fundamentally does not support JS injection into frames other
1037
+     * than the main frame, so these two properties are mostly here just for parity with iOS & macOS.
1038
+     */
1039
+    protected boolean injectedJavaScriptForMainFrameOnly = true;
1040
+    protected boolean injectedJavaScriptBeforeContentLoadedForMainFrameOnly = true;
1041
+
1014 1042
     protected boolean messagingEnabled = false;
1015 1043
     protected @Nullable
1016 1044
     String messagingModuleName;
@@ -1105,6 +1133,18 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
1105 1133
       injectedJS = js;
1106 1134
     }
1107 1135
 
1136
+    public void setInjectedJavaScriptBeforeContentLoaded(@Nullable String js) {
1137
+      injectedJSBeforeContentLoaded = js;
1138
+    }
1139
+
1140
+    public void setInjectedJavaScriptForMainFrameOnly(boolean enabled) {
1141
+      injectedJavaScriptForMainFrameOnly = enabled;
1142
+    }
1143
+
1144
+    public void setInjectedJavaScriptBeforeContentLoadedForMainFrameOnly(boolean enabled) {
1145
+      injectedJavaScriptBeforeContentLoadedForMainFrameOnly = enabled;
1146
+    }
1147
+
1108 1148
     protected RNCWebViewBridge createRNCWebViewBridge(RNCWebView webView) {
1109 1149
       return new RNCWebViewBridge(webView);
1110 1150
     }
@@ -1159,6 +1199,14 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
1159 1199
       }
1160 1200
     }
1161 1201
 
1202
+    public void callInjectedJavaScriptBeforeContentLoaded() {
1203
+      if (getSettings().getJavaScriptEnabled() &&
1204
+      injectedJSBeforeContentLoaded != null &&
1205
+      !TextUtils.isEmpty(injectedJSBeforeContentLoaded)) {
1206
+        evaluateJavascriptWithFallback("(function() {\n" + injectedJSBeforeContentLoaded + ";\n})();");
1207
+      }
1208
+    }
1209
+
1162 1210
     public void onMessage(String message) {
1163 1211
       ReactContext reactContext = (ReactContext) this.getContext();
1164 1212
       RNCWebView mContext = this;

+ 16
- 12
docs/Guide.md 查看文件

@@ -88,6 +88,7 @@ class MyWeb extends Component {
88 88
   }
89 89
 }
90 90
 ```
91
+
91 92
 </details>
92 93
 
93 94
 ### Controlling navigation state changes
@@ -104,14 +105,14 @@ class MyWeb extends Component {
104 105
   render() {
105 106
     return (
106 107
       <WebView
107
-        ref={ref => (this.webview = ref)}
108
+        ref={(ref) => (this.webview = ref)}
108 109
         source={{ uri: 'https://reactnative.dev/' }}
109 110
         onNavigationStateChange={this.handleWebViewNavigationStateChange}
110 111
       />
111 112
     );
112 113
   }
113 114
 
114
-  handleWebViewNavigationStateChange = newNavState => {
115
+  handleWebViewNavigationStateChange = (newNavState) => {
115 116
     // newNavState looks something like this:
116 117
     // {
117 118
     //   url?: string;
@@ -240,11 +241,12 @@ is used to determine if an HTTP response should be a download. On iOS 12 or olde
240 241
 trigger calls to `onFileDownload`.
241 242
 
242 243
 Example:
244
+
243 245
 ```javascript
244
-  onFileDownload = ({ nativeEvent }) => {
245
-    const { downloadUrl } = nativeEvent;
246
-    // --> Your download code goes here <--
247
-  }
246
+onFileDownload = ({ nativeEvent }) => {
247
+  const { downloadUrl } = nativeEvent;
248
+  // --> Your download code goes here <--
249
+};
248 250
 ```
249 251
 
250 252
 To be able to save images to the gallery you need to specify this permission in your `ios/[project]/Info.plist` file:
@@ -313,7 +315,7 @@ export default class App extends Component {
313 315
 
314 316
 This runs the JavaScript in the `runFirst` string once the page is loaded. In this case, you can see that both the body style was changed to red and the alert showed up after 2 seconds.
315 317
 
316
-By setting `injectedJavaScriptForMainFrameOnly: false`, the JavaScript injection will occur on all frames (not just the top frame) if supported for the given platform.
318
+By setting `injectedJavaScriptForMainFrameOnly: false`, the JavaScript injection will occur on all frames (not just the main frame) if supported for the given platform. For example, if a page contains an iframe, the javascript will be injected into that iframe as well with this set to `false`. (Note this is not supported on Android.) There is also `injectedJavaScriptBeforeContentLoadedForMainFrameOnly` for injecting prior to content loading. Read more about this in the [Reference](./Reference.md#injectedjavascriptformainframeonly).
317 319
 
318 320
 <img alt="screenshot of Github repo" width="200" src="https://user-images.githubusercontent.com/1479215/53609254-e5dc9c00-3b7a-11e9-9118-bc4e520ce6ca.png" />
319 321
 
@@ -354,10 +356,11 @@ export default class App extends Component {
354 356
 
355 357
 This runs the JavaScript in the `runFirst` string before the page is loaded. In this case, the value of `window.isNativeApp` will be set to true before the web code executes.
356 358
 
357
-By setting `injectedJavaScriptBeforeContentLoadedForMainFrameOnly: false`, the JavaScript injection will occur on all frames (not just the top frame) if supported for the given platform. Howver, although support for `injectedJavaScriptBeforeContentLoadedForMainFrameOnly: false` has been implemented for iOS and macOS, [it is not clear](https://github.com/react-native-community/react-native-webview/pull/1119#issuecomment-600275750) that it is actually possible to inject JS into iframes at this point in the page lifecycle, and so relying on the expected behaviour of this prop when set to `false` is not recommended.
359
+By setting `injectedJavaScriptBeforeContentLoadedForMainFrameOnly: false`, the JavaScript injection will occur on all frames (not just the top frame) if supported for the given platform. However, although support for `injectedJavaScriptBeforeContentLoadedForMainFrameOnly: false` has been implemented for iOS and macOS, [it is not clear](https://github.com/react-native-community/react-native-webview/pull/1119#issuecomment-600275750) that it is actually possible to inject JS into iframes at this point in the page lifecycle, and so relying on the expected behaviour of this prop when set to `false` is not recommended.
358 360
 
359 361
 > On iOS, ~~`injectedJavaScriptBeforeContentLoaded` runs a method on WebView called `evaluateJavaScript:completionHandler:`~~ – this is no longer true as of version `8.2.0`. Instead, we use a `WKUserScript` with injection time `WKUserScriptInjectionTimeAtDocumentStart`. As a consequence, `injectedJavaScriptBeforeContentLoaded` no longer returns an evaluation value nor logs a warning to the console. In the unlikely event that your app depended upon this behaviour, please see migration steps [here](https://github.com/react-native-community/react-native-webview/pull/1119#issuecomment-574919464) to retain equivalent behaviour.
360 362
 > On Android, `injectedJavaScript` runs a method on the Android WebView called `evaluateJavascriptWithFallback`
363
+> Note on Android Compatibility: For applications targeting `Build.VERSION_CODES.N` or later, JavaScript state from an empty WebView is no longer persisted across navigations like `loadUrl(java.lang.String)`. For example, global variables and functions defined before calling `loadUrl(java.lang.String)` will not exist in the loaded page. Applications should use the Android Native API `addJavascriptInterface(Object, String)` instead to persist JavaScript objects across navigations.
361 364
 
362 365
 #### The `injectJavaScript` method
363 366
 
@@ -382,7 +385,7 @@ export default class App extends Component {
382 385
     return (
383 386
       <View style={{ flex: 1 }}>
384 387
         <WebView
385
-          ref={r => (this.webref = r)}
388
+          ref={(r) => (this.webref = r)}
386 389
           source={{
387 390
             uri:
388 391
               'https://github.com/react-native-community/react-native-webview',
@@ -435,7 +438,7 @@ export default class App extends Component {
435 438
       <View style={{ flex: 1 }}>
436 439
         <WebView
437 440
           source={{ html }}
438
-          onMessage={event => {
441
+          onMessage={(event) => {
439 442
             alert(event.nativeEvent.data);
440 443
           }}
441 444
         />
@@ -471,7 +474,7 @@ This will set the header on the first load, but not on subsequent page navigatio
471 474
 In order to work around this, you can track the current URL, intercept new page loads, and navigate to them yourself ([original credit for this technique to Chirag Shah from Big Binary](https://blog.bigbinary.com/2016/07/26/passing-request-headers-on-each-webview-request-in-react-native.html)):
472 475
 
473 476
 ```jsx
474
-const CustomHeaderWebView = props => {
477
+const CustomHeaderWebView = (props) => {
475 478
   const { uri, onLoadStart, ...restProps } = props;
476 479
   const [currentURI, setURI] = useState(props.source.uri);
477 480
   const newSource = { ...props.source, uri: currentURI };
@@ -480,7 +483,7 @@ const CustomHeaderWebView = props => {
480 483
     <WebView
481 484
       {...restProps}
482 485
       source={newSource}
483
-      onShouldStartLoadWithRequest={request => {
486
+      onShouldStartLoadWithRequest={(request) => {
484 487
         // If we're loading the current URI, allow it to load
485 488
         if (request.url === currentURI) return true;
486 489
         // We're loading a new URL -- change state first
@@ -539,6 +542,7 @@ const App = () => {
539 542
 Note that these cookies will only be sent on the first request unless you use the technique above for [setting custom headers on each page load](#Setting-Custom-Headers).
540 543
 
541 544
 ### Hardware Silence Switch
545
+
542 546
 There are some inconsistencies in how the hardware silence switch is handled between embedded `audio` and `video` elements and between iOS and Android platforms.
543 547
 
544 548
 Audio on `iOS` will be muted when the hardware silence switch is in the on position, unless the `ignoreSilentHardwareSwitch` parameter is set to true.

+ 6
- 8
docs/Reference.md 查看文件

@@ -190,27 +190,25 @@ const INJECTED_JAVASCRIPT = `(function() {
190 190
 
191 191
 ### `injectedJavaScriptForMainFrameOnly`
192 192
 
193
-If `true` (default), loads the `injectedJavaScript` only into the main frame.
193
+If `true` (default; mandatory for Android), loads the `injectedJavaScript` only into the main frame.
194 194
 
195
-If `false`, loads it into all frames (e.g. iframes).
195
+If `false`, (only supported on iOS and macOS), loads it into all frames (e.g. iframes).
196 196
 
197 197
 | Type   | Required | Platform |
198 198
 | ------ | -------- | -------- |
199
-| bool | No       | iOS, macOS       |
199
+| bool | No       | iOS and macOS (only `true` supported for Android) |
200 200
 
201 201
 ---
202 202
 
203 203
 ### `injectedJavaScriptBeforeContentLoadedForMainFrameOnly`
204 204
 
205
-If `true` (default), loads the `injectedJavaScriptBeforeContentLoaded` only into the main frame.
205
+If `true` (default; mandatory for Android), loads the `injectedJavaScriptBeforeContentLoaded` only into the main frame.
206 206
 
207
-If `false`, loads it into all frames (e.g. iframes).
208
-
209
-Warning: although support for `injectedJavaScriptBeforeContentLoadedForMainFrameOnly: false` has been implemented for iOS and macOS, [it is not clear](https://github.com/react-native-community/react-native-webview/pull/1119#issuecomment-600275750) that it is actually possible to inject JS into iframes at this point in the page lifecycle, and so relying on the expected behaviour of this prop when set to `false` is not recommended.
207
+If `false`, (only supported on iOS and macOS), loads it into all frames (e.g. iframes).
210 208
 
211 209
 | Type   | Required | Platform |
212 210
 | ------ | -------- | -------- |
213
-| bool | No       | iOS, macOS       |
211
+| bool | No       | iOS and macOS (only `true` supported for Android) |
214 212
 
215 213
 ---
216 214
 

+ 7
- 0
example/android/app/src/main/java/com/example/MainApplication.java 查看文件

@@ -2,6 +2,9 @@ package com.example;
2 2
 
3 3
 import android.app.Application;
4 4
 import android.content.Context;
5
+import android.os.Build;
6
+import android.webkit.WebView;
7
+
5 8
 import com.facebook.react.PackageList;
6 9
 import com.facebook.react.ReactApplication;
7 10
 import com.facebook.react.ReactNativeHost;
@@ -44,6 +47,10 @@ public class MainApplication extends Application implements ReactApplication {
44 47
   @Override
45 48
   public void onCreate() {
46 49
     super.onCreate();
50
+    /* https://developers.google.com/web/tools/chrome-devtools/remote-debugging/webviews */
51
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
52
+      WebView.setWebContentsDebuggingEnabled(true);
53
+    }
47 54
     SoLoader.init(this, /* native exopackage */ false);
48 55
     initializeFlipper(this); // Remove this line if you don't want Flipper enabled
49 56
   }

+ 28
- 27
example/examples/Injection.tsx 查看文件

@@ -3,23 +3,23 @@ import {Text, View, ScrollView} from 'react-native';
3 3
 
4 4
 import WebView from 'react-native-webview';
5 5
 
6
-// const HTML = `
7
-// <!DOCTYPE html>
8
-// <html>
9
-//   <head>
10
-//       <meta charset="utf-8">
11
-//       <meta name="viewport" content="width=device-width, initial-scale=1">
12
-//       <title>iframe test</title>
13
-//   </head>
14
-//   <body>
15
-//     <p style="">beforeContentLoaded on the top frame <span id="before_failed" style="display: inline-block;">failed</span><span id="before_succeeded" style="display: none;">succeeded</span>!</p>
16
-//     <p style="">afterContentLoaded on the top frame <span id="after_failed" style="display: inline-block;">failed</span><span id="after_succeeded" style="display: none;">succeeded</span>!</p>
17
-//     <iframe src="https://birchlabs.co.uk/linguabrowse/infopages/obsol/iframe.html?v=1" name="iframe_0" style="width: 100%; height: 25px;"></iframe>
18
-//     <iframe src="https://birchlabs.co.uk/linguabrowse/infopages/obsol/iframe2.html?v=1" name="iframe_1" style="width: 100%; height: 25px;"></iframe>
19
-//     <iframe src="https://www.ebay.co.uk" name="iframe_2" style="width: 100%; height: 25px;"></iframe>
20
-//   </body>
21
-// </html>
22
-// `;
6
+const HTML = `
7
+<!DOCTYPE html>
8
+<html>
9
+  <head>
10
+      <meta charset="utf-8">
11
+      <meta name="viewport" content="width=device-width, initial-scale=1">
12
+      <title>iframe test</title>
13
+  </head>
14
+  <body>
15
+    <p style="">beforeContentLoaded on the top frame <span id="before_failed" style="display: inline-block;">failed</span><span id="before_succeeded" style="display: none;">succeeded</span>!</p>
16
+    <p style="">afterContentLoaded on the top frame <span id="after_failed" style="display: inline-block;">failed</span><span id="after_succeeded" style="display: none;">succeeded</span>!</p>
17
+    <iframe src="https://birchlabs.co.uk/linguabrowse/infopages/obsol/iframe.html?v=1" name="iframe_0" style="width: 100%; height: 25px;"></iframe>
18
+    <iframe src="https://birchlabs.co.uk/linguabrowse/infopages/obsol/iframe2.html?v=1" name="iframe_1" style="width: 100%; height: 25px;"></iframe>
19
+    <iframe src="https://www.ebay.co.uk" name="iframe_2" style="width: 100%; height: 25px;"></iframe>
20
+  </body>
21
+</html>
22
+`;
23 23
 
24 24
 type Props = {};
25 25
 type State = {
@@ -35,11 +35,12 @@ export default class Injection extends Component<Props, State> {
35 35
     return (
36 36
       <ScrollView>
37 37
         <View style={{ }}>
38
-          <View style={{ height: 300 }}>
38
+          <View style={{ height: 400 }}>
39 39
             <WebView
40 40
               /**
41
-               * This HTML is a copy of a multi-frame JS injection test that I had lying around.
42
-               * @see https://birchlabs.co.uk/linguabrowse/infopages/obsol/iframeTest.html
41
+               * This HTML is a copy of the hosted multi-frame JS injection test.
42
+               * I have found that Android doesn't support beforeContentLoaded for a hosted HTML webpage, yet does for a static source.
43
+               * The cause of this is unresolved.
43 44
                */
44 45
               // source={{ html: HTML }}
45 46
               source={{ uri: "https://birchlabs.co.uk/linguabrowse/infopages/obsol/rnw_iframe_test.html" }}
@@ -50,10 +51,12 @@ export default class Injection extends Component<Props, State> {
50 51
                * JS injection user scripts, consistent with current behaviour. This is undesirable,
51 52
                * so needs addressing in a follow-up PR. */
52 53
               onMessage={() => {}}
54
+              injectedJavaScriptBeforeContentLoadedForMainFrameOnly={false}
55
+              injectedJavaScriptForMainFrameOnly={false}
53 56
 
54 57
               /* We set this property in each frame */
55 58
               injectedJavaScriptBeforeContentLoaded={`
56
-              console.log("executing injectedJavaScriptBeforeContentLoaded...");
59
+              console.log("executing injectedJavaScriptBeforeContentLoaded... " + (new Date()).toString());
57 60
               if(typeof window.top.injectedIframesBeforeContentLoaded === "undefined"){
58 61
                 window.top.injectedIframesBeforeContentLoaded = [];
59 62
               }
@@ -84,12 +87,10 @@ export default class Injection extends Component<Props, State> {
84 87
                 console.log("wasn't window.top. Still going...");
85 88
               }
86 89
               `}
87
-              
88
-              injectedJavaScriptForMainFrameOnly={false}
89 90
 
90 91
               /* We read the colourToUse property in each frame to recolour each frame */
91 92
               injectedJavaScript={`
92
-              console.log("executing injectedJavaScript...");
93
+              console.log("executing injectedJavaScript... " + (new Date()).toString());
93 94
               if(typeof window.top.injectedIframesAfterContentLoaded === "undefined"){
94 95
                 window.top.injectedIframesAfterContentLoaded = [];
95 96
               }
@@ -119,7 +120,7 @@ export default class Injection extends Component<Props, State> {
119 120
                 // numberOfFramesAtAfterContentLoadedEle.id = "numberOfFramesAtAfterContentLoadedEle";
120 121
 
121 122
                 var namedFramesAtBeforeContentLoadedEle = document.createElement('p');
122
-                namedFramesAtBeforeContentLoadedEle.textContent = "Names of iframes that called beforeContentLoaded: " + JSON.stringify(window.top.injectedIframesBeforeContentLoaded);
123
+                namedFramesAtBeforeContentLoadedEle.textContent = "Names of iframes that called beforeContentLoaded: " + JSON.stringify(window.top.injectedIframesBeforeContentLoaded || []);
123 124
                 namedFramesAtBeforeContentLoadedEle.id = "namedFramesAtBeforeContentLoadedEle";
124 125
 
125 126
                 var namedFramesAtAfterContentLoadedEle = document.createElement('p');
@@ -147,8 +148,8 @@ export default class Injection extends Component<Props, State> {
147 148
         <Text>✅ If the main frame becomes orange, then top-frame injection both beforeContentLoaded and afterContentLoaded is supported.</Text>
148 149
         <Text>✅ If iframe_0, and iframe_1 become orange, then multi-frame injection beforeContentLoaded and afterContentLoaded is supported.</Text>
149 150
         <Text>✅ If the two texts say "beforeContentLoaded on the top frame succeeded!" and "afterContentLoaded on the top frame succeeded!", then both injection times are supported at least on the main frame.</Text>
150
-        <Text>⚠️ If either of the two iframes become coloured cyan, then for that given frame, JS injection succeeded after the content loaded, but didn't occur before the content loaded - please note that for iframes, this may not be a test failure, as it is not clear whether we would expect iframes to support an injection time of beforeContentLoaded anyway.</Text>
151
-        <Text>⚠️ If "Names of iframes that called beforeContentLoaded: " is [], then see above.</Text>
151
+        <Text> If either of the two iframes become coloured cyan, then for that given frame, JS injection succeeded after the content loaded, but didn't occur before the content loaded.</Text>
152
+        <Text> If "Names of iframes that called beforeContentLoaded: " is [], then see above.</Text>
152 153
         <Text>❌ If "Names of iframes that called afterContentLoaded: " is [], then afterContentLoaded is not supported in iframes.</Text>
153 154
         <Text>❌ If the main frame becomes coloured cyan, then JS injection succeeded after the content loaded, but didn't occur before the content loaded.</Text>
154 155
         <Text>❌ If the text "beforeContentLoaded on the top frame failed" remains unchanged, then JS injection has failed on the main frame before the content loaded.</Text>

+ 14
- 0
src/WebViewTypes.ts 查看文件

@@ -240,6 +240,8 @@ export interface CommonNativeWebViewProps extends ViewProps {
240 240
   incognito?: boolean;
241 241
   injectedJavaScript?: string;
242 242
   injectedJavaScriptBeforeContentLoaded?: string;
243
+  injectedJavaScriptForMainFrameOnly?: boolean;
244
+  injectedJavaScriptBeforeContentLoadedForMainFrameOnly?: boolean;
243 245
   javaScriptCanOpenWindowsAutomatically?: boolean;
244 246
   mediaPlaybackRequiresUserAction?: boolean;
245 247
   messagingEnabled: boolean;
@@ -915,6 +917,18 @@ export interface WebViewSharedProps extends ViewProps {
915 917
    */
916 918
   injectedJavaScriptBeforeContentLoaded?: string;
917 919
 
920
+  /**
921
+   * If `true` (default; mandatory for Android), loads the `injectedJavaScript` only into the main frame.
922
+   * If `false` (only supported on iOS and macOS), loads it into all frames (e.g. iframes).
923
+   */
924
+  injectedJavaScriptForMainFrameOnly?: boolean;
925
+
926
+  /**
927
+   * If `true` (default; mandatory for Android), loads the `injectedJavaScriptBeforeContentLoaded` only into the main frame.
928
+   * If `false` (only supported on iOS and macOS), loads it into all frames (e.g. iframes).
929
+   */
930
+  injectedJavaScriptBeforeContentLoadedForMainFrameOnly?: boolean;
931
+
918 932
   /**
919 933
    * Boolean value that determines whether a horizontal scroll indicator is
920 934
    * shown in the `WebView`. The default value is `true`.