|
@@ -51,6 +51,9 @@ static NSDictionary* customCertificatesForHost;
|
51
|
51
|
@property (nonatomic, copy) RCTDirectEventBlock onScroll;
|
52
|
52
|
@property (nonatomic, copy) RCTDirectEventBlock onContentProcessDidTerminate;
|
53
|
53
|
@property (nonatomic, copy) WKWebView *webView;
|
|
54
|
+@property (nonatomic, strong) WKUserScript *postMessageScript;
|
|
55
|
+@property (nonatomic, strong) WKUserScript *atStartScript;
|
|
56
|
+@property (nonatomic, strong) WKUserScript *atEndScript;
|
54
|
57
|
@end
|
55
|
58
|
|
56
|
59
|
@implementation RNCWebView
|
|
@@ -84,6 +87,10 @@ static NSDictionary* customCertificatesForHost;
|
84
|
87
|
_savedKeyboardDisplayRequiresUserAction = YES;
|
85
|
88
|
_savedStatusBarStyle = RCTSharedApplication().statusBarStyle;
|
86
|
89
|
_savedStatusBarHidden = RCTSharedApplication().statusBarHidden;
|
|
90
|
+ _injectedJavaScript = nil;
|
|
91
|
+ _injectedJavaScriptForMainFrameOnly = YES;
|
|
92
|
+ _injectedJavaScriptBeforeContentLoaded = nil;
|
|
93
|
+ _injectedJavaScriptBeforeContentLoadedForMainFrameOnly = YES;
|
87
|
94
|
|
88
|
95
|
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
|
89
|
96
|
_savedContentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
|
@@ -163,50 +170,8 @@ static NSDictionary* customCertificatesForHost;
|
163
|
170
|
// Shim the HTML5 history API:
|
164
|
171
|
[wkWebViewConfig.userContentController addScriptMessageHandler:[[RNCWeakScriptMessageDelegate alloc] initWithDelegate:self]
|
165
|
172
|
name:HistoryShimName];
|
166
|
|
- NSString *source = [NSString stringWithFormat:
|
167
|
|
- @"(function(history) {\n"
|
168
|
|
- " function notify(type) {\n"
|
169
|
|
- " setTimeout(function() {\n"
|
170
|
|
- " window.webkit.messageHandlers.%@.postMessage(type)\n"
|
171
|
|
- " }, 0)\n"
|
172
|
|
- " }\n"
|
173
|
|
- " function shim(f) {\n"
|
174
|
|
- " return function pushState() {\n"
|
175
|
|
- " notify('other')\n"
|
176
|
|
- " return f.apply(history, arguments)\n"
|
177
|
|
- " }\n"
|
178
|
|
- " }\n"
|
179
|
|
- " history.pushState = shim(history.pushState)\n"
|
180
|
|
- " history.replaceState = shim(history.replaceState)\n"
|
181
|
|
- " window.addEventListener('popstate', function() {\n"
|
182
|
|
- " notify('backforward')\n"
|
183
|
|
- " })\n"
|
184
|
|
- "})(window.history)\n", HistoryShimName
|
185
|
|
- ];
|
186
|
|
- WKUserScript *script = [[WKUserScript alloc] initWithSource:source injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
|
187
|
|
- [wkWebViewConfig.userContentController addUserScript:script];
|
188
|
|
-
|
189
|
|
- if (_messagingEnabled) {
|
190
|
|
- [wkWebViewConfig.userContentController addScriptMessageHandler:[[RNCWeakScriptMessageDelegate alloc] initWithDelegate:self]
|
191
|
|
- name:MessageHandlerName];
|
192
|
|
-
|
193
|
|
- NSString *source = [NSString stringWithFormat:
|
194
|
|
- @"window.%@ = {"
|
195
|
|
- " postMessage: function (data) {"
|
196
|
|
- " window.webkit.messageHandlers.%@.postMessage(String(data));"
|
197
|
|
- " }"
|
198
|
|
- "};", MessageHandlerName, MessageHandlerName
|
199
|
|
- ];
|
200
|
|
-
|
201
|
|
- WKUserScript *script = [[WKUserScript alloc] initWithSource:source injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
|
202
|
|
- [wkWebViewConfig.userContentController addUserScript:script];
|
203
|
|
-
|
204
|
|
- if (_injectedJavaScriptBeforeContentLoaded) {
|
205
|
|
- // If user has provided an injectedJavascript prop, execute it at the start of the document
|
206
|
|
- WKUserScript *injectedScript = [[WKUserScript alloc] initWithSource:_injectedJavaScriptBeforeContentLoaded injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
|
207
|
|
- [wkWebViewConfig.userContentController addUserScript:injectedScript];
|
208
|
|
- }
|
209
|
|
- }
|
|
173
|
+
|
|
174
|
+ [self resetupScripts];
|
210
|
175
|
|
211
|
176
|
wkWebViewConfig.allowsInlineMediaPlayback = _allowsInlineMediaPlayback;
|
212
|
177
|
#if WEBKIT_IOS_10_APIS_AVAILABLE
|
|
@@ -221,68 +186,6 @@ static NSDictionary* customCertificatesForHost;
|
221
|
186
|
if (_applicationNameForUserAgent) {
|
222
|
187
|
wkWebViewConfig.applicationNameForUserAgent = [NSString stringWithFormat:@"%@ %@", wkWebViewConfig.applicationNameForUserAgent, _applicationNameForUserAgent];
|
223
|
188
|
}
|
224
|
|
-
|
225
|
|
- if(_sharedCookiesEnabled) {
|
226
|
|
- // More info to sending cookies with WKWebView
|
227
|
|
- // https://stackoverflow.com/questions/26573137/can-i-set-the-cookies-to-be-used-by-a-wkwebview/26577303#26577303
|
228
|
|
- if (@available(iOS 11.0, *)) {
|
229
|
|
- // Set Cookies in iOS 11 and above, initialize websiteDataStore before setting cookies
|
230
|
|
- // See also https://forums.developer.apple.com/thread/97194
|
231
|
|
- // check if websiteDataStore has not been initialized before
|
232
|
|
- if(!_incognito && !_cacheEnabled) {
|
233
|
|
- wkWebViewConfig.websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
|
234
|
|
- }
|
235
|
|
- for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
|
236
|
|
- [wkWebViewConfig.websiteDataStore.httpCookieStore setCookie:cookie completionHandler:nil];
|
237
|
|
- }
|
238
|
|
- } else {
|
239
|
|
- NSMutableString *script = [NSMutableString string];
|
240
|
|
-
|
241
|
|
- // Clear all existing cookies in a direct called function. This ensures that no
|
242
|
|
- // javascript error will break the web content javascript.
|
243
|
|
- // We keep this code here, if someone requires that Cookies are also removed within the
|
244
|
|
- // the WebView and want to extends the current sharedCookiesEnabled option with an
|
245
|
|
- // additional property.
|
246
|
|
- // Generates JS: document.cookie = "key=; Expires=Thu, 01 Jan 1970 00:00:01 GMT;"
|
247
|
|
- // for each cookie which is already available in the WebView context.
|
248
|
|
- /*
|
249
|
|
- [script appendString:@"(function () {\n"];
|
250
|
|
- [script appendString:@" var cookies = document.cookie.split('; ');\n"];
|
251
|
|
- [script appendString:@" for (var i = 0; i < cookies.length; i++) {\n"];
|
252
|
|
- [script appendString:@" if (cookies[i].indexOf('=') !== -1) {\n"];
|
253
|
|
- [script appendString:@" document.cookie = cookies[i].split('=')[0] + '=; Expires=Thu, 01 Jan 1970 00:00:01 GMT';\n"];
|
254
|
|
- [script appendString:@" }\n"];
|
255
|
|
- [script appendString:@" }\n"];
|
256
|
|
- [script appendString:@"})();\n\n"];
|
257
|
|
- */
|
258
|
|
-
|
259
|
|
- // Set cookies in a direct called function. This ensures that no
|
260
|
|
- // javascript error will break the web content javascript.
|
261
|
|
- // Generates JS: document.cookie = "key=value; Path=/; Expires=Thu, 01 Jan 20xx 00:00:01 GMT;"
|
262
|
|
- // for each cookie which is available in the application context.
|
263
|
|
- [script appendString:@"(function () {\n"];
|
264
|
|
- for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
|
265
|
|
- [script appendFormat:@"document.cookie = %@ + '=' + %@",
|
266
|
|
- RCTJSONStringify(cookie.name, NULL),
|
267
|
|
- RCTJSONStringify(cookie.value, NULL)];
|
268
|
|
- if (cookie.path) {
|
269
|
|
- [script appendFormat:@" + '; Path=' + %@", RCTJSONStringify(cookie.path, NULL)];
|
270
|
|
- }
|
271
|
|
- if (cookie.expiresDate) {
|
272
|
|
- [script appendFormat:@" + '; Expires=' + new Date(%f).toUTCString()",
|
273
|
|
- cookie.expiresDate.timeIntervalSince1970 * 1000
|
274
|
|
- ];
|
275
|
|
- }
|
276
|
|
- [script appendString:@";\n"];
|
277
|
|
- }
|
278
|
|
- [script appendString:@"})();\n"];
|
279
|
|
-
|
280
|
|
- WKUserScript* cookieInScript = [[WKUserScript alloc] initWithSource:script
|
281
|
|
- injectionTime:WKUserScriptInjectionTimeAtDocumentStart
|
282
|
|
- forMainFrameOnly:YES];
|
283
|
|
- [wkWebViewConfig.userContentController addUserScript:cookieInScript];
|
284
|
|
- }
|
285
|
|
- }
|
286
|
189
|
|
287
|
190
|
return wkWebViewConfig;
|
288
|
191
|
}
|
|
@@ -1064,6 +967,166 @@ static NSDictionary* customCertificatesForHost;
|
1064
|
967
|
_webView.scrollView.bounces = bounces;
|
1065
|
968
|
}
|
1066
|
969
|
|
|
970
|
+
|
|
971
|
+- (void)setInjectedJavaScript:(NSString *)script {
|
|
972
|
+ _injectedJavaScript = script;
|
|
973
|
+
|
|
974
|
+ self.atStartScript = script == nil ? nil : [[WKUserScript alloc] initWithSource:script
|
|
975
|
+ injectionTime:WKUserScriptInjectionTimeAtDocumentStart
|
|
976
|
+ forMainFrameOnly:_injectedJavaScriptForMainFrameOnly];
|
|
977
|
+
|
|
978
|
+ [self resetupScripts];
|
|
979
|
+}
|
|
980
|
+
|
|
981
|
+- (void)setInjectedJavaScriptBeforeContentLoaded:(NSString *)script {
|
|
982
|
+ _injectedJavaScriptBeforeContentLoaded = script;
|
|
983
|
+
|
|
984
|
+ self.atEndScript = script == nil ? nil : [[WKUserScript alloc] initWithSource:script
|
|
985
|
+ injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
|
|
986
|
+ forMainFrameOnly:_injectedJavaScriptBeforeContentLoadedForMainFrameOnly];
|
|
987
|
+
|
|
988
|
+ [self resetupScripts];
|
|
989
|
+}
|
|
990
|
+
|
|
991
|
+- (void)setInjectedJavaScriptForMainFrameOnly:(BOOL)inject {
|
|
992
|
+ _injectedJavaScriptForMainFrameOnly = inject;
|
|
993
|
+ [self setInjectedJavaScript:_injectedJavaScript];
|
|
994
|
+}
|
|
995
|
+
|
|
996
|
+- (void)setInjectedJavaScriptBeforeContentLoadedForMainFrameOnly:(BOOL)inject {
|
|
997
|
+ _injectedJavaScriptBeforeContentLoadedForMainFrameOnly = inject;
|
|
998
|
+ [self setInjectedJavaScriptBeforeContentLoaded:_injectedJavaScriptBeforeContentLoaded];
|
|
999
|
+}
|
|
1000
|
+
|
|
1001
|
+- (void)setMessagingEnabled:(BOOL)messagingEnabled {
|
|
1002
|
+ _messagingEnabled = messagingEnabled;
|
|
1003
|
+
|
|
1004
|
+ self.postMessageScript = _messagingEnabled ?
|
|
1005
|
+ [
|
|
1006
|
+ [WKUserScript alloc]
|
|
1007
|
+ initWithSource: [
|
|
1008
|
+ NSString
|
|
1009
|
+ stringWithFormat:
|
|
1010
|
+ @"window.%@ = {"
|
|
1011
|
+ " postMessage: function (data) {"
|
|
1012
|
+ " window.webkit.messageHandlers.%@.postMessage(String(data));"
|
|
1013
|
+ " }"
|
|
1014
|
+ "};", MessageHandlerName, MessageHandlerName
|
|
1015
|
+ ]
|
|
1016
|
+ injectionTime:WKUserScriptInjectionTimeAtDocumentStart
|
|
1017
|
+ /* This was previously YES, but I'm following react-native-wkwebview's approach on this;
|
|
1018
|
+ * It's very useful to be able to inject postMessage into iframes. */
|
|
1019
|
+ forMainFrameOnly:_injectedJavaScriptBeforeContentLoadedForMainFrameOnly
|
|
1020
|
+ ] :
|
|
1021
|
+ nil;
|
|
1022
|
+
|
|
1023
|
+ [self resetupScripts];
|
|
1024
|
+}
|
|
1025
|
+
|
|
1026
|
+- (void)resetupScripts {
|
|
1027
|
+ [_webView.configuration.userContentController removeAllUserScripts];
|
|
1028
|
+ [_webView.configuration.userContentController removeScriptMessageHandlerForName:MessageHandlerName];
|
|
1029
|
+
|
|
1030
|
+ NSString *html5HistoryAPIShimSource = [NSString stringWithFormat:
|
|
1031
|
+ @"(function(history) {\n"
|
|
1032
|
+ " function notify(type) {\n"
|
|
1033
|
+ " setTimeout(function() {\n"
|
|
1034
|
+ " window.webkit.messageHandlers.%@.postMessage(type)\n"
|
|
1035
|
+ " }, 0)\n"
|
|
1036
|
+ " }\n"
|
|
1037
|
+ " function shim(f) {\n"
|
|
1038
|
+ " return function pushState() {\n"
|
|
1039
|
+ " notify('other')\n"
|
|
1040
|
+ " return f.apply(history, arguments)\n"
|
|
1041
|
+ " }\n"
|
|
1042
|
+ " }\n"
|
|
1043
|
+ " history.pushState = shim(history.pushState)\n"
|
|
1044
|
+ " history.replaceState = shim(history.replaceState)\n"
|
|
1045
|
+ " window.addEventListener('popstate', function() {\n"
|
|
1046
|
+ " notify('backforward')\n"
|
|
1047
|
+ " })\n"
|
|
1048
|
+ "})(window.history)\n", HistoryShimName
|
|
1049
|
+ ];
|
|
1050
|
+ WKUserScript *script = [[WKUserScript alloc] initWithSource:html5HistoryAPIShimSource injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
|
|
1051
|
+ [_webView.configuration.userContentController addUserScript:script];
|
|
1052
|
+
|
|
1053
|
+ if(_sharedCookiesEnabled) {
|
|
1054
|
+ // More info to sending cookies with WKWebView
|
|
1055
|
+ // https://stackoverflow.com/questions/26573137/can-i-set-the-cookies-to-be-used-by-a-wkwebview/26577303#26577303
|
|
1056
|
+ if (@available(iOS 11.0, *)) {
|
|
1057
|
+ // Set Cookies in iOS 11 and above, initialize websiteDataStore before setting cookies
|
|
1058
|
+ // See also https://forums.developer.apple.com/thread/97194
|
|
1059
|
+ // check if websiteDataStore has not been initialized before
|
|
1060
|
+ if(!_incognito && !_cacheEnabled) {
|
|
1061
|
+ _webView.configuration.websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
|
|
1062
|
+ }
|
|
1063
|
+ for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
|
|
1064
|
+ [_webView.configuration.websiteDataStore.httpCookieStore setCookie:cookie completionHandler:nil];
|
|
1065
|
+ }
|
|
1066
|
+ } else {
|
|
1067
|
+ NSMutableString *script = [NSMutableString string];
|
|
1068
|
+
|
|
1069
|
+ // Clear all existing cookies in a direct called function. This ensures that no
|
|
1070
|
+ // javascript error will break the web content javascript.
|
|
1071
|
+ // We keep this code here, if someone requires that Cookies are also removed within the
|
|
1072
|
+ // the WebView and want to extends the current sharedCookiesEnabled option with an
|
|
1073
|
+ // additional property.
|
|
1074
|
+ // Generates JS: document.cookie = "key=; Expires=Thu, 01 Jan 1970 00:00:01 GMT;"
|
|
1075
|
+ // for each cookie which is already available in the WebView context.
|
|
1076
|
+ /*
|
|
1077
|
+ [script appendString:@"(function () {\n"];
|
|
1078
|
+ [script appendString:@" var cookies = document.cookie.split('; ');\n"];
|
|
1079
|
+ [script appendString:@" for (var i = 0; i < cookies.length; i++) {\n"];
|
|
1080
|
+ [script appendString:@" if (cookies[i].indexOf('=') !== -1) {\n"];
|
|
1081
|
+ [script appendString:@" document.cookie = cookies[i].split('=')[0] + '=; Expires=Thu, 01 Jan 1970 00:00:01 GMT';\n"];
|
|
1082
|
+ [script appendString:@" }\n"];
|
|
1083
|
+ [script appendString:@" }\n"];
|
|
1084
|
+ [script appendString:@"})();\n\n"];
|
|
1085
|
+ */
|
|
1086
|
+
|
|
1087
|
+ // Set cookies in a direct called function. This ensures that no
|
|
1088
|
+ // javascript error will break the web content javascript.
|
|
1089
|
+ // Generates JS: document.cookie = "key=value; Path=/; Expires=Thu, 01 Jan 20xx 00:00:01 GMT;"
|
|
1090
|
+ // for each cookie which is available in the application context.
|
|
1091
|
+ [script appendString:@"(function () {\n"];
|
|
1092
|
+ for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
|
|
1093
|
+ [script appendFormat:@"document.cookie = %@ + '=' + %@",
|
|
1094
|
+ RCTJSONStringify(cookie.name, NULL),
|
|
1095
|
+ RCTJSONStringify(cookie.value, NULL)];
|
|
1096
|
+ if (cookie.path) {
|
|
1097
|
+ [script appendFormat:@" + '; Path=' + %@", RCTJSONStringify(cookie.path, NULL)];
|
|
1098
|
+ }
|
|
1099
|
+ if (cookie.expiresDate) {
|
|
1100
|
+ [script appendFormat:@" + '; Expires=' + new Date(%f).toUTCString()",
|
|
1101
|
+ cookie.expiresDate.timeIntervalSince1970 * 1000
|
|
1102
|
+ ];
|
|
1103
|
+ }
|
|
1104
|
+ [script appendString:@";\n"];
|
|
1105
|
+ }
|
|
1106
|
+ [script appendString:@"})();\n"];
|
|
1107
|
+
|
|
1108
|
+ WKUserScript* cookieInScript = [[WKUserScript alloc] initWithSource:script
|
|
1109
|
+ injectionTime:WKUserScriptInjectionTimeAtDocumentStart
|
|
1110
|
+ forMainFrameOnly:YES];
|
|
1111
|
+ [_webView.configuration.userContentController addUserScript:cookieInScript];
|
|
1112
|
+ }
|
|
1113
|
+ }
|
|
1114
|
+
|
|
1115
|
+ if(_messagingEnabled){
|
|
1116
|
+ if (self.postMessageScript){
|
|
1117
|
+ [_webView.configuration.userContentController addScriptMessageHandler:[[RNCWeakScriptMessageDelegate alloc] initWithDelegate:self]
|
|
1118
|
+ name:MessageHandlerName];
|
|
1119
|
+ [_webView.configuration.userContentController addUserScript:self.postMessageScript];
|
|
1120
|
+ }
|
|
1121
|
+ if (self.atStartScript) {
|
|
1122
|
+ [_webView.configuration.userContentController addUserScript:self.atStartScript];
|
|
1123
|
+ }
|
|
1124
|
+ if (self.atEndScript) {
|
|
1125
|
+ [_webView.configuration.userContentController addUserScript:self.atEndScript];
|
|
1126
|
+ }
|
|
1127
|
+ }
|
|
1128
|
+}
|
|
1129
|
+
|
1067
|
1130
|
- (NSURLRequest *)requestForSource:(id)json {
|
1068
|
1131
|
NSURLRequest *request = [RCTConvert NSURLRequest:self.source];
|
1069
|
1132
|
|