|
@@ -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
|
|