-
Notifications
You must be signed in to change notification settings - Fork 269
Enable wk cookie store #171
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
1580336
d54fd07
232de7e
7f2e56d
3c8c29d
0dd8f06
1c08e36
8d4382e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,6 +35,7 @@ @interface RCTWKWebView () <WKNavigationDelegate, RCTAutoInsetsProtocol, WKScrip | |
| @property (nonatomic, copy) RCTDirectEventBlock onScroll; | ||
| @property (nonatomic, copy) RCTDirectEventBlock onNavigationResponse; | ||
| @property (assign) BOOL sendCookies; | ||
| @property (assign) BOOL useWKCookieStore; | ||
| @property (nonatomic, strong) WKUserScript *atStartScript; | ||
| @property (nonatomic, strong) WKUserScript *atEndScript; | ||
|
|
||
|
|
@@ -63,18 +64,18 @@ - (instancetype)initWithProcessPool:(WKProcessPool *)processPool | |
| super.backgroundColor = [UIColor clearColor]; | ||
| _automaticallyAdjustContentInsets = YES; | ||
| _contentInset = UIEdgeInsetsZero; | ||
|
|
||
| WKWebViewConfiguration* config = [[WKWebViewConfiguration alloc] init]; | ||
| config.processPool = processPool; | ||
| WKUserContentController* userController = [[WKUserContentController alloc]init]; | ||
| [userController addScriptMessageHandler:[[WeakScriptMessageDelegate alloc] initWithDelegate:self] name:@"reactNative"]; | ||
| config.userContentController = userController; | ||
|
|
||
| _webView = [[WKWebView alloc] initWithFrame:self.bounds configuration:config]; | ||
| _webView.UIDelegate = self; | ||
| _webView.navigationDelegate = self; | ||
| _webView.scrollView.delegate = self; | ||
|
|
||
| #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */ | ||
| // `contentInsetAdjustmentBehavior` is only available since iOS 11. | ||
| // We set the default behavior to "never" so that iOS | ||
|
|
@@ -163,7 +164,7 @@ - (void)loadRequest:(NSURLRequest *)request | |
| request = mutableRequest; | ||
| } | ||
| } | ||
|
|
||
| [_webView loadRequest:request]; | ||
| } | ||
|
|
||
|
|
@@ -179,29 +180,29 @@ -(void)setHideKeyboardAccessoryView:(BOOL)hideKeyboardAccessoryView | |
| if (!hideKeyboardAccessoryView) { | ||
| return; | ||
| } | ||
|
|
||
| UIView* subview; | ||
| for (UIView* view in _webView.scrollView.subviews) { | ||
| if([[view.class description] hasPrefix:@"WKContent"]) | ||
| subview = view; | ||
| } | ||
|
|
||
| if(subview == nil) return; | ||
|
|
||
| NSString* name = [NSString stringWithFormat:@"%@_SwizzleHelperWK", subview.class.superclass]; | ||
| Class newClass = NSClassFromString(name); | ||
|
|
||
| if(newClass == nil) | ||
| { | ||
| newClass = objc_allocateClassPair(subview.class, [name cStringUsingEncoding:NSASCIIStringEncoding], 0); | ||
| if(!newClass) return; | ||
|
|
||
| Method method = class_getInstanceMethod([_SwizzleHelperWK class], @selector(inputAccessoryView)); | ||
| class_addMethod(newClass, @selector(inputAccessoryView), method_getImplementation(method), method_getTypeEncoding(method)); | ||
|
|
||
| objc_registerClassPair(newClass); | ||
| } | ||
|
|
||
| object_setClass(subview, newClass); | ||
| } | ||
|
|
||
|
|
@@ -212,7 +213,7 @@ -(void)setKeyboardDisplayRequiresUserAction:(BOOL)keyboardDisplayRequiresUserAct | |
| if (!keyboardDisplayRequiresUserAction) { | ||
| Class class = NSClassFromString(@"WKContentView"); | ||
| NSOperatingSystemVersion iOS_11_3_0 = (NSOperatingSystemVersion){11, 3, 0}; | ||
|
|
||
| if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion: iOS_11_3_0]) { | ||
| SEL selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:changingActivityState:userObject:"); | ||
| Method method = class_getInstanceMethod(class, selector); | ||
|
|
@@ -306,28 +307,88 @@ - (void)stopLoading | |
| [_webView stopLoading]; | ||
| } | ||
|
|
||
| - (NSString *) cookieDescription:(NSHTTPCookie *)cookie { | ||
|
|
||
| NSMutableString *cDesc = [[NSMutableString alloc] init]; | ||
| [cDesc appendFormat:@"%@=%@;", | ||
| [[cookie name] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding], | ||
| [[cookie value] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; | ||
| if ([cookie.domain length] > 0) | ||
| [cDesc appendFormat:@"domain=%@;", [cookie domain]]; | ||
| if ([cookie.path length] > 0) | ||
| [cDesc appendFormat:@"path=%@;", [cookie path]]; | ||
| if (cookie.expiresDate != nil) | ||
| [cDesc appendFormat:@"expiresDate=%@;", [cookie expiresDate]]; | ||
| if (cookie.HTTPOnly == YES) | ||
| [cDesc appendString:@"HttpOnly;"]; | ||
| if (cookie.secure == YES) | ||
| [cDesc appendString:@"Secure;"]; | ||
|
|
||
|
|
||
| return cDesc; | ||
| } | ||
|
|
||
| - (void) copyCookies { | ||
|
|
||
| NSHTTPCookieStorage* storage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; | ||
| NSArray* array = [storage cookies]; | ||
|
|
||
|
|
||
| if (@available(ios 11,*)) { | ||
|
|
||
| // The webView websiteDataStore only gets initialized, when needed. Setting cookies on the dataStore's | ||
| // httpCookieStore doesn't seem to initialize it. That's why fetchDataRecordsOfTypes is called. | ||
| // All the cookies of the sharedHttpCookieStorage, which is used in react-native-cookie, | ||
| // are copied to the webSiteDataStore's httpCookieStore. | ||
| // https://bugs.webkit.org/show_bug.cgi?id=185483 | ||
| [_webView.configuration.websiteDataStore fetchDataRecordsOfTypes:[NSSet<NSString *> setWithObject:WKWebsiteDataTypeCookies] completionHandler:^(NSArray<WKWebsiteDataRecord *> *records) { | ||
| for (NSHTTPCookie* cookie in array) { | ||
| [_webView.configuration.websiteDataStore.httpCookieStore setCookie:cookie completionHandler:nil]; | ||
| } | ||
| }]; | ||
| } else { | ||
| // Create WKUserScript for each cookie | ||
| // Cookies are injected with Javascript AtDocumentStart | ||
| for (NSHTTPCookie* cookie in array){ | ||
| NSString* cookieSource = [NSString stringWithFormat:@"document.cookie = '%@'", [self cookieDescription:cookie]]; | ||
| WKUserScript* cookieScript = [[WKUserScript alloc] | ||
| initWithSource:cookieSource | ||
| injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO]; | ||
|
|
||
|
|
||
| [_webView.configuration.userContentController addUserScript:cookieScript]; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| - (void)setSource:(NSDictionary *)source | ||
| { | ||
| if (![_source isEqualToDictionary:source]) { | ||
| _source = [source copy]; | ||
| _sendCookies = [source[@"sendCookies"] boolValue]; | ||
| _useWKCookieStore = [source[@"useWKCookieStore"] boolValue]; | ||
|
|
||
| if (_useWKCookieStore) { | ||
| [self copyCookies]; | ||
| } | ||
|
|
||
| if ([source[@"customUserAgent"] length] != 0 && [_webView respondsToSelector:@selector(setCustomUserAgent:)]) { | ||
| [_webView setCustomUserAgent:source[@"customUserAgent"]]; | ||
| } | ||
|
|
||
| // Allow loading local files: | ||
| // <WKWebView source={{ file: RNFS.MainBundlePath + '/data/index.html', allowingReadAccessToURL: RNFS.MainBundlePath }} /> | ||
| // Only works for iOS 9+. So iOS 8 will simply ignore those two values | ||
| NSString *file = [RCTConvert NSString:source[@"file"]]; | ||
| NSString *allowingReadAccessToURL = [RCTConvert NSString:source[@"allowingReadAccessToURL"]]; | ||
|
|
||
| if (file && [_webView respondsToSelector:@selector(loadFileURL:allowingReadAccessToURL:)]) { | ||
| NSURL *fileURL = [RCTConvert NSURL:file]; | ||
| NSURL *baseURL = [RCTConvert NSURL:allowingReadAccessToURL]; | ||
| [_webView loadFileURL:fileURL allowingReadAccessToURL:baseURL]; | ||
| return; | ||
| } | ||
|
|
||
| // Check for a static html source first | ||
| NSString *html = [RCTConvert NSString:source[@"html"]]; | ||
| if (html) { | ||
|
|
@@ -338,7 +399,7 @@ - (void)setSource:(NSDictionary *)source | |
| [_webView loadHTMLString:html baseURL:baseURL]; | ||
| return; | ||
| } | ||
|
|
||
| NSURLRequest *request = [RCTConvert NSURLRequest:source]; | ||
| // Because of the way React works, as pages redirect, we actually end up | ||
| // passing the redirect urls back here, so we ignore them if trying to load | ||
|
|
@@ -391,7 +452,7 @@ - (UIColor *)backgroundColor | |
| @"canGoBack": @(_webView.canGoBack), | ||
| @"canGoForward" : @(_webView.canGoForward), | ||
| }]; | ||
|
|
||
| return event; | ||
| } | ||
|
|
||
|
|
@@ -455,6 +516,30 @@ - (void)scrollViewDidScroll:(UIScrollView *)scrollView | |
|
|
||
| #pragma mark - WKNavigationDelegate methods | ||
|
|
||
| - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there are duplicate methods with this same name, can you please fix this? This is breaking my build with compilation issue |
||
| if (_sendCookies) { | ||
| NSHTTPURLResponse *response = (NSHTTPURLResponse *)navigationResponse.response; | ||
| NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:response.URL]; | ||
| for (NSHTTPCookie *cookie in cookies) { | ||
| [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie]; | ||
| } | ||
| } | ||
|
|
||
| if (_onNavigationResponse) { | ||
| NSDictionary *headers = ((NSHTTPURLResponse *)navigationResponse.response).allHeaderFields; | ||
| NSInteger statusCode = ((NSHTTPURLResponse *)navigationResponse.response).statusCode; | ||
| NSMutableDictionary<NSString *, id> *event = [self baseEvent]; | ||
| [event addEntriesFromDictionary:@{ | ||
| @"headers": headers, | ||
| @"status": [NSHTTPURLResponse localizedStringForStatusCode:statusCode], | ||
| @"statusCode": @(statusCode), | ||
| }]; | ||
| _onNavigationResponse(event); | ||
| } | ||
|
|
||
| decisionHandler(WKNavigationResponsePolicyAllow); | ||
| } | ||
|
|
||
| #if DEBUG | ||
| - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler { | ||
| NSURLCredential * credential = [[NSURLCredential alloc] initWithTrust:[challenge protectionSpace].serverTrust]; | ||
|
|
@@ -468,9 +553,9 @@ - (void)webView:(__unused WKWebView *)webView decidePolicyForNavigationAction:(W | |
| NSURLRequest *request = navigationAction.request; | ||
| NSURL* url = request.URL; | ||
| NSString* scheme = url.scheme; | ||
|
|
||
| BOOL isJSNavigation = [scheme isEqualToString:RCTJSNavigationScheme]; | ||
|
|
||
| // handle mailto and tel schemes | ||
| if ([scheme isEqualToString:@"mailto"] || [scheme isEqualToString:@"tel"]) { | ||
| if ([app canOpenURL:url]) { | ||
|
|
@@ -479,7 +564,7 @@ - (void)webView:(__unused WKWebView *)webView decidePolicyForNavigationAction:(W | |
| return; | ||
| } | ||
| } | ||
|
|
||
| // skip this for the JS Navigation handler | ||
| if (!isJSNavigation && _onShouldStartLoadWithRequest) { | ||
| NSMutableDictionary<NSString *, id> *event = [self baseEvent]; | ||
|
|
@@ -493,7 +578,7 @@ - (void)webView:(__unused WKWebView *)webView decidePolicyForNavigationAction:(W | |
| return decisionHandler(WKNavigationActionPolicyCancel); | ||
| } | ||
| } | ||
|
|
||
| if (_onLoadingStart) { | ||
| // We have this check to filter out iframe requests and whatnot | ||
| BOOL isTopFrame = [url isEqual:request.mainDocumentURL]; | ||
|
|
@@ -506,7 +591,7 @@ - (void)webView:(__unused WKWebView *)webView decidePolicyForNavigationAction:(W | |
| _onLoadingStart(event); | ||
| } | ||
| } | ||
|
|
||
| if (isJSNavigation) { | ||
| decisionHandler(WKNavigationActionPolicyCancel); | ||
| } | ||
|
|
@@ -525,7 +610,7 @@ - (void)webView:(__unused WKWebView *)webView didFailProvisionalNavigation:(__un | |
| // http://stackoverflow.com/questions/1024748/how-do-i-fix-nsurlerrordomain-error-999-in-iphone-3-0-os | ||
| return; | ||
| } | ||
|
|
||
| NSMutableDictionary<NSString *, id> *event = [self baseEvent]; | ||
| [event addEntriesFromDictionary:@{ | ||
| @"domain": error.domain, | ||
|
|
@@ -548,7 +633,7 @@ - (void)webView:(WKWebView *)webView didFinishNavigation:(__unused WKNavigation | |
|
|
||
| - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler { | ||
| UIAlertController *alertController = [UIAlertController alertControllerWithTitle:message message:nil preferredStyle:UIAlertControllerStyleAlert]; | ||
|
|
||
| [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Close", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { | ||
| completionHandler(); | ||
| }]]; | ||
|
|
@@ -557,7 +642,7 @@ - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSStrin | |
| } | ||
|
|
||
| - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler { | ||
|
|
||
| // TODO We have to think message to confirm "YES" | ||
| UIAlertController *alertController = [UIAlertController alertControllerWithTitle:message message:nil preferredStyle:UIAlertControllerStyleAlert]; | ||
| [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { | ||
|
|
@@ -571,17 +656,17 @@ - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSStr | |
| } | ||
|
|
||
| - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *))completionHandler { | ||
|
|
||
| UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:nil preferredStyle:UIAlertControllerStyleAlert]; | ||
| [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) { | ||
| textField.text = defaultText; | ||
| }]; | ||
|
|
||
| [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { | ||
| NSString *input = ((UITextField *)alertController.textFields.firstObject).text; | ||
| completionHandler(input); | ||
| }]]; | ||
|
|
||
| [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { | ||
| completionHandler(nil); | ||
| }]]; | ||
|
|
@@ -609,20 +694,5 @@ - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView | |
| RCTLogWarn(@"Webview Process Terminated"); | ||
| } | ||
|
|
||
| - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the quick response. Appreciate it |
||
| if (_onNavigationResponse) { | ||
| NSDictionary *headers = ((NSHTTPURLResponse *)navigationResponse.response).allHeaderFields; | ||
| NSInteger statusCode = ((NSHTTPURLResponse *)navigationResponse.response).statusCode; | ||
| NSMutableDictionary<NSString *, id> *event = [self baseEvent]; | ||
| [event addEntriesFromDictionary:@{ | ||
| @"headers": headers, | ||
| @"status": [NSHTTPURLResponse localizedStringForStatusCode:statusCode], | ||
| @"statusCode": @(statusCode), | ||
| }]; | ||
| _onNavigationResponse(event); | ||
| } | ||
|
|
||
| decisionHandler(WKNavigationResponsePolicyAllow); | ||
| } | ||
|
|
||
| @end | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its missing HttpOnly, secure and sessionOnly property. Is this something you would be able to add?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for the HttpOnly: I can do that,
for the sessionOnly there ist no such thing, If there is no expire date than it should be a session cookie, otherwise it will be stored until the expire date
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://developer.apple.com/documentation/foundation/nshttpcookie/1392991-sessiononly?language=objc
I was referencing that boolean to indicate its a session only cookie
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And Thanks for fixing this issue!