Browse Source

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

Salvatore Randazzo 4 years ago
parent
commit
ac4e05e0f2
No account linked to committer's email address

+ 48
- 0
android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java View File

400
     ((RNCWebView) view).setInjectedJavaScript(injectedJavaScript);
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
   @ReactProp(name = "messagingEnabled")
418
   @ReactProp(name = "messagingEnabled")
404
   public void setMessagingEnabled(WebView view, boolean enabled) {
419
   public void setMessagingEnabled(WebView view, boolean enabled) {
405
     ((RNCWebView) view).setMessagingEnabled(enabled);
420
     ((RNCWebView) view).setMessagingEnabled(enabled);
753
       super.onPageStarted(webView, url, favicon);
768
       super.onPageStarted(webView, url, favicon);
754
       mLastLoadFailed = false;
769
       mLastLoadFailed = false;
755
 
770
 
771
+      RNCWebView reactWebView = (RNCWebView) webView;
772
+      reactWebView.callInjectedJavaScriptBeforeContentLoaded();       
773
+
756
       dispatchEvent(
774
       dispatchEvent(
757
         webView,
775
         webView,
758
         new TopLoadingStartEvent(
776
         new TopLoadingStartEvent(
1011
   protected static class RNCWebView extends WebView implements LifecycleEventListener {
1029
   protected static class RNCWebView extends WebView implements LifecycleEventListener {
1012
     protected @Nullable
1030
     protected @Nullable
1013
     String injectedJS;
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
     protected boolean messagingEnabled = false;
1042
     protected boolean messagingEnabled = false;
1015
     protected @Nullable
1043
     protected @Nullable
1016
     String messagingModuleName;
1044
     String messagingModuleName;
1105
       injectedJS = js;
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
     protected RNCWebViewBridge createRNCWebViewBridge(RNCWebView webView) {
1148
     protected RNCWebViewBridge createRNCWebViewBridge(RNCWebView webView) {
1109
       return new RNCWebViewBridge(webView);
1149
       return new RNCWebViewBridge(webView);
1110
     }
1150
     }
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
     public void onMessage(String message) {
1210
     public void onMessage(String message) {
1163
       ReactContext reactContext = (ReactContext) this.getContext();
1211
       ReactContext reactContext = (ReactContext) this.getContext();
1164
       RNCWebView mContext = this;
1212
       RNCWebView mContext = this;

+ 16
- 12
docs/Guide.md View File

88
   }
88
   }
89
 }
89
 }
90
 ```
90
 ```
91
+
91
 </details>
92
 </details>
92
 
93
 
93
 ### Controlling navigation state changes
94
 ### Controlling navigation state changes
104
   render() {
105
   render() {
105
     return (
106
     return (
106
       <WebView
107
       <WebView
107
-        ref={ref => (this.webview = ref)}
108
+        ref={(ref) => (this.webview = ref)}
108
         source={{ uri: 'https://reactnative.dev/' }}
109
         source={{ uri: 'https://reactnative.dev/' }}
109
         onNavigationStateChange={this.handleWebViewNavigationStateChange}
110
         onNavigationStateChange={this.handleWebViewNavigationStateChange}
110
       />
111
       />
111
     );
112
     );
112
   }
113
   }
113
 
114
 
114
-  handleWebViewNavigationStateChange = newNavState => {
115
+  handleWebViewNavigationStateChange = (newNavState) => {
115
     // newNavState looks something like this:
116
     // newNavState looks something like this:
116
     // {
117
     // {
117
     //   url?: string;
118
     //   url?: string;
240
 trigger calls to `onFileDownload`.
241
 trigger calls to `onFileDownload`.
241
 
242
 
242
 Example:
243
 Example:
244
+
243
 ```javascript
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
 To be able to save images to the gallery you need to specify this permission in your `ios/[project]/Info.plist` file:
252
 To be able to save images to the gallery you need to specify this permission in your `ios/[project]/Info.plist` file:
313
 
315
 
314
 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.
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
 <img alt="screenshot of Github repo" width="200" src="https://user-images.githubusercontent.com/1479215/53609254-e5dc9c00-3b7a-11e9-9118-bc4e520ce6ca.png" />
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
 
356
 
355
 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.
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
 > 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.
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
 > On Android, `injectedJavaScript` runs a method on the Android WebView called `evaluateJavascriptWithFallback`
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
 #### The `injectJavaScript` method
365
 #### The `injectJavaScript` method
363
 
366
 
382
     return (
385
     return (
383
       <View style={{ flex: 1 }}>
386
       <View style={{ flex: 1 }}>
384
         <WebView
387
         <WebView
385
-          ref={r => (this.webref = r)}
388
+          ref={(r) => (this.webref = r)}
386
           source={{
389
           source={{
387
             uri:
390
             uri:
388
               'https://github.com/react-native-community/react-native-webview',
391
               'https://github.com/react-native-community/react-native-webview',
435
       <View style={{ flex: 1 }}>
438
       <View style={{ flex: 1 }}>
436
         <WebView
439
         <WebView
437
           source={{ html }}
440
           source={{ html }}
438
-          onMessage={event => {
441
+          onMessage={(event) => {
439
             alert(event.nativeEvent.data);
442
             alert(event.nativeEvent.data);
440
           }}
443
           }}
441
         />
444
         />
471
 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)):
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
 ```jsx
476
 ```jsx
474
-const CustomHeaderWebView = props => {
477
+const CustomHeaderWebView = (props) => {
475
   const { uri, onLoadStart, ...restProps } = props;
478
   const { uri, onLoadStart, ...restProps } = props;
476
   const [currentURI, setURI] = useState(props.source.uri);
479
   const [currentURI, setURI] = useState(props.source.uri);
477
   const newSource = { ...props.source, uri: currentURI };
480
   const newSource = { ...props.source, uri: currentURI };
480
     <WebView
483
     <WebView
481
       {...restProps}
484
       {...restProps}
482
       source={newSource}
485
       source={newSource}
483
-      onShouldStartLoadWithRequest={request => {
486
+      onShouldStartLoadWithRequest={(request) => {
484
         // If we're loading the current URI, allow it to load
487
         // If we're loading the current URI, allow it to load
485
         if (request.url === currentURI) return true;
488
         if (request.url === currentURI) return true;
486
         // We're loading a new URL -- change state first
489
         // We're loading a new URL -- change state first
539
 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).
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
 ### Hardware Silence Switch
544
 ### Hardware Silence Switch
545
+
542
 There are some inconsistencies in how the hardware silence switch is handled between embedded `audio` and `video` elements and between iOS and Android platforms.
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
 Audio on `iOS` will be muted when the hardware silence switch is in the on position, unless the `ignoreSilentHardwareSwitch` parameter is set to true.
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 View File

190
 
190
 
191
 ### `injectedJavaScriptForMainFrameOnly`
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
 | Type   | Required | Platform |
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
 ### `injectedJavaScriptBeforeContentLoadedForMainFrameOnly`
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
 | Type   | Required | Platform |
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 View File

2
 
2
 
3
 import android.app.Application;
3
 import android.app.Application;
4
 import android.content.Context;
4
 import android.content.Context;
5
+import android.os.Build;
6
+import android.webkit.WebView;
7
+
5
 import com.facebook.react.PackageList;
8
 import com.facebook.react.PackageList;
6
 import com.facebook.react.ReactApplication;
9
 import com.facebook.react.ReactApplication;
7
 import com.facebook.react.ReactNativeHost;
10
 import com.facebook.react.ReactNativeHost;
44
   @Override
47
   @Override
45
   public void onCreate() {
48
   public void onCreate() {
46
     super.onCreate();
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
     SoLoader.init(this, /* native exopackage */ false);
54
     SoLoader.init(this, /* native exopackage */ false);
48
     initializeFlipper(this); // Remove this line if you don't want Flipper enabled
55
     initializeFlipper(this); // Remove this line if you don't want Flipper enabled
49
   }
56
   }

+ 28
- 27
example/examples/Injection.tsx View File

3
 
3
 
4
 import WebView from 'react-native-webview';
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
 type Props = {};
24
 type Props = {};
25
 type State = {
25
 type State = {
35
     return (
35
     return (
36
       <ScrollView>
36
       <ScrollView>
37
         <View style={{ }}>
37
         <View style={{ }}>
38
-          <View style={{ height: 300 }}>
38
+          <View style={{ height: 400 }}>
39
             <WebView
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
               // source={{ html: HTML }}
45
               // source={{ html: HTML }}
45
               source={{ uri: "https://birchlabs.co.uk/linguabrowse/infopages/obsol/rnw_iframe_test.html" }}
46
               source={{ uri: "https://birchlabs.co.uk/linguabrowse/infopages/obsol/rnw_iframe_test.html" }}
50
                * JS injection user scripts, consistent with current behaviour. This is undesirable,
51
                * JS injection user scripts, consistent with current behaviour. This is undesirable,
51
                * so needs addressing in a follow-up PR. */
52
                * so needs addressing in a follow-up PR. */
52
               onMessage={() => {}}
53
               onMessage={() => {}}
54
+              injectedJavaScriptBeforeContentLoadedForMainFrameOnly={false}
55
+              injectedJavaScriptForMainFrameOnly={false}
53
 
56
 
54
               /* We set this property in each frame */
57
               /* We set this property in each frame */
55
               injectedJavaScriptBeforeContentLoaded={`
58
               injectedJavaScriptBeforeContentLoaded={`
56
-              console.log("executing injectedJavaScriptBeforeContentLoaded...");
59
+              console.log("executing injectedJavaScriptBeforeContentLoaded... " + (new Date()).toString());
57
               if(typeof window.top.injectedIframesBeforeContentLoaded === "undefined"){
60
               if(typeof window.top.injectedIframesBeforeContentLoaded === "undefined"){
58
                 window.top.injectedIframesBeforeContentLoaded = [];
61
                 window.top.injectedIframesBeforeContentLoaded = [];
59
               }
62
               }
84
                 console.log("wasn't window.top. Still going...");
87
                 console.log("wasn't window.top. Still going...");
85
               }
88
               }
86
               `}
89
               `}
87
-              
88
-              injectedJavaScriptForMainFrameOnly={false}
89
 
90
 
90
               /* We read the colourToUse property in each frame to recolour each frame */
91
               /* We read the colourToUse property in each frame to recolour each frame */
91
               injectedJavaScript={`
92
               injectedJavaScript={`
92
-              console.log("executing injectedJavaScript...");
93
+              console.log("executing injectedJavaScript... " + (new Date()).toString());
93
               if(typeof window.top.injectedIframesAfterContentLoaded === "undefined"){
94
               if(typeof window.top.injectedIframesAfterContentLoaded === "undefined"){
94
                 window.top.injectedIframesAfterContentLoaded = [];
95
                 window.top.injectedIframesAfterContentLoaded = [];
95
               }
96
               }
119
                 // numberOfFramesAtAfterContentLoadedEle.id = "numberOfFramesAtAfterContentLoadedEle";
120
                 // numberOfFramesAtAfterContentLoadedEle.id = "numberOfFramesAtAfterContentLoadedEle";
120
 
121
 
121
                 var namedFramesAtBeforeContentLoadedEle = document.createElement('p');
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
                 namedFramesAtBeforeContentLoadedEle.id = "namedFramesAtBeforeContentLoadedEle";
124
                 namedFramesAtBeforeContentLoadedEle.id = "namedFramesAtBeforeContentLoadedEle";
124
 
125
 
125
                 var namedFramesAtAfterContentLoadedEle = document.createElement('p');
126
                 var namedFramesAtAfterContentLoadedEle = document.createElement('p');
147
         <Text>✅ If the main frame becomes orange, then top-frame injection both beforeContentLoaded and afterContentLoaded is supported.</Text>
148
         <Text>✅ If the main frame becomes orange, then top-frame injection both beforeContentLoaded and afterContentLoaded is supported.</Text>
148
         <Text>✅ If iframe_0, and iframe_1 become orange, then multi-frame injection beforeContentLoaded and afterContentLoaded is supported.</Text>
149
         <Text>✅ If iframe_0, and iframe_1 become orange, then multi-frame injection beforeContentLoaded and afterContentLoaded is supported.</Text>
149
         <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 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
         <Text>❌ If "Names of iframes that called afterContentLoaded: " is [], then afterContentLoaded is not supported in iframes.</Text>
153
         <Text>❌ If "Names of iframes that called afterContentLoaded: " is [], then afterContentLoaded is not supported in iframes.</Text>
153
         <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
         <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
         <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>
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 View File

240
   incognito?: boolean;
240
   incognito?: boolean;
241
   injectedJavaScript?: string;
241
   injectedJavaScript?: string;
242
   injectedJavaScriptBeforeContentLoaded?: string;
242
   injectedJavaScriptBeforeContentLoaded?: string;
243
+  injectedJavaScriptForMainFrameOnly?: boolean;
244
+  injectedJavaScriptBeforeContentLoadedForMainFrameOnly?: boolean;
243
   javaScriptCanOpenWindowsAutomatically?: boolean;
245
   javaScriptCanOpenWindowsAutomatically?: boolean;
244
   mediaPlaybackRequiresUserAction?: boolean;
246
   mediaPlaybackRequiresUserAction?: boolean;
245
   messagingEnabled: boolean;
247
   messagingEnabled: boolean;
915
    */
917
    */
916
   injectedJavaScriptBeforeContentLoaded?: string;
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
    * Boolean value that determines whether a horizontal scroll indicator is
933
    * Boolean value that determines whether a horizontal scroll indicator is
920
    * shown in the `WebView`. The default value is `true`.
934
    * shown in the `WebView`. The default value is `true`.