// /** // * Copyright (c) 2015-present, Facebook, Inc. // * // * This source code is licensed under the MIT license found in the // * LICENSE file in the root directory of this source tree. // */ // #import "IRWebView.h" // #import // #import "RCTAutoInsetsProtocol.h" // #import "RCTConvert.h" // #import "RCTEventDispatcher.h" // #import "RCTLog.h" // #import "RCTUtils.h" // #import "RCTView.h" // #import "UIView+React.h" // NSString *const RCTJSNavigationScheme = @"react-js-navigation"; // static NSString *const kPostMessageHost = @"postMessage"; // @interface IRWebView () // @property (nonatomic, copy) RCTDirectEventBlock onLoadingStart; // @property (nonatomic, copy) RCTDirectEventBlock onLoadingFinish; // @property (nonatomic, copy) RCTDirectEventBlock onLoadingError; // @property (nonatomic, copy) RCTDirectEventBlock onShouldStartLoadWithRequest; // @property (nonatomic, copy) RCTDirectEventBlock onMessage; // @end // @implementation IRWebView // { // UIWebView *_webView; // NSString *_injectedJavaScript; // } // - (void)dealloc // { // _webView.delegate = nil; // } // - (instancetype)initWithFrame:(CGRect)frame // { // if ((self = [super initWithFrame:frame])) { // super.backgroundColor = [UIColor clearColor]; // _automaticallyAdjustContentInsets = YES; // _contentInset = UIEdgeInsetsZero; // _webView = [[UIWebView alloc] initWithFrame:self.bounds]; // _webView.delegate = self; // #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 = UIScrollViewContentInsetAdjustmentNever; // } // #endif // [self addSubview:_webView]; // } // return self; // } // RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) // - (void)goForward // { // [_webView goForward]; // } // - (void)goBack // { // [_webView goBack]; // } // - (void)reload // { // NSURLRequest *request = [RCTConvert NSURLRequest:self.source]; // if (request.URL && !_webView.request.URL.absoluteString.length) { // [_webView loadRequest:request]; // } // else { // [_webView reload]; // } // } // - (void)stopLoading // { // [_webView stopLoading]; // } // - (void)postMessage:(NSString *)message // { // NSDictionary *eventInitDict = @{ // @"data": message, // }; // NSString *source = [NSString // stringWithFormat:@"document.dispatchEvent(new MessageEvent('message', %@));", // RCTJSONStringify(eventInitDict, NULL) // ]; // [_webView stringByEvaluatingJavaScriptFromString:source]; // } // - (void)injectJavaScript:(NSString *)script // { // [_webView stringByEvaluatingJavaScriptFromString:script]; // } // - (void)setSource:(NSDictionary *)source // { // if (![_source isEqualToDictionary:source]) { // _source = [source copy]; // // Check for a static html source first // NSString *html = [RCTConvert NSString:source[@"html"]]; // if (html) { // NSURL *baseURL = [RCTConvert NSURL:source[@"baseUrl"]]; // if (!baseURL) { // baseURL = [NSURL URLWithString:@"about:blank"]; // } // [_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 // // the same url. We'll expose a call to 'reload' to allow a user to load // // the existing page. // if ([request.URL isEqual:_webView.request.URL]) { // return; // } // if (!request.URL) { // // Clear the webview // [_webView loadHTMLString:@"" baseURL:nil]; // return; // } // [_webView loadRequest:request]; // } // } // - (void)layoutSubviews // { // [super layoutSubviews]; // _webView.frame = self.bounds; // } // - (void)setContentInset:(UIEdgeInsets)contentInset // { // _contentInset = contentInset; // [RCTView autoAdjustInsetsForView:self // withScrollView:_webView.scrollView // updateOffset:NO]; // } // - (void)setScalesPageToFit:(BOOL)scalesPageToFit // { // if (_webView.scalesPageToFit != scalesPageToFit) { // _webView.scalesPageToFit = scalesPageToFit; // [_webView reload]; // } // } // - (BOOL)scalesPageToFit // { // return _webView.scalesPageToFit; // } // - (void)setBackgroundColor:(UIColor *)backgroundColor // { // CGFloat alpha = CGColorGetAlpha(backgroundColor.CGColor); // self.opaque = _webView.opaque = (alpha == 1.0); // _webView.backgroundColor = backgroundColor; // } // - (UIColor *)backgroundColor // { // return _webView.backgroundColor; // } // - (NSMutableDictionary *)baseEvent // { // NSMutableDictionary *event = [[NSMutableDictionary alloc] initWithDictionary:@{ // @"url": _webView.request.URL.absoluteString ?: @"", // @"loading" : @(_webView.loading), // @"title": [_webView stringByEvaluatingJavaScriptFromString:@"document.title"], // @"canGoBack": @(_webView.canGoBack), // @"canGoForward" : @(_webView.canGoForward), // }]; // return event; // } // - (void)refreshContentInset // { // [RCTView autoAdjustInsetsForView:self // withScrollView:_webView.scrollView // updateOffset:YES]; // } // #pragma mark - UIWebViewDelegate methods // - (BOOL)webView:(__unused UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request // navigationType:(UIWebViewNavigationType)navigationType // { // BOOL isJSNavigation = [request.URL.scheme isEqualToString:RCTJSNavigationScheme]; // static NSDictionary *navigationTypes; // static dispatch_once_t onceToken; // dispatch_once(&onceToken, ^{ // navigationTypes = @{ // @(UIWebViewNavigationTypeLinkClicked): @"click", // @(UIWebViewNavigationTypeFormSubmitted): @"formsubmit", // @(UIWebViewNavigationTypeBackForward): @"backforward", // @(UIWebViewNavigationTypeReload): @"reload", // @(UIWebViewNavigationTypeFormResubmitted): @"formresubmit", // @(UIWebViewNavigationTypeOther): @"other", // }; // }); // // skip this for the JS Navigation handler // if (!isJSNavigation && _onShouldStartLoadWithRequest) { // NSMutableDictionary *event = [self baseEvent]; // [event addEntriesFromDictionary: @{ // @"url": (request.URL).absoluteString, // @"navigationType": navigationTypes[@(navigationType)] // }]; // if (![self.delegate webView:self // shouldStartLoadForRequest:event // withCallback:_onShouldStartLoadWithRequest]) { // return NO; // } // } // if (_onLoadingStart) { // // We have this check to filter out iframe requests and whatnot // BOOL isTopFrame = [request.URL isEqual:request.mainDocumentURL]; // if (isTopFrame) { // NSMutableDictionary *event = [self baseEvent]; // [event addEntriesFromDictionary: @{ // @"url": (request.URL).absoluteString, // @"navigationType": navigationTypes[@(navigationType)] // }]; // _onLoadingStart(event); // } // } // if (isJSNavigation && [request.URL.host isEqualToString:kPostMessageHost]) { // NSString *data = request.URL.query; // data = [data stringByReplacingOccurrencesOfString:@"+" withString:@" "]; // data = [data stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; // NSMutableDictionary *event = [self baseEvent]; // [event addEntriesFromDictionary: @{ // @"data": data, // }]; // NSString *source = @"document.dispatchEvent(new MessageEvent('message:received'));"; // [_webView stringByEvaluatingJavaScriptFromString:source]; // _onMessage(event); // } // // JS Navigation handler // return !isJSNavigation; // } // - (void)webView:(__unused UIWebView *)webView didFailLoadWithError:(NSError *)error // { // if (_onLoadingError) { // if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled) { // // NSURLErrorCancelled is reported when a page has a redirect OR if you load // // a new URL in the WebView before the previous one came back. We can just // // ignore these since they aren't real errors. // // http://stackoverflow.com/questions/1024748/how-do-i-fix-nsurlerrordomain-error-999-in-iphone-3-0-os // return; // } // if ([error.domain isEqualToString:@"WebKitErrorDomain"] && error.code == 102) { // // Error code 102 "Frame load interrupted" is raised by the UIWebView if // // its delegate returns FALSE from webView:shouldStartLoadWithRequest:navigationType // // when the URL is from an http redirect. This is a common pattern when // // implementing OAuth with a WebView. // return; // } // NSMutableDictionary *event = [self baseEvent]; // [event addEntriesFromDictionary:@{ // @"domain": error.domain, // @"code": @(error.code), // @"description": error.localizedDescription, // }]; // _onLoadingError(event); // } // } // - (void)webViewDidFinishLoad:(UIWebView *)webView // { // if (_messagingEnabled) { // #if RCT_DEV // // See isNative in lodash // NSString *testPostMessageNative = @"String(window.postMessage) === String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage')"; // BOOL postMessageIsNative = [ // [webView stringByEvaluatingJavaScriptFromString:testPostMessageNative] // isEqualToString:@"true" // ]; // if (!postMessageIsNative) { // RCTLogError(@"Setting onMessage on a WebView overrides existing values of window.postMessage, but a previous value was defined"); // } // #endif // NSString *source = [NSString stringWithFormat: // @"(function() {" // "window.originalPostMessage = window.postMessage;" // "var messageQueue = [];" // "var messagePending = false;" // "function processQueue() {" // "if (!messageQueue.length || messagePending) return;" // "messagePending = true;" // "window.location = '%@://%@?' + encodeURIComponent(messageQueue.shift());" // "}" // "window.postMessage = function(data) {" // "messageQueue.push(String(data));" // "processQueue();" // "};" // "document.addEventListener('message:received', function(e) {" // "messagePending = false;" // "processQueue();" // "});" // "})();", RCTJSNavigationScheme, kPostMessageHost // ]; // [webView stringByEvaluatingJavaScriptFromString:source]; // } // if (_injectedJavaScript != nil) { // NSString *jsEvaluationValue = [webView stringByEvaluatingJavaScriptFromString:_injectedJavaScript]; // NSMutableDictionary *event = [self baseEvent]; // event[@"jsEvaluationValue"] = jsEvaluationValue; // _onLoadingFinish(event); // } // // we only need the final 'finishLoad' call so only fire the event when we're actually done loading. // else if (_onLoadingFinish && !webView.loading && ![webView.request.URL.absoluteString isEqualToString:@"about:blank"]) { // _onLoadingFinish([self baseEvent]); // } // } // @end