diff --git a/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java b/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java index 2cbcdd3113c117cca6f53bc43de8722edf72abdd..9a52fd9c195f0d4d1ef24ef4db04a337f8c7e5ea 100644 --- a/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java +++ b/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java @@ -37,6 +37,7 @@ import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.FrameLayout; +import android.widget.Toast; import com.facebook.common.logging.FLog; import com.facebook.react.bridge.Arguments; @@ -201,39 +202,7 @@ public class RNCWebViewManager extends SimpleViewManager { webView.setDownloadListener(new DownloadListener() { public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) { - RNCWebViewModule module = getModule(reactContext); - - DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url)); - - String fileName = URLUtil.guessFileName(url, contentDisposition, mimetype); - String downloadMessage = "Downloading " + fileName; - - //Attempt to add cookie, if it exists - URL urlObj = null; - try { - urlObj = new URL(url); - String baseUrl = urlObj.getProtocol() + "://" + urlObj.getHost(); - String cookie = CookieManager.getInstance().getCookie(baseUrl); - request.addRequestHeader("Cookie", cookie); - System.out.println("Got cookie for DownloadManager: " + cookie); - } catch (MalformedURLException e) { - System.out.println("Error getting cookie for DownloadManager: " + e.toString()); - e.printStackTrace(); - } - - //Finish setting up request - request.addRequestHeader("User-Agent", userAgent); - request.setTitle(fileName); - request.setDescription(downloadMessage); - request.allowScanningByMediaScanner(); - request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); - request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName); - - module.setDownloadRequest(request); - - if (module.grantFileDownloaderPermissions()) { - module.downloadFile(); - } + Toast.makeText(reactContext, "暂不支持下载", Toast.LENGTH_SHORT).show(); } }); diff --git a/ios/CustomURLSchemeHandler.m b/ios/CustomURLSchemeHandler.m index 4cdf3385d81191d49861acf2f8eda596bc0e6157..ee5d4114ec75f9370f554e60a20d9ce4b84026b7 100644 --- a/ios/CustomURLSchemeHandler.m +++ b/ios/CustomURLSchemeHandler.m @@ -11,6 +11,7 @@ #import "AFHTTPSessionManager.h" #import "AFURLSessionManager.h" #import "WebViewUtils.h" +#import static AFHTTPSessionManager *manager ; @@ -18,6 +19,49 @@ static AFHTTPSessionManager *manager ; // 拦截特定http(s)请求,判断是否走本地缓存 - (void)webView:(WKWebView *)webView startURLSchemeTask:(id )urlSchemeTask{ + + NSArray *exceptFileArr = @[@"png", @"jpeg", @"jpg", @"mp4", @"html", @"css"]; + NSString *requestPath = [NSString stringWithFormat:@"%@", [urlSchemeTask.request.URL path]]; + NSString *resourceExtension = (requestPath && [requestPath isKindOfClass:[NSString class]]) ? requestPath.pathExtension : @""; + + if ([exceptFileArr containsObject:resourceExtension]) { + // 请求为资源文件 + [self wb_sendRequestForWebview:webView startURLSchemeTask:urlSchemeTask]; + return; + } + + __weak __typeof(self)weakSelf = self; + __weak __typeof(webView)weakWebview = webView; + __weak __typeof(urlSchemeTask)weakUrlSchemeTask = urlSchemeTask; + void (^completionBlock)(BOOL needInsert) = ^(BOOL needInsert) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + __strong __typeof(weakWebview)strongWebview = weakWebview; + __strong __typeof(weakUrlSchemeTask)strongUrlSchemeTask = weakUrlSchemeTask; + if (!needInsert) { + // 不需要重新注入 + [strongSelf wb_sendRequestForWebview:strongWebview startURLSchemeTask:strongUrlSchemeTask]; + } else { + // 需要重新注入 + [strongSelf wb_insertCookieForWebView:strongWebview startURLSchemeTask:strongUrlSchemeTask]; + } + }; + + + Class WBCookieObserverClass = NSClassFromString(@"WBCookieObserver"); + id WBCookieObserverInstance = nil; + if (WBCookieObserverClass && [WBCookieObserverClass respondsToSelector:@selector(sharedInstance)]) { + WBCookieObserverInstance = [WBCookieObserverClass performSelector:@selector(sharedInstance)]; + if (WBCookieObserverInstance && [WBCookieObserverInstance respondsToSelector:@selector(wb_needInterceptReqInsertCookie: completion:)]) { + [WBCookieObserverInstance performSelector:@selector(wb_needInterceptReqInsertCookie: completion:) withObject:webView withObject:completionBlock]; + } + } +} + +- (void)webView:(WKWebView *)webVie stopURLSchemeTask:(id)urlSchemeTask { +// NSLog(@"stop = %@",urlSchemeTask); +} + +- (void)wb_sendRequestForWebview:(WKWebView *)webView startURLSchemeTask:(id )urlSchemeTask { NSURLRequest *request = urlSchemeTask.request; // only catch [xxx.md5.fileType] @@ -70,8 +114,72 @@ static AFHTTPSessionManager *manager ; [task resume]; } -- (void)webView:(WKWebView *)webVie stopURLSchemeTask:(id)urlSchemeTask { -// NSLog(@"stop = %@",urlSchemeTask); +// 给 webview 注入 cookie +- (void)wb_insertCookieForWebView:(WKWebView *)webView startURLSchemeTask:(id )urlSchemeTask{ + for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) { + [webView.configuration.websiteDataStore.httpCookieStore setCookie:cookie completionHandler:nil]; + } + + NSMutableString *script = [NSMutableString string]; + [script appendString:@"(function () {\n"]; + for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) { + [script appendFormat:@"document.cookie = %@ + '=' + %@", + RCTJSONStringify(cookie.name, NULL), + RCTJSONStringify(cookie.value, NULL)]; + if (cookie.domain) { + [script appendFormat:@" + '; domain=' + %@", RCTJSONStringify(cookie.domain, NULL)]; + } + if (cookie.path) { + [script appendFormat:@" + '; Path=' + %@", RCTJSONStringify(cookie.path, NULL)]; + } + + if (cookie.isSecure) { + [script appendFormat:@" + '; isSecure=' + %@", @"TRUE"]; + } + + if (cookie.isHTTPOnly) { + [script appendFormat:@" + '; isHTTPOnly=' + %@", @"TRUE"]; + } + + if (cookie.expiresDate) { + [script appendFormat:@" + '; Expires=' + new Date(%f).toUTCString()", + cookie.expiresDate.timeIntervalSince1970 * 1000 + ]; + } + [script appendString:@";\n"]; + } + [script appendString:@"})();\n"]; + WKUserScript* cookieInScript = [[WKUserScript alloc] initWithSource:script + injectionTime:WKUserScriptInjectionTimeAtDocumentStart + forMainFrameOnly:YES]; + + // 替换旧的 Cookies + NSArray *userScripts = [NSArray arrayWithArray:webView.configuration.userContentController.userScripts]; + NSLog(@"insertCookies ======= 注入前 userScripts 个数 = %ld, userScripts = %@", [userScripts count], userScripts); + [webView.configuration.userContentController removeAllUserScripts]; + for (WKUserScript *wkScript in userScripts) { + NSString *wkSource = wkScript.source; + if ([wkSource containsString:@"document.cookie"]) { + [webView.configuration.userContentController addUserScript:cookieInScript]; + } else { + [webView.configuration.userContentController addUserScript:wkScript]; + } + } + +// NSArray *userScripts1 = [NSArray arrayWithArray:webView.configuration.userContentController.userScripts]; +// NSLog(@"insertCookies ======= 注入后 userScripts 个数 = %ld, userScripts = %@", [userScripts1 count], userScripts1); +// +// [webView.configuration.websiteDataStore.httpCookieStore getAllCookies:^(NSArray * _Nonnull cookies) { +// if (cookies) { +// NSLog(@"insertCookies ======= 注入后 cookies 个数 = %ld, cookies = %@", [cookies count], cookies); +// } else { +// NSLog(@"insertCookies ======= 注入后 web cookies is nil"); +// } +// }]; + + // 注入成功后再发请求 + [self wb_sendRequestForWebview:webView startURLSchemeTask:urlSchemeTask]; } @end + diff --git a/ios/RNCWebView.m b/ios/RNCWebView.m index 994d07494b39761692028bb0575d1ff9352f63c7..a2097aaf123db89d6bc66c5ddd761b6600969044 100644 --- a/ios/RNCWebView.m +++ b/ios/RNCWebView.m @@ -44,7 +44,7 @@ static NSString *const kPostMessageHost = @"postMessage"; } @end -@interface RNCWebView () +@interface RNCWebView () @property (nonatomic, copy) RCTDirectEventBlock onLoadingStart; @property (nonatomic, copy) RCTDirectEventBlock onLoadingFinish; @property (nonatomic, copy) RCTDirectEventBlock onLoadingError; @@ -55,6 +55,7 @@ static NSString *const kPostMessageHost = @"postMessage"; @property (nonatomic, copy) RCTDirectEventBlock onScroll; @property (nonatomic, copy) RCTDirectEventBlock onContentProcessDidTerminate; @property (nonatomic, copy) WKWebView *webView; +@property (nonatomic, assign) BOOL isInsertingCookie; // 是否正在注入 cookie @end @implementation RNCWebView @@ -277,6 +278,15 @@ static NSString *const kPostMessageHost = @"postMessage"; if (cookie.path) { [script appendFormat:@" + '; Path=' + %@", RCTJSONStringify(cookie.path, NULL)]; } + + if (cookie.isSecure) { + [script appendFormat:@" + '; isSecure=' + %@", @"TRUE"]; + } + + if (cookie.isHTTPOnly) { + [script appendFormat:@" + '; isHTTPOnly=' + %@", @"TRUE"]; + } + if (cookie.expiresDate) { [script appendFormat:@" + '; Expires=' + new Date(%f).toUTCString()", cookie.expiresDate.timeIntervalSince1970 * 1000 @@ -317,15 +327,21 @@ static NSString *const kPostMessageHost = @"postMessage"; [_webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil]; _webView.allowsBackForwardNavigationGestures = _allowsBackForwardNavigationGestures; - if (_userAgent) { - _webView.customUserAgent = _userAgent; + if ([[NSUserDefaults standardUserDefaults] objectForKey:@"UserAgent"]) { + NSString *userAgent = [[NSUserDefaults standardUserDefaults] objectForKey:@"UserAgent"]; + _webView.customUserAgent = userAgent; } + #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */ if ([_webView.scrollView respondsToSelector:@selector(setContentInsetAdjustmentBehavior:)]) { _webView.scrollView.contentInsetAdjustmentBehavior = _savedContentInsetAdjustmentBehavior; } #endif + // 添加观察者 + [wkWebViewConfig.websiteDataStore.httpCookieStore addObserver:self]; + _isInsertingCookie = NO; + [self addSubview:_webView]; [self setHideKeyboardAccessoryView: _savedHideKeyboardAccessoryView]; [self setKeyboardDisplayRequiresUserAction: _savedKeyboardDisplayRequiresUserAction]; @@ -346,6 +362,9 @@ static NSString *const kPostMessageHost = @"postMessage"; [_webView.configuration.userContentController removeScriptMessageHandlerForName:HistoryShimName]; [_webView.configuration.userContentController removeScriptMessageHandlerForName:MessageHandlerName]; [_webView removeObserver:self forKeyPath:@"estimatedProgress"]; + // 移除观察者 + [_webView.configuration.websiteDataStore.httpCookieStore removeObserver:self]; + _isInsertingCookie = NO; [_webView removeFromSuperview]; #if !TARGET_OS_OSX _webView.scrollView.delegate = nil; @@ -1150,4 +1169,146 @@ static NSString *const kPostMessageHost = @"postMessage"; return request; } +// WKHTTPCookieStore 中 Cookie 发生变化时调用 +- (void)cookiesDidChangeInCookieStore:(WKHTTPCookieStore *)cookieStore{ + + if (_isInsertingCookie) { + // 注入期间禁止进入,防止死循环 + return; + } + + __weak __typeof(self)weakSelf = self; + void (^completionBlock)(BOOL needInsert) = ^(BOOL needInsert) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if (needInsert) { + // 需要重新注入 + [strongSelf wb_insertCookieForWebView]; + } + }; + + Class WBCookieObserverClass = NSClassFromString(@"WBCookieObserver"); + id WBCookieObserverInstance = nil; + if (WBCookieObserverClass && [WBCookieObserverClass respondsToSelector:@selector(sharedInstance)]) { + WBCookieObserverInstance = [WBCookieObserverClass performSelector:@selector(sharedInstance)]; + if (WBCookieObserverInstance && [WBCookieObserverInstance respondsToSelector:@selector(wb_needAddObserverInsertCookie: completion:)]) { + [WBCookieObserverInstance performSelector:@selector(wb_needAddObserverInsertCookie: completion:) withObject:_webView withObject:completionBlock]; + } + } +} + +// 给 webview 注入 cookie +- (void)wb_insertCookieForWebView{ + +// [_webView.configuration.websiteDataStore.httpCookieStore getAllCookies:^(NSArray * _Nonnull cookies) { +// if (cookies) { +// NSLog(@"insertCookies ======= 注入前1 cookies 个数 = %ld, cookies = %@", [cookies count], cookies); +// } else { +// NSLog(@"insertCookies ======= 注入前1 web cookies is nil"); +// } +// }]; + + if (!_webView) { + return; + } + + _isInsertingCookie = YES; + + NSArray *sharedCookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]; + if (sharedCookies && [sharedCookies isKindOfClass:[NSArray class]] && [sharedCookies count] > 0) { + NSLog(@"insertCookies ======= 注入前1 cookies 个数 = %ld, cookies = %@", [sharedCookies count], sharedCookies); + for (int i = 0; i < [sharedCookies count]; i++) { + NSHTTPCookie *cookie = [sharedCookies objectAtIndex:i]; + if (_webView) { + if (i < ([sharedCookies count] - 1)) { + [_webView.configuration.websiteDataStore.httpCookieStore setCookie:cookie completionHandler:nil]; + } else { + // 最后一个添加完后要关闭开关 + __weak __typeof(self)weakSelf = self; + [_webView.configuration.websiteDataStore.httpCookieStore setCookie:cookie completionHandler:^{ + __strong __typeof(weakSelf)strongSelf = weakSelf; + strongSelf.isInsertingCookie = NO; +// [_webView.configuration.websiteDataStore.httpCookieStore getAllCookies:^(NSArray * _Nonnull cookies) { +// if (cookies) { +// NSLog(@"insertCookies ======= 注入1后 cookies 个数 = %ld, cookies = %@", [cookies count], cookies); +// } else { +// NSLog(@"insertCookies ======= 注入1后 web cookies is nil"); +// } +// }]; + }]; + } + } else { + _isInsertingCookie = NO; + break; + return; + } + } + } else { + _isInsertingCookie = NO; + return; + } + + NSMutableString *script = [NSMutableString string]; + [script appendString:@"(function () {\n"]; + for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) { + [script appendFormat:@"document.cookie = %@ + '=' + %@", + RCTJSONStringify(cookie.name, NULL), + RCTJSONStringify(cookie.value, NULL)]; + if (cookie.domain) { + [script appendFormat:@" + '; domain=' + %@", RCTJSONStringify(cookie.domain, NULL)]; + } + if (cookie.path) { + [script appendFormat:@" + '; Path=' + %@", RCTJSONStringify(cookie.path, NULL)]; + } + + if (cookie.isSecure) { + [script appendFormat:@" + '; isSecure=' + %@", @"TRUE"]; + } + + if (cookie.isHTTPOnly) { + [script appendFormat:@" + '; isHTTPOnly=' + %@", @"TRUE"]; + } + + if (cookie.expiresDate) { + [script appendFormat:@" + '; Expires=' + new Date(%f).toUTCString()", + cookie.expiresDate.timeIntervalSince1970 * 1000 + ]; + } + [script appendString:@";\n"]; + } + [script appendString:@"})();\n"]; + WKUserScript* cookieInScript = [[WKUserScript alloc] initWithSource:script + injectionTime:WKUserScriptInjectionTimeAtDocumentStart + forMainFrameOnly:YES]; + + if (_webView) { + // 替换旧的 Cookies + NSArray *userScripts = [NSArray arrayWithArray:_webView.configuration.userContentController.userScripts]; + [_webView.configuration.userContentController removeAllUserScripts]; + for (WKUserScript *wkScript in userScripts) { + NSString *wkSource = wkScript.source; + if ([wkSource containsString:@"document.cookie"]) { + if (_webView) { + [_webView.configuration.userContentController addUserScript:cookieInScript]; + } + } else { + if (_webView) { + [_webView.configuration.userContentController addUserScript:wkScript]; + } + } + } + } + +// NSArray *userScripts1 = [NSArray arrayWithArray:_webView.configuration.userContentController.userScripts]; +// NSLog(@"insertCookies ======= 注入后 userScripts 个数 = %ld, userScripts = %@", [userScripts1 count], userScripts1); +// +// [_webView.configuration.websiteDataStore.httpCookieStore getAllCookies:^(NSArray * _Nonnull cookies) { +// if (cookies) { +// NSLog(@"insertCookies ======= 注入后1 cookies 个数 = %ld, cookies = %@", [cookies count], cookies); +// } else { +// NSLog(@"insertCookies ======= 注入后1 web cookies is nil"); +// } +// }]; +} + + @end