Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 102 additions & 1 deletion apple/RNCWebViewImpl.m
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ - (WKWebViewConfiguration *)setUpWkWebViewConfig
wkWebViewConfig.processPool = [[RNCWKProcessPoolManager sharedManager] sharedProcessPool];
}
wkWebViewConfig.userContentController = [WKUserContentController new];
[wkWebViewConfig.userContentController addScriptMessageHandler:self name:@"base64Handler"];

#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 /* iOS 13 */
if (@available(iOS 13.0, *)) {
Expand Down Expand Up @@ -771,7 +772,10 @@ - (void)setAutomaticallyAdjustsScrollIndicatorInsets:(BOOL)automaticallyAdjustsS
- (void)userContentController:(WKUserContentController *)userContentController
didReceiveScriptMessage:(WKScriptMessage *)message
{
if ([message.name isEqualToString:HistoryShimName]) {
if ([message.name isEqualToString:@"base64Handler"]) {
NSString *base64String = message.body;
[self downloadBase64File:base64String];
} else if ([message.name isEqualToString:HistoryShimName]) {
if (_onLoadingFinish) {
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
[event addEntriesFromDictionary: @{@"navigationType": message.body}];
Expand Down Expand Up @@ -962,6 +966,68 @@ -(void)setKeyboardDisplayRequiresUserAction:(BOOL)keyboardDisplayRequiresUserAct
method_setImplementation(method, override);
}

- (void)downloadBase64File:(NSString *)base64String {
NSArray *components = [base64String componentsSeparatedByString:@","];
NSString *base64ContentPart = components.lastObject;

NSData *fileData = [[NSData alloc] initWithBase64EncodedString:base64ContentPart options:NSDataBase64DecodingIgnoreUnknownCharacters];
NSString *fileExtension = [self fileExtensionFromBase64String:base64String];

NSString *tempFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"File.%@", fileExtension]];
[fileData writeToFile:tempFilePath atomically:YES];

NSURL *tempFileURL = [NSURL fileURLWithPath:tempFilePath];

UIDocumentPickerViewController *documentPicker = nil;
if (@available(iOS 14.0, *)) {
documentPicker = [[UIDocumentPickerViewController alloc] initForExportingURLs:@[tempFileURL] asCopy:YES];
} else {
// Usage of initWithURL:inMode: might lose file's extension and user has to type it manually
// Problem was solved for iOS 14 and higher with initForExportingURLs
documentPicker = [[UIDocumentPickerViewController alloc] initWithURL:tempFileURL inMode:UIDocumentPickerModeExportToService];
}
documentPicker.delegate = self;
documentPicker.modalPresentationStyle = UIModalPresentationFullScreen;

UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
while (rootViewController.presentedViewController) {
rootViewController = rootViewController.presentedViewController;
}
[rootViewController presentViewController:documentPicker animated:YES completion:nil];
}

- (NSString *)fileExtensionFromBase64String:(NSString *)base64String {
NSRange mimeTypeRange = [base64String rangeOfString:@"data:"];
NSRange base64Range = [base64String rangeOfString:@";base64"];

if (mimeTypeRange.location != NSNotFound && base64Range.location != NSNotFound) {
NSUInteger start = NSMaxRange(mimeTypeRange);
NSUInteger length = base64Range.location - start;
NSString *mimeType = [base64String substringWithRange:NSMakeRange(start, length)];

NSDictionary *mimeTypeToExtension = @{
@"image/png": @"png",
@"image/jpeg": @"jpg",
@"image/gif": @"gif",
@"application/pdf": @"pdf",
@"text/plain": @"txt",
@"application/json": @"json",
@"application/octet-stream": @"bin"
};

NSString *fileExtension = mimeTypeToExtension[mimeType];
return fileExtension ?: @"bin";
}

return @"bin";
}

- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
if (urls.count > 0) {
NSURL *destinationURL = urls.firstObject;
}
}

-(void)setHideKeyboardAccessoryView:(BOOL)hideKeyboardAccessoryView
{
_hideKeyboardAccessoryView = hideKeyboardAccessoryView;
Expand Down Expand Up @@ -1322,6 +1388,21 @@ - (void) webView:(WKWebView *)webView
static NSDictionary<NSNumber *, NSString *> *navigationTypes;
static dispatch_once_t onceToken;

NSString *urlString = navigationAction.request.URL.absoluteString;
if ([urlString hasPrefix:@"blob:"]) {
if (_onFileDownload) {
[self handleBlobDownloadUrl:urlString];
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
} else if ([urlString hasPrefix:@"data:"]) {
if (_onFileDownload) {
[self downloadBase64File:urlString];
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
}

dispatch_once(&onceToken, ^{
navigationTypes = @{
@(WKNavigationTypeLinkActivated): @"click",
Expand Down Expand Up @@ -1952,6 +2033,26 @@ - (NSURLRequest *)requestForSource:(id)json {
return request;
}

- (void)handleBlobDownloadUrl:(NSString *)urlString {
NSString *jsCode = [NSString stringWithFormat:
@"var xhr = new XMLHttpRequest();"
"xhr.open('GET', '%@', true);"
"xhr.responseType = 'blob';"
"xhr.onload = function(e) {"
" if (this.status == 200) {"
" var blobFile = this.response;"
" var reader = new FileReader();"
" reader.readAsDataURL(blobFile);"
" reader.onloadend = function() {"
" var base64 = reader.result;"
" window.webkit.messageHandlers.base64Handler.postMessage(base64);"
" };"
" }"
"};"
"xhr.send();", urlString];
[self.webView evaluateJavaScript:jsCode completionHandler:^(id result, NSError *error) {}];
}

@end

@implementation RNCWeakScriptMessageDelegate
Expand Down
Loading