Jamie Birch 4 years ago
parent
commit
16ca1a0f01
No account linked to committer's email address

+ 27
- 1
docs/Reference.md View File

7
 - [`source`](Reference.md#source)
7
 - [`source`](Reference.md#source)
8
 - [`automaticallyAdjustContentInsets`](Reference.md#automaticallyadjustcontentinsets)
8
 - [`automaticallyAdjustContentInsets`](Reference.md#automaticallyadjustcontentinsets)
9
 - [`injectedJavaScript`](Reference.md#injectedjavascript)
9
 - [`injectedJavaScript`](Reference.md#injectedjavascript)
10
-- [`injectedJavaScriptBeforeContentLoaded`](Reference.md#injectedJavaScriptBeforeContentLoaded)
10
+- [`injectedJavaScriptBeforeContentLoaded`](Reference.md#injectedjavascriptbeforecontentloaded)
11
+- [`injectedJavaScriptForMainFrameOnly`](Reference.md#injectedjavascriptformainframeonly)
12
+- [`injectedJavaScriptBeforeContentLoadedForMainFrameOnly`](Reference.md#injectedjavascriptbeforecontentloadedformainframeonly)
11
 - [`mediaPlaybackRequiresUserAction`](Reference.md#mediaplaybackrequiresuseraction)
13
 - [`mediaPlaybackRequiresUserAction`](Reference.md#mediaplaybackrequiresuseraction)
12
 - [`nativeConfig`](Reference.md#nativeconfig)
14
 - [`nativeConfig`](Reference.md#nativeconfig)
13
 - [`onError`](Reference.md#onerror)
15
 - [`onError`](Reference.md#onerror)
174
 
176
 
175
 ---
177
 ---
176
 
178
 
179
+### `injectedJavaScriptForMainFrameOnly`
180
+
181
+If `true` (default), loads the `injectedJavaScript` only into the main frame.
182
+
183
+If `false`, loads it into all frames (e.g. iframes).
184
+
185
+| Type   | Required | Platform |
186
+| ------ | -------- | -------- |
187
+| bool | No       | iOS       |
188
+
189
+---
190
+
191
+### `injectedJavaScriptBeforeContentLoadedForMainFrameOnly`
192
+
193
+If `true` (default), loads the `injectedJavaScriptBeforeContentLoaded` only into the main frame.
194
+
195
+If `false`, loads it into all frames (e.g. iframes).
196
+
197
+| Type   | Required | Platform |
198
+| ------ | -------- | -------- |
199
+| bool | No       | iOS       |
200
+
201
+---
202
+
177
 ### `mediaPlaybackRequiresUserAction`
203
 ### `mediaPlaybackRequiresUserAction`
178
 
204
 
179
 Boolean that determines whether HTML5 audio and video requires the user to tap them before they start playing. The default value is `true`. (Android API minimum version 17).
205
 Boolean that determines whether HTML5 audio and video requires the user to tap them before they start playing. The default value is `true`. (Android API minimum version 17).

+ 14
- 0
example/App.tsx View File

14
 import Scrolling from './examples/Scrolling';
14
 import Scrolling from './examples/Scrolling';
15
 import Background from './examples/Background';
15
 import Background from './examples/Background';
16
 import Uploads from './examples/Uploads';
16
 import Uploads from './examples/Uploads';
17
+import Injection from './examples/Injection';
17
 
18
 
18
 const TESTS = {
19
 const TESTS = {
19
   Alerts: {
20
   Alerts: {
48
       return <Uploads />;
49
       return <Uploads />;
49
     },
50
     },
50
   },
51
   },
52
+  Injection: {
53
+    title: 'Injection',
54
+    testId: 'injection',
55
+    description: 'Injection test',
56
+    render() {
57
+      return <Injection />;
58
+    },
59
+  },
51
 };
60
 };
52
 
61
 
53
 type Props = {};
62
 type Props = {};
101
             title="Background"
110
             title="Background"
102
             onPress={() => this._changeTest('Background')}
111
             onPress={() => this._changeTest('Background')}
103
           />
112
           />
113
+          <Button
114
+            testID="testType_injection"
115
+            title="Injection"
116
+            onPress={() => this._changeTest('Injection')}
117
+          />
104
           {Platform.OS === 'android' && <Button
118
           {Platform.OS === 'android' && <Button
105
             testID="testType_uploads"
119
             testID="testType_uploads"
106
             title="Uploads"
120
             title="Uploads"

+ 68
- 0
example/examples/Injection.tsx View File

1
+import React, {Component} from 'react';
2
+import {Text, View, ScrollView} from 'react-native';
3
+
4
+import WebView from 'react-native-webview';
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
+		<iframe src="https://birchlabs.co.uk/linguabrowse/infopages/obsol/iframe.html?v=1" name="iframe_0" style="width: 100%; height: 25px;"></iframe>
16
+		<iframe src="https://birchlabs.co.uk/linguabrowse/infopages/obsol/iframe2.html?v=1" name="iframe_1" style="width: 100%; height: 25px;"></iframe>
17
+		<iframe src="https://www.ebay.co.uk" name="iframe_2" style="width: 100%; height: 25px;"></iframe>
18
+	</body>
19
+</html>
20
+`;
21
+
22
+type Props = {};
23
+type State = {
24
+  backgroundColor: string,
25
+};
26
+
27
+export default class Injection extends Component<Props, State> {
28
+  state = {
29
+    backgroundColor: '#FF00FF00'
30
+  };
31
+
32
+  render() {
33
+    return (
34
+      <ScrollView>
35
+        <View style={{ }}>
36
+          <View style={{ height: 120 }}>
37
+            <WebView
38
+              /**
39
+               * This HTML is a copy of a multi-frame JS injection test that I had lying around.
40
+               * @see https://birchlabs.co.uk/linguabrowse/infopages/obsol/iframeTest.html
41
+               */
42
+              source={{ html: HTML }}
43
+              automaticallyAdjustContentInsets={false}
44
+              style={{backgroundColor:'#00000000'}}
45
+              
46
+              /* Must be populated in order for `messagingEnabled` to be `true` to activate the
47
+               * JS injection user scripts, consistent with current behaviour. This is undesirable,
48
+               * so needs addressing in a follow-up PR. */
49
+              onMessage={() => {}}
50
+              /* We set this property in each frame */
51
+              injectedJavaScriptBeforeContentLoaded={`window.self.colourToUse = "orange";`}
52
+              injectedJavaScriptForMainFrameOnly={false}
53
+              /* We read the colourToUse property in each frame to recolour each frame */
54
+              injectedJavaScript={`window.self.document.body.style.backgroundColor = window.self.colourToUse;`}
55
+            />
56
+          </View>
57
+        </View>
58
+        <Text>This test presents three iframes: iframe_0 (yellow); iframe_1 (pink); and iframe_2 (transparent, because its 'X-Frame-Options' is set to 'SAMEORIGIN').</Text>
59
+        <Text>Before injection, the main frame's background is the browser's default value (transparent or white) and each frame has its natural colour.</Text>
60
+        <Text>1) At injection time "beforeContentLoaded", a variable will be set in each frame to set 'orange' as the "colour to be used".</Text>
61
+        <Text>2) At injection time "afterContentLoaded", that variable will be read, and thus the colour orange will be injected into all frames.</Text>
62
+        <Text>✅ If the main frame, iframe_0, and iframe_1 all become orange, then multi-frame injection is supported and both injection times are supported.</Text>
63
+        <Text>❌ If no frames become orange, then only one (or neither) of the injection times are supported, or even injection into the main frame is failing.</Text>
64
+        <Text>❌ If only the main frame becomes orange, then multi-frame injection is not supported.</Text>
65
+      </ScrollView>
66
+    );
67
+  }
68
+}

+ 2
- 2
example/ios/Podfile.lock View File

182
     - React-cxxreact (= 0.61.5)
182
     - React-cxxreact (= 0.61.5)
183
     - React-jsi (= 0.61.5)
183
     - React-jsi (= 0.61.5)
184
   - React-jsinspector (0.61.5)
184
   - React-jsinspector (0.61.5)
185
-  - react-native-webview (8.0.6):
185
+  - react-native-webview (8.1.1):
186
     - React
186
     - React
187
   - React-RCTActionSheet (0.61.5):
187
   - React-RCTActionSheet (0.61.5):
188
     - React-Core/RCTActionSheetHeaders (= 0.61.5)
188
     - React-Core/RCTActionSheetHeaders (= 0.61.5)
326
   React-jsi: cb2cd74d7ccf4cffb071a46833613edc79cdf8f7
326
   React-jsi: cb2cd74d7ccf4cffb071a46833613edc79cdf8f7
327
   React-jsiexecutor: d5525f9ed5f782fdbacb64b9b01a43a9323d2386
327
   React-jsiexecutor: d5525f9ed5f782fdbacb64b9b01a43a9323d2386
328
   React-jsinspector: fa0ecc501688c3c4c34f28834a76302233e29dc0
328
   React-jsinspector: fa0ecc501688c3c4c34f28834a76302233e29dc0
329
-  react-native-webview: 222d83c9c489e09b5d3541519110a637490ad4fa
329
+  react-native-webview: 0ec2ca34f52e60892e4a9141508751588d45a518
330
   React-RCTActionSheet: 600b4d10e3aea0913b5a92256d2719c0cdd26d76
330
   React-RCTActionSheet: 600b4d10e3aea0913b5a92256d2719c0cdd26d76
331
   React-RCTAnimation: 791a87558389c80908ed06cc5dfc5e7920dfa360
331
   React-RCTAnimation: 791a87558389c80908ed06cc5dfc5e7920dfa360
332
   React-RCTBlob: d89293cc0236d9cb0933d85e430b0bbe81ad1d72
332
   React-RCTBlob: d89293cc0236d9cb0933d85e430b0bbe81ad1d72

+ 2
- 0
ios/RNCWebView.h View File

31
 @property (nonatomic, assign) BOOL messagingEnabled;
31
 @property (nonatomic, assign) BOOL messagingEnabled;
32
 @property (nonatomic, copy) NSString * _Nullable injectedJavaScript;
32
 @property (nonatomic, copy) NSString * _Nullable injectedJavaScript;
33
 @property (nonatomic, copy) NSString * _Nullable injectedJavaScriptBeforeContentLoaded;
33
 @property (nonatomic, copy) NSString * _Nullable injectedJavaScriptBeforeContentLoaded;
34
+@property (nonatomic, assign) BOOL injectedJavaScriptForMainFrameOnly;
35
+@property (nonatomic, assign) BOOL injectedJavaScriptBeforeContentLoadedForMainFrameOnly;
34
 @property (nonatomic, assign) BOOL scrollEnabled;
36
 @property (nonatomic, assign) BOOL scrollEnabled;
35
 @property (nonatomic, assign) BOOL sharedCookiesEnabled;
37
 @property (nonatomic, assign) BOOL sharedCookiesEnabled;
36
 @property (nonatomic, assign) BOOL pagingEnabled;
38
 @property (nonatomic, assign) BOOL pagingEnabled;

+ 179
- 118
ios/RNCWebView.m View File

81
 #else
81
 #else
82
 @property (nonatomic, copy) RNCWKWebView *webView;
82
 @property (nonatomic, copy) RNCWKWebView *webView;
83
 #endif // !TARGET_OS_OSX
83
 #endif // !TARGET_OS_OSX
84
+@property (nonatomic, strong) WKUserScript *postMessageScript;
85
+@property (nonatomic, strong) WKUserScript *atStartScript;
86
+@property (nonatomic, strong) WKUserScript *atEndScript;
84
 @end
87
 @end
85
 
88
 
86
 @implementation RNCWebView
89
 @implementation RNCWebView
122
     _automaticallyAdjustContentInsets = YES;
125
     _automaticallyAdjustContentInsets = YES;
123
     _contentInset = UIEdgeInsetsZero;
126
     _contentInset = UIEdgeInsetsZero;
124
     _savedKeyboardDisplayRequiresUserAction = YES;
127
     _savedKeyboardDisplayRequiresUserAction = YES;
125
-#if !TARGET_OS_OSX
128
+    #if !TARGET_OS_OSX
126
     _savedStatusBarStyle = RCTSharedApplication().statusBarStyle;
129
     _savedStatusBarStyle = RCTSharedApplication().statusBarStyle;
127
     _savedStatusBarHidden = RCTSharedApplication().statusBarHidden;
130
     _savedStatusBarHidden = RCTSharedApplication().statusBarHidden;
128
-#endif // !TARGET_OS_OSX
131
+    #endif // !TARGET_OS_OSX
132
+    _injectedJavaScript = nil;
133
+    _injectedJavaScriptForMainFrameOnly = YES;
134
+    _injectedJavaScriptBeforeContentLoaded = nil;
135
+    _injectedJavaScriptBeforeContentLoadedForMainFrameOnly = YES;
129
 
136
 
130
 #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
137
 #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
131
     _savedContentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
138
     _savedContentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
206
   // Shim the HTML5 history API:
213
   // Shim the HTML5 history API:
207
   [wkWebViewConfig.userContentController addScriptMessageHandler:[[RNCWeakScriptMessageDelegate alloc] initWithDelegate:self]
214
   [wkWebViewConfig.userContentController addScriptMessageHandler:[[RNCWeakScriptMessageDelegate alloc] initWithDelegate:self]
208
                                                             name:HistoryShimName];
215
                                                             name:HistoryShimName];
209
-  NSString *source = [NSString stringWithFormat:
210
-    @"(function(history) {\n"
211
-    "  function notify(type) {\n"
212
-    "    setTimeout(function() {\n"
213
-    "      window.webkit.messageHandlers.%@.postMessage(type)\n"
214
-    "    }, 0)\n"
215
-    "  }\n"
216
-    "  function shim(f) {\n"
217
-    "    return function pushState() {\n"
218
-    "      notify('other')\n"
219
-    "      return f.apply(history, arguments)\n"
220
-    "    }\n"
221
-    "  }\n"
222
-    "  history.pushState = shim(history.pushState)\n"
223
-    "  history.replaceState = shim(history.replaceState)\n"
224
-    "  window.addEventListener('popstate', function() {\n"
225
-    "    notify('backforward')\n"
226
-    "  })\n"
227
-    "})(window.history)\n", HistoryShimName
228
-  ];
229
-  WKUserScript *script = [[WKUserScript alloc] initWithSource:source injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
230
-  [wkWebViewConfig.userContentController addUserScript:script];
231
-
232
-  if (_messagingEnabled) {
233
-    [wkWebViewConfig.userContentController addScriptMessageHandler:[[RNCWeakScriptMessageDelegate alloc] initWithDelegate:self]
234
-                                                              name:MessageHandlerName];
235
-
236
-    NSString *source = [NSString stringWithFormat:
237
-      @"window.%@ = {"
238
-       "  postMessage: function (data) {"
239
-       "    window.webkit.messageHandlers.%@.postMessage(String(data));"
240
-       "  }"
241
-       "};", MessageHandlerName, MessageHandlerName
242
-    ];
243
-
244
-    WKUserScript *script = [[WKUserScript alloc] initWithSource:source injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
245
-    [wkWebViewConfig.userContentController addUserScript:script];
246
-      
247
-    if (_injectedJavaScriptBeforeContentLoaded) {
248
-      // If user has provided an injectedJavascript prop, execute it at the start of the document
249
-      WKUserScript *injectedScript = [[WKUserScript alloc] initWithSource:_injectedJavaScriptBeforeContentLoaded injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
250
-      [wkWebViewConfig.userContentController addUserScript:injectedScript];
251
-    }
252
-  }
216
+  [self resetupScripts:wkWebViewConfig];
253
 
217
 
254
 #if !TARGET_OS_OSX
218
 #if !TARGET_OS_OSX
255
   wkWebViewConfig.allowsInlineMediaPlayback = _allowsInlineMediaPlayback;
219
   wkWebViewConfig.allowsInlineMediaPlayback = _allowsInlineMediaPlayback;
266
   if (_applicationNameForUserAgent) {
230
   if (_applicationNameForUserAgent) {
267
       wkWebViewConfig.applicationNameForUserAgent = [NSString stringWithFormat:@"%@ %@", wkWebViewConfig.applicationNameForUserAgent, _applicationNameForUserAgent];
231
       wkWebViewConfig.applicationNameForUserAgent = [NSString stringWithFormat:@"%@ %@", wkWebViewConfig.applicationNameForUserAgent, _applicationNameForUserAgent];
268
   }
232
   }
269
-
270
-  if(_sharedCookiesEnabled) {
271
-    // More info to sending cookies with WKWebView
272
-    // https://stackoverflow.com/questions/26573137/can-i-set-the-cookies-to-be-used-by-a-wkwebview/26577303#26577303
273
-    if (@available(iOS 11.0, *)) {
274
-      // Set Cookies in iOS 11 and above, initialize websiteDataStore before setting cookies
275
-      // See also https://forums.developer.apple.com/thread/97194
276
-      // check if websiteDataStore has not been initialized before
277
-      if(!_incognito && !_cacheEnabled) {
278
-        wkWebViewConfig.websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
279
-      }
280
-      for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
281
-        [wkWebViewConfig.websiteDataStore.httpCookieStore setCookie:cookie completionHandler:nil];
282
-      }
283
-    } else {
284
-      NSMutableString *script = [NSMutableString string];
285
-
286
-      // Clear all existing cookies in a direct called function. This ensures that no
287
-      // javascript error will break the web content javascript.
288
-      // We keep this code here, if someone requires that Cookies are also removed within the
289
-      // the WebView and want to extends the current sharedCookiesEnabled option with an
290
-      // additional property.
291
-      // Generates JS: document.cookie = "key=; Expires=Thu, 01 Jan 1970 00:00:01 GMT;"
292
-      // for each cookie which is already available in the WebView context.
293
-      /*
294
-      [script appendString:@"(function () {\n"];
295
-      [script appendString:@"  var cookies = document.cookie.split('; ');\n"];
296
-      [script appendString:@"  for (var i = 0; i < cookies.length; i++) {\n"];
297
-      [script appendString:@"    if (cookies[i].indexOf('=') !== -1) {\n"];
298
-      [script appendString:@"      document.cookie = cookies[i].split('=')[0] + '=; Expires=Thu, 01 Jan 1970 00:00:01 GMT';\n"];
299
-      [script appendString:@"    }\n"];
300
-      [script appendString:@"  }\n"];
301
-      [script appendString:@"})();\n\n"];
302
-      */
303
-
304
-      // Set cookies in a direct called function. This ensures that no
305
-      // javascript error will break the web content javascript.
306
-        // Generates JS: document.cookie = "key=value; Path=/; Expires=Thu, 01 Jan 20xx 00:00:01 GMT;"
307
-      // for each cookie which is available in the application context.
308
-      [script appendString:@"(function () {\n"];
309
-      for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
310
-        [script appendFormat:@"document.cookie = %@ + '=' + %@",
311
-          RCTJSONStringify(cookie.name, NULL),
312
-          RCTJSONStringify(cookie.value, NULL)];
313
-        if (cookie.path) {
314
-          [script appendFormat:@" + '; Path=' + %@", RCTJSONStringify(cookie.path, NULL)];
315
-        }
316
-        if (cookie.expiresDate) {
317
-          [script appendFormat:@" + '; Expires=' + new Date(%f).toUTCString()",
318
-            cookie.expiresDate.timeIntervalSince1970 * 1000
319
-          ];
320
-        }
321
-        [script appendString:@";\n"];
322
-      }
323
-      [script appendString:@"})();\n"];
324
-
325
-      WKUserScript* cookieInScript = [[WKUserScript alloc] initWithSource:script
326
-                                                            injectionTime:WKUserScriptInjectionTimeAtDocumentStart
327
-                                                         forMainFrameOnly:YES];
328
-      [wkWebViewConfig.userContentController addUserScript:cookieInScript];
329
-    }
330
-  }
331
   
233
   
332
   return wkWebViewConfig;
234
   return wkWebViewConfig;
333
 }
235
 }
1136
 - (void)webView:(WKWebView *)webView
1038
 - (void)webView:(WKWebView *)webView
1137
   didFinishNavigation:(WKNavigation *)navigation
1039
   didFinishNavigation:(WKNavigation *)navigation
1138
 {
1040
 {
1139
-   if (_injectedJavaScript) {
1140
-     [self evaluateJS: _injectedJavaScript thenCall: ^(NSString *jsEvaluationValue) {
1141
-       NSMutableDictionary *event = [self baseEvent];
1142
-       event[@"jsEvaluationValue"] = jsEvaluationValue;
1143
-
1144
-       if (self.onLoadingFinish) {
1145
-         self.onLoadingFinish(event);
1146
-       }
1147
-     }];
1148
-   } else if (_onLoadingFinish) {
1041
+  if (_onLoadingFinish) {
1149
     _onLoadingFinish([self baseEvent]);
1042
     _onLoadingFinish([self baseEvent]);
1150
   }
1043
   }
1151
 }
1044
 }
1194
 }
1087
 }
1195
 #endif // !TARGET_OS_OSX
1088
 #endif // !TARGET_OS_OSX
1196
 
1089
 
1090
+
1091
+- (void)setInjectedJavaScript:(NSString *)source {
1092
+  _injectedJavaScript = source;
1093
+  
1094
+  self.atEndScript = source == nil ? nil : [[WKUserScript alloc] initWithSource:source
1095
+      injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
1096
+    forMainFrameOnly:_injectedJavaScriptForMainFrameOnly];
1097
+  
1098
+  if(_webView != nil){
1099
+    [self resetupScripts:_webView.configuration];
1100
+  }
1101
+}
1102
+
1103
+- (void)setInjectedJavaScriptBeforeContentLoaded:(NSString *)source {
1104
+  _injectedJavaScriptBeforeContentLoaded = source;
1105
+  
1106
+  self.atStartScript = source == nil ? nil : [[WKUserScript alloc] initWithSource:source
1107
+       injectionTime:WKUserScriptInjectionTimeAtDocumentStart
1108
+    forMainFrameOnly:_injectedJavaScriptBeforeContentLoadedForMainFrameOnly];
1109
+  
1110
+  if(_webView != nil){
1111
+    [self resetupScripts:_webView.configuration];
1112
+  }
1113
+}
1114
+
1115
+- (void)setInjectedJavaScriptForMainFrameOnly:(BOOL)mainFrameOnly {
1116
+  _injectedJavaScriptForMainFrameOnly = mainFrameOnly;
1117
+  [self setInjectedJavaScript:_injectedJavaScript];
1118
+}
1119
+
1120
+- (void)setInjectedJavaScriptBeforeContentLoadedForMainFrameOnly:(BOOL)mainFrameOnly {
1121
+  _injectedJavaScriptBeforeContentLoadedForMainFrameOnly = mainFrameOnly;
1122
+  [self setInjectedJavaScriptBeforeContentLoaded:_injectedJavaScriptBeforeContentLoaded];
1123
+}
1124
+
1125
+- (void)setMessagingEnabled:(BOOL)messagingEnabled {
1126
+  _messagingEnabled = messagingEnabled;
1127
+  
1128
+  self.postMessageScript = _messagingEnabled ?
1129
+  [
1130
+   [WKUserScript alloc]
1131
+   initWithSource: [
1132
+                    NSString
1133
+                    stringWithFormat:
1134
+                    @"window.%@ = {"
1135
+                    "  postMessage: function (data) {"
1136
+                    "    window.webkit.messageHandlers.%@.postMessage(String(data));"
1137
+                    "  }"
1138
+                    "};", MessageHandlerName, MessageHandlerName
1139
+                    ]
1140
+   injectionTime:WKUserScriptInjectionTimeAtDocumentStart
1141
+   /* TODO: For a separate (minor) PR: use logic like this (as react-native-wkwebview does) so that messaging can be used in all frames if desired.
1142
+    *       I am keeping it as YES for consistency with previous behaviour. */
1143
+   // forMainFrameOnly:_messagingEnabledForMainFrameOnly
1144
+   forMainFrameOnly:YES
1145
+   ] :
1146
+  nil;
1147
+  
1148
+  if(_webView != nil){
1149
+    [self resetupScripts:_webView.configuration];
1150
+  }
1151
+}
1152
+
1153
+- (void)resetupScripts:(WKWebViewConfiguration *)wkWebViewConfig {
1154
+  [wkWebViewConfig.userContentController removeAllUserScripts];
1155
+  [wkWebViewConfig.userContentController removeScriptMessageHandlerForName:MessageHandlerName];
1156
+  
1157
+  NSString *html5HistoryAPIShimSource = [NSString stringWithFormat:
1158
+    @"(function(history) {\n"
1159
+    "  function notify(type) {\n"
1160
+    "    setTimeout(function() {\n"
1161
+    "      window.webkit.messageHandlers.%@.postMessage(type)\n"
1162
+    "    }, 0)\n"
1163
+    "  }\n"
1164
+    "  function shim(f) {\n"
1165
+    "    return function pushState() {\n"
1166
+    "      notify('other')\n"
1167
+    "      return f.apply(history, arguments)\n"
1168
+    "    }\n"
1169
+    "  }\n"
1170
+    "  history.pushState = shim(history.pushState)\n"
1171
+    "  history.replaceState = shim(history.replaceState)\n"
1172
+    "  window.addEventListener('popstate', function() {\n"
1173
+    "    notify('backforward')\n"
1174
+    "  })\n"
1175
+    "})(window.history)\n", HistoryShimName
1176
+  ];
1177
+  WKUserScript *script = [[WKUserScript alloc] initWithSource:html5HistoryAPIShimSource injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
1178
+  [wkWebViewConfig.userContentController addUserScript:script];
1179
+  
1180
+  if(_sharedCookiesEnabled) {
1181
+    // More info to sending cookies with WKWebView
1182
+    // https://stackoverflow.com/questions/26573137/can-i-set-the-cookies-to-be-used-by-a-wkwebview/26577303#26577303
1183
+    if (@available(iOS 11.0, *)) {
1184
+      // Set Cookies in iOS 11 and above, initialize websiteDataStore before setting cookies
1185
+      // See also https://forums.developer.apple.com/thread/97194
1186
+      // check if websiteDataStore has not been initialized before
1187
+      if(!_incognito && !_cacheEnabled) {
1188
+        wkWebViewConfig.websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
1189
+      }
1190
+      for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
1191
+        [wkWebViewConfig.websiteDataStore.httpCookieStore setCookie:cookie completionHandler:nil];
1192
+      }
1193
+    } else {
1194
+      NSMutableString *script = [NSMutableString string];
1195
+
1196
+      // Clear all existing cookies in a direct called function. This ensures that no
1197
+      // javascript error will break the web content javascript.
1198
+      // We keep this code here, if someone requires that Cookies are also removed within the
1199
+      // the WebView and want to extends the current sharedCookiesEnabled option with an
1200
+      // additional property.
1201
+      // Generates JS: document.cookie = "key=; Expires=Thu, 01 Jan 1970 00:00:01 GMT;"
1202
+      // for each cookie which is already available in the WebView context.
1203
+      /*
1204
+      [script appendString:@"(function () {\n"];
1205
+      [script appendString:@"  var cookies = document.cookie.split('; ');\n"];
1206
+      [script appendString:@"  for (var i = 0; i < cookies.length; i++) {\n"];
1207
+      [script appendString:@"    if (cookies[i].indexOf('=') !== -1) {\n"];
1208
+      [script appendString:@"      document.cookie = cookies[i].split('=')[0] + '=; Expires=Thu, 01 Jan 1970 00:00:01 GMT';\n"];
1209
+      [script appendString:@"    }\n"];
1210
+      [script appendString:@"  }\n"];
1211
+      [script appendString:@"})();\n\n"];
1212
+      */
1213
+
1214
+      // Set cookies in a direct called function. This ensures that no
1215
+      // javascript error will break the web content javascript.
1216
+        // Generates JS: document.cookie = "key=value; Path=/; Expires=Thu, 01 Jan 20xx 00:00:01 GMT;"
1217
+      // for each cookie which is available in the application context.
1218
+      [script appendString:@"(function () {\n"];
1219
+      for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
1220
+        [script appendFormat:@"document.cookie = %@ + '=' + %@",
1221
+          RCTJSONStringify(cookie.name, NULL),
1222
+          RCTJSONStringify(cookie.value, NULL)];
1223
+        if (cookie.path) {
1224
+          [script appendFormat:@" + '; Path=' + %@", RCTJSONStringify(cookie.path, NULL)];
1225
+        }
1226
+        if (cookie.expiresDate) {
1227
+          [script appendFormat:@" + '; Expires=' + new Date(%f).toUTCString()",
1228
+            cookie.expiresDate.timeIntervalSince1970 * 1000
1229
+          ];
1230
+        }
1231
+        [script appendString:@";\n"];
1232
+      }
1233
+      [script appendString:@"})();\n"];
1234
+
1235
+      WKUserScript* cookieInScript = [[WKUserScript alloc] initWithSource:script
1236
+                                                            injectionTime:WKUserScriptInjectionTimeAtDocumentStart
1237
+                                                         forMainFrameOnly:YES];
1238
+      [wkWebViewConfig.userContentController addUserScript:cookieInScript];
1239
+    }
1240
+  }
1241
+  
1242
+  if(_messagingEnabled){
1243
+    if (self.postMessageScript){
1244
+      [wkWebViewConfig.userContentController addScriptMessageHandler:[[RNCWeakScriptMessageDelegate alloc] initWithDelegate:self]
1245
+                                                                       name:MessageHandlerName];
1246
+      [wkWebViewConfig.userContentController addUserScript:self.postMessageScript];
1247
+    }
1248
+    // FIXME: For a separate (minor) PR: these two shouldn't be gated by messagingEnabled; just keeping consistency with previous behaviour.
1249
+    if (self.atStartScript) {
1250
+      [wkWebViewConfig.userContentController addUserScript:self.atStartScript];
1251
+    }
1252
+    if (self.atEndScript) {
1253
+      [wkWebViewConfig.userContentController addUserScript:self.atEndScript];
1254
+    }
1255
+  }
1256
+}
1257
+
1197
 - (NSURLRequest *)requestForSource:(id)json {
1258
 - (NSURLRequest *)requestForSource:(id)json {
1198
   NSURLRequest *request = [RCTConvert NSURLRequest:self.source];
1259
   NSURLRequest *request = [RCTConvert NSURLRequest:self.source];
1199
 
1260
 

+ 2
- 0
ios/RNCWebViewManager.m View File

43
 RCT_EXPORT_VIEW_PROPERTY(onContentProcessDidTerminate, RCTDirectEventBlock)
43
 RCT_EXPORT_VIEW_PROPERTY(onContentProcessDidTerminate, RCTDirectEventBlock)
44
 RCT_EXPORT_VIEW_PROPERTY(injectedJavaScript, NSString)
44
 RCT_EXPORT_VIEW_PROPERTY(injectedJavaScript, NSString)
45
 RCT_EXPORT_VIEW_PROPERTY(injectedJavaScriptBeforeContentLoaded, NSString)
45
 RCT_EXPORT_VIEW_PROPERTY(injectedJavaScriptBeforeContentLoaded, NSString)
46
+RCT_EXPORT_VIEW_PROPERTY(injectedJavaScriptForMainFrameOnly, BOOL)
47
+RCT_EXPORT_VIEW_PROPERTY(injectedJavaScriptBeforeContentLoadedForMainFrameOnly, BOOL)
46
 RCT_EXPORT_VIEW_PROPERTY(javaScriptEnabled, BOOL)
48
 RCT_EXPORT_VIEW_PROPERTY(javaScriptEnabled, BOOL)
47
 RCT_EXPORT_VIEW_PROPERTY(allowFileAccessFromFileURLs, BOOL)
49
 RCT_EXPORT_VIEW_PROPERTY(allowFileAccessFromFileURLs, BOOL)
48
 RCT_EXPORT_VIEW_PROPERTY(allowsInlineMediaPlayback, BOOL)
50
 RCT_EXPORT_VIEW_PROPERTY(allowsInlineMediaPlayback, BOOL)

+ 6
- 0
src/WebView.ios.tsx View File

290
       originWhitelist,
290
       originWhitelist,
291
       renderError,
291
       renderError,
292
       renderLoading,
292
       renderLoading,
293
+      injectedJavaScriptForMainFrameOnly = true,
294
+      injectedJavaScriptBeforeContentLoadedForMainFrameOnly = true,
293
       style,
295
       style,
294
       containerStyle,
296
       containerStyle,
295
       ...otherProps
297
       ...otherProps
344
         onScroll={this.props.onScroll}
346
         onScroll={this.props.onScroll}
345
         onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
347
         onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
346
         onContentProcessDidTerminate={this.onContentProcessDidTerminate}
348
         onContentProcessDidTerminate={this.onContentProcessDidTerminate}
349
+        injectedJavaScript={this.props.injectedJavaScript}
350
+        injectedJavaScriptBeforeContentLoaded={this.props.injectedJavaScriptBeforeContentLoaded}
351
+        injectedJavaScriptForMainFrameOnly={injectedJavaScriptForMainFrameOnly}
352
+        injectedJavaScriptBeforeContentLoadedForMainFrameOnly={injectedJavaScriptBeforeContentLoadedForMainFrameOnly}
347
         ref={this.webViewRef}
353
         ref={this.webViewRef}
348
         // TODO: find a better way to type this.
354
         // TODO: find a better way to type this.
349
         source={resolveAssetSource(this.props.source as ImageSourcePropType)}
355
         source={resolveAssetSource(this.props.source as ImageSourcePropType)}

+ 16
- 0
src/WebViewTypes.ts View File

290
   scrollEnabled?: boolean;
290
   scrollEnabled?: boolean;
291
   useSharedProcessPool?: boolean;
291
   useSharedProcessPool?: boolean;
292
   onContentProcessDidTerminate?: (event: WebViewTerminatedEvent) => void;
292
   onContentProcessDidTerminate?: (event: WebViewTerminatedEvent) => void;
293
+  injectedJavaScriptForMainFrameOnly?: boolean;
294
+  injectedJavaScriptBeforeContentLoadedForMainFrameOnly?: boolean;
293
 }
295
 }
294
 
296
 
295
 export interface MacOSNativeWebViewProps extends CommonNativeWebViewProps {
297
 export interface MacOSNativeWebViewProps extends CommonNativeWebViewProps {
482
    * @platform ios
484
    * @platform ios
483
    */
485
    */
484
   onContentProcessDidTerminate?: (event: WebViewTerminatedEvent) => void;
486
   onContentProcessDidTerminate?: (event: WebViewTerminatedEvent) => void;
487
+
488
+  /**
489
+   * If `true` (default), loads the `injectedJavaScript` only into the main frame.
490
+   * If `false`, loads it into all frames (e.g. iframes).
491
+   * @platform ios
492
+  */
493
+  injectedJavaScriptForMainFrameOnly?: boolean;
494
+
495
+  /**
496
+   * If `true` (default), loads the `injectedJavaScriptBeforeContentLoaded` only into the main frame.
497
+   * If `false`, loads it into all frames (e.g. iframes).
498
+   * @platform ios
499
+  */
500
+  injectedJavaScriptBeforeContentLoadedForMainFrameOnly?: boolean;
485
 }
501
 }
486
 
502
 
487
 export interface MacOSWebViewProps extends WebViewSharedProps {
503
 export interface MacOSWebViewProps extends WebViewSharedProps {