Jamie Birch 4 years ago
parent
commit
16ca1a0f01
No account linked to committer's email address
9 changed files with 316 additions and 121 deletions
  1. 27
    1
      docs/Reference.md
  2. 14
    0
      example/App.tsx
  3. 68
    0
      example/examples/Injection.tsx
  4. 2
    2
      example/ios/Podfile.lock
  5. 2
    0
      ios/RNCWebView.h
  6. 179
    118
      ios/RNCWebView.m
  7. 2
    0
      ios/RNCWebViewManager.m
  8. 6
    0
      src/WebView.ios.tsx
  9. 16
    0
      src/WebViewTypes.ts

+ 27
- 1
docs/Reference.md View File

@@ -7,7 +7,9 @@ This document lays out the current public properties and methods for the React N
7 7
 - [`source`](Reference.md#source)
8 8
 - [`automaticallyAdjustContentInsets`](Reference.md#automaticallyadjustcontentinsets)
9 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 13
 - [`mediaPlaybackRequiresUserAction`](Reference.md#mediaplaybackrequiresuseraction)
12 14
 - [`nativeConfig`](Reference.md#nativeconfig)
13 15
 - [`onError`](Reference.md#onerror)
@@ -174,6 +176,30 @@ const INJECTED_JAVASCRIPT = `(function() {
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 203
 ### `mediaPlaybackRequiresUserAction`
178 204
 
179 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,6 +14,7 @@ import Alerts from './examples/Alerts';
14 14
 import Scrolling from './examples/Scrolling';
15 15
 import Background from './examples/Background';
16 16
 import Uploads from './examples/Uploads';
17
+import Injection from './examples/Injection';
17 18
 
18 19
 const TESTS = {
19 20
   Alerts: {
@@ -48,6 +49,14 @@ const TESTS = {
48 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 62
 type Props = {};
@@ -101,6 +110,11 @@ export default class App extends Component<Props, State> {
101 110
             title="Background"
102 111
             onPress={() => this._changeTest('Background')}
103 112
           />
113
+          <Button
114
+            testID="testType_injection"
115
+            title="Injection"
116
+            onPress={() => this._changeTest('Injection')}
117
+          />
104 118
           {Platform.OS === 'android' && <Button
105 119
             testID="testType_uploads"
106 120
             title="Uploads"

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

@@ -0,0 +1,68 @@
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,7 +182,7 @@ PODS:
182 182
     - React-cxxreact (= 0.61.5)
183 183
     - React-jsi (= 0.61.5)
184 184
   - React-jsinspector (0.61.5)
185
-  - react-native-webview (8.0.6):
185
+  - react-native-webview (8.1.1):
186 186
     - React
187 187
   - React-RCTActionSheet (0.61.5):
188 188
     - React-Core/RCTActionSheetHeaders (= 0.61.5)
@@ -326,7 +326,7 @@ SPEC CHECKSUMS:
326 326
   React-jsi: cb2cd74d7ccf4cffb071a46833613edc79cdf8f7
327 327
   React-jsiexecutor: d5525f9ed5f782fdbacb64b9b01a43a9323d2386
328 328
   React-jsinspector: fa0ecc501688c3c4c34f28834a76302233e29dc0
329
-  react-native-webview: 222d83c9c489e09b5d3541519110a637490ad4fa
329
+  react-native-webview: 0ec2ca34f52e60892e4a9141508751588d45a518
330 330
   React-RCTActionSheet: 600b4d10e3aea0913b5a92256d2719c0cdd26d76
331 331
   React-RCTAnimation: 791a87558389c80908ed06cc5dfc5e7920dfa360
332 332
   React-RCTBlob: d89293cc0236d9cb0933d85e430b0bbe81ad1d72

+ 2
- 0
ios/RNCWebView.h View File

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

+ 179
- 118
ios/RNCWebView.m View File

@@ -81,6 +81,9 @@ static NSDictionary* customCertificatesForHost;
81 81
 #else
82 82
 @property (nonatomic, copy) RNCWKWebView *webView;
83 83
 #endif // !TARGET_OS_OSX
84
+@property (nonatomic, strong) WKUserScript *postMessageScript;
85
+@property (nonatomic, strong) WKUserScript *atStartScript;
86
+@property (nonatomic, strong) WKUserScript *atEndScript;
84 87
 @end
85 88
 
86 89
 @implementation RNCWebView
@@ -122,10 +125,14 @@ static NSDictionary* customCertificatesForHost;
122 125
     _automaticallyAdjustContentInsets = YES;
123 126
     _contentInset = UIEdgeInsetsZero;
124 127
     _savedKeyboardDisplayRequiresUserAction = YES;
125
-#if !TARGET_OS_OSX
128
+    #if !TARGET_OS_OSX
126 129
     _savedStatusBarStyle = RCTSharedApplication().statusBarStyle;
127 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 137
 #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
131 138
     _savedContentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
@@ -206,50 +213,7 @@ static NSDictionary* customCertificatesForHost;
206 213
   // Shim the HTML5 history API:
207 214
   [wkWebViewConfig.userContentController addScriptMessageHandler:[[RNCWeakScriptMessageDelegate alloc] initWithDelegate:self]
208 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 218
 #if !TARGET_OS_OSX
255 219
   wkWebViewConfig.allowsInlineMediaPlayback = _allowsInlineMediaPlayback;
@@ -266,68 +230,6 @@ static NSDictionary* customCertificatesForHost;
266 230
   if (_applicationNameForUserAgent) {
267 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 234
   return wkWebViewConfig;
333 235
 }
@@ -1136,16 +1038,7 @@ static NSDictionary* customCertificatesForHost;
1136 1038
 - (void)webView:(WKWebView *)webView
1137 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 1042
     _onLoadingFinish([self baseEvent]);
1150 1043
   }
1151 1044
 }
@@ -1194,6 +1087,174 @@ static NSDictionary* customCertificatesForHost;
1194 1087
 }
1195 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 1258
 - (NSURLRequest *)requestForSource:(id)json {
1198 1259
   NSURLRequest *request = [RCTConvert NSURLRequest:self.source];
1199 1260
 

+ 2
- 0
ios/RNCWebViewManager.m View File

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

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

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

+ 16
- 0
src/WebViewTypes.ts View File

@@ -290,6 +290,8 @@ export interface IOSNativeWebViewProps extends CommonNativeWebViewProps {
290 290
   scrollEnabled?: boolean;
291 291
   useSharedProcessPool?: boolean;
292 292
   onContentProcessDidTerminate?: (event: WebViewTerminatedEvent) => void;
293
+  injectedJavaScriptForMainFrameOnly?: boolean;
294
+  injectedJavaScriptBeforeContentLoadedForMainFrameOnly?: boolean;
293 295
 }
294 296
 
295 297
 export interface MacOSNativeWebViewProps extends CommonNativeWebViewProps {
@@ -482,6 +484,20 @@ export interface IOSWebViewProps extends WebViewSharedProps {
482 484
    * @platform ios
483 485
    */
484 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 503
 export interface MacOSWebViewProps extends WebViewSharedProps {