Skip to content
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

[expo] Upgrade react-native-webview to 5.8.1 #4146

Merged
merged 5 commits into from May 10, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -36,6 +36,7 @@ This is the log of notable changes to the Expo client that are developer-facing.
- added `Location.enableNetworkProviderAsync` method to ask the user to turn on high accuracy location services by [@tsapeta](https://github.com/tsapeta) ([#3273](https://github.com/expo/expo/pull/3273))
- upgraded Facebook Audience Network SDK dependency to 5.1.1 by [@sjchmiela](https://github.com/sjchmiela) ([#3394](https://github.com/expo/expo/pull/3394))
- upgraded Facebook Core- and LoginKit dependency to 4.40.0 by [@sjchmiela](https://github.com/sjchmiela) ([#3394](https://github.com/expo/expo/pull/3394))
- upgraded `react-native-webview` to `5.8.1` by [@sjchmiela](https://github.com/sjchmiela) ([#4146](https://github.com/expo/expo/pull/4146))
- upgrade `react-native-maps` to `0.23.0` by [@sjchmiela](https://github.com/sjchmiela) ([#3389](https://github.com/expo/expo/pull/3389))
- added Firebase integration to `expo-analytics-segment` by [@sjchmiela](https://github.com/sjchmiela) ([#3615](https://github.com/expo/expo/pull/3615))
- added support for new arguments in `WebBrowser.openBrowserAsync` as described in [the documentation](https://docs.expo.io/versions/latest/sdk/webbrowser/) by [@mczernek](https://github.com/mczernek) ([#3691](https://github.com/expo/expo/pull/3691))
Expand Down
Expand Up @@ -319,6 +319,11 @@ public void setThirdPartyCookiesEnabled(WebView view, boolean enabled) {
}
}

@ReactProp(name = "textZoom")
public void setTextZoom(WebView view, int value) {
view.getSettings().setTextZoom(value);
}

@ReactProp(name = "scalesPageToFit")
public void setScalesPageToFit(WebView view, boolean enabled) {
view.getSettings().setLoadWithOverviewMode(enabled);
Expand Down Expand Up @@ -369,12 +374,8 @@ public void setSource(WebView view, @Nullable ReadableMap source) {
if (source != null) {
if (source.hasKey("html")) {
String html = source.getString("html");
if (source.hasKey("baseUrl")) {
view.loadDataWithBaseURL(
source.getString("baseUrl"), html, HTML_MIME_TYPE, HTML_ENCODING, null);
} else {
view.loadData(html, HTML_MIME_TYPE + "; charset=" + HTML_ENCODING, null);
}
String baseUrl = source.hasKey("baseUrl") ? source.getString("baseUrl") : "";
view.loadDataWithBaseURL(baseUrl, html, HTML_MIME_TYPE, HTML_ENCODING, null);
return;
}
if (source.hasKey("uri")) {
Expand Down
Expand Up @@ -28,6 +28,7 @@
@property (nonatomic, assign) BOOL messagingEnabled;
@property (nonatomic, copy) NSString *injectedJavaScript;
@property (nonatomic, assign) BOOL scrollEnabled;
@property (nonatomic, assign) BOOL sharedCookiesEnabled;
@property (nonatomic, assign) BOOL pagingEnabled;
@property (nonatomic, assign) CGFloat decelerationRate;
@property (nonatomic, assign) BOOL allowsInlineMediaPlayback;
Expand All @@ -38,6 +39,7 @@
#endif
@property (nonatomic, assign) UIEdgeInsets contentInset;
@property (nonatomic, assign) BOOL automaticallyAdjustContentInsets;
@property (nonatomic, assign) BOOL keyboardDisplayRequiresUserAction;
@property (nonatomic, assign) BOOL hideKeyboardAccessoryView;
@property (nonatomic, assign) BOOL allowsBackForwardNavigationGestures;
@property (nonatomic, assign) BOOL incognito;
Expand Down
239 changes: 209 additions & 30 deletions ios/Exponent/Versioned/Core/Api/Components/WebView/RNCWKWebView.m
Expand Up @@ -41,6 +41,13 @@ @implementation RNCWKWebView
{
UIColor * _savedBackgroundColor;
BOOL _savedHideKeyboardAccessoryView;
BOOL _savedKeyboardDisplayRequiresUserAction;

// Workaround for StatusBar appearance bug for iOS 12
// https://github.com/react-native-community/react-native-webview/issues/62
BOOL _isFullScreenVideoOpen;
UIStatusBarStyle _savedStatusBarStyle;
BOOL _savedStatusBarHidden;
}

- (instancetype)initWithFrame:(CGRect)frame
Expand All @@ -54,11 +61,14 @@ - (instancetype)initWithFrame:(CGRect)frame
_directionalLockEnabled = YES;
_automaticallyAdjustContentInsets = YES;
_contentInset = UIEdgeInsetsZero;
_savedKeyboardDisplayRequiresUserAction = YES;
_savedStatusBarStyle = RCTSharedApplication().statusBarStyle;
_savedStatusBarHidden = RCTSharedApplication().statusBarHidden;
}

// Workaround for a keyboard dismissal bug present in iOS 12
// https://openradar.appspot.com/radar?id=5018321736957952
if (@available(iOS 12.0, *)) {
// Workaround for a keyboard dismissal bug present in iOS 12
// https://openradar.appspot.com/radar?id=5018321736957952
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(keyboardWillHide)
Expand All @@ -67,8 +77,12 @@ - (instancetype)initWithFrame:(CGRect)frame
addObserver:self
selector:@selector(keyboardWillShow)
name:UIKeyboardWillShowNotification object:nil];

// Workaround for StatusBar appearance bug for iOS 12
// https://github.com/react-native-community/react-native-webview/issues/62
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(toggleFullScreenVideoStatusBars) name:@"_MRMediaRemotePlayerSupportedCommandsDidChangeNotification" object:nil];
}

return self;
}

Expand Down Expand Up @@ -127,6 +141,68 @@ - (void)didMoveToWindow
wkWebViewConfig.mediaPlaybackRequiresUserAction = _mediaPlaybackRequiresUserAction;
#endif

if(_sharedCookiesEnabled) {
// More info to sending cookies with WKWebView
// https://stackoverflow.com/questions/26573137/can-i-set-the-cookies-to-be-used-by-a-wkwebview/26577303#26577303
if (@available(iOS 11.0, *)) {
// Set Cookies in iOS 11 and above, initialize websiteDataStore before setting cookies
// See also https://forums.developer.apple.com/thread/97194
// check if websiteDataStore has not been initialized before
if(!_incognito && !_cacheEnabled) {
wkWebViewConfig.websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
}
for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
[wkWebViewConfig.websiteDataStore.httpCookieStore setCookie:cookie completionHandler:nil];
}
} else {
NSMutableString *script = [NSMutableString string];

// Clear all existing cookies in a direct called function. This ensures that no
// javascript error will break the web content javascript.
// We keep this code here, if someone requires that Cookies are also removed within the
// the WebView and want to extends the current sharedCookiesEnabled option with an
// additional property.
// Generates JS: document.cookie = "key=; Expires=Thu, 01 Jan 1970 00:00:01 GMT;"
// for each cookie which is already available in the WebView context.
/*
[script appendString:@"(function () {\n"];
[script appendString:@" var cookies = document.cookie.split('; ');\n"];
[script appendString:@" for (var i = 0; i < cookies.length; i++) {\n"];
[script appendString:@" if (cookies[i].indexOf('=') !== -1) {\n"];
[script appendString:@" document.cookie = cookies[i].split('=')[0] + '=; Expires=Thu, 01 Jan 1970 00:00:01 GMT';\n"];
[script appendString:@" }\n"];
[script appendString:@" }\n"];
[script appendString:@"})();\n\n"];
*/

// Set cookies in a direct called function. This ensures that no
// javascript error will break the web content javascript.
// Generates JS: document.cookie = "key=value; Path=/; Expires=Thu, 01 Jan 20xx 00:00:01 GMT;"
// for each cookie which is available in the application context.
[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.path) {
[script appendFormat:@" + '; Path=' + %@", RCTJSONStringify(cookie.path, NULL)];
}
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];
[wkWebViewConfig.userContentController addUserScript:cookieInScript];
}
}

_webView = [[WKWebView alloc] initWithFrame:self.bounds configuration: wkWebViewConfig];
_webView.scrollView.delegate = self;
_webView.UIDelegate = self;
Expand All @@ -152,6 +228,7 @@ - (void)didMoveToWindow

[self addSubview:_webView];
[self setHideKeyboardAccessoryView: _savedHideKeyboardAccessoryView];
[self setKeyboardDisplayRequiresUserAction: _savedKeyboardDisplayRequiresUserAction];
[self visitSource];
}
}
Expand All @@ -176,6 +253,24 @@ - (void)removeFromSuperview
[super removeFromSuperview];
}

-(void)toggleFullScreenVideoStatusBars
{
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
if (!_isFullScreenVideoOpen) {
_isFullScreenVideoOpen = YES;
RCTUnsafeExecuteOnMainQueueSync(^{
[RCTSharedApplication() setStatusBarStyle:UIStatusBarStyleLightContent animated:YES];
});
} else {
_isFullScreenVideoOpen = NO;
RCTUnsafeExecuteOnMainQueueSync(^{
[RCTSharedApplication() setStatusBarHidden:_savedStatusBarHidden animated:YES];
[RCTSharedApplication() setStatusBarStyle:_savedStatusBarStyle animated:YES];
});
}
#pragma clang diagnostic pop
}

-(void)keyboardWillHide
{
keyboardTimer = [NSTimer scheduledTimerWithTimeInterval:0 target:self selector:@selector(keyboardDisplacementFix) userInfo:nil repeats:false];
Expand Down Expand Up @@ -270,31 +365,94 @@ - (void)refreshContentInset

- (void)visitSource
{
// 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"];
// 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;
}
[_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.URL]) {
return;
}
if (!request.URL) {
// Clear the webview
[_webView loadHTMLString:@"" baseURL:nil];
return;
}
[_webView loadRequest:request];
NSURLRequest *request = [self requestForSource:_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.URL]) {
return;
}
if (!request.URL) {
// Clear the webview
[_webView loadHTMLString:@"" baseURL:nil];
return;
}
if (request.URL.host) {
[_webView loadRequest:request];
}
else {
[_webView loadFileURL:request.URL allowingReadAccessToURL:request.URL];
}
}

-(void)setKeyboardDisplayRequiresUserAction:(BOOL)keyboardDisplayRequiresUserAction
{
if (_webView == nil) {
_savedKeyboardDisplayRequiresUserAction = keyboardDisplayRequiresUserAction;
return;
}

if (_savedKeyboardDisplayRequiresUserAction == true) {
return;
}

UIView* subview;

for (UIView* view in _webView.scrollView.subviews) {
if([[view.class description] hasPrefix:@"WK"])
subview = view;
}

if(subview == nil) return;

Class class = subview.class;

NSOperatingSystemVersion iOS_11_3_0 = (NSOperatingSystemVersion){11, 3, 0};
NSOperatingSystemVersion iOS_12_2_0 = (NSOperatingSystemVersion){12, 2, 0};

Method method;
IMP override;

if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion: iOS_12_2_0]) {
// iOS 12.2.0 - Future
SEL selector = sel_getUid("_elementDidFocus:userIsInteracting:blurPreviousNode:changingActivityState:userObject:");
method = class_getInstanceMethod(class, selector);
IMP original = method_getImplementation(method);
override = imp_implementationWithBlock(^void(id me, void* arg0, BOOL arg1, BOOL arg2, BOOL arg3, id arg4) {
((void (*)(id, SEL, void*, BOOL, BOOL, BOOL, id))original)(me, selector, arg0, TRUE, arg2, arg3, arg4);
});
}
else if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion: iOS_11_3_0]) {
// iOS 11.3.0 - 12.2.0
SEL selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:changingActivityState:userObject:");
method = class_getInstanceMethod(class, selector);
IMP original = method_getImplementation(method);
override = imp_implementationWithBlock(^void(id me, void* arg0, BOOL arg1, BOOL arg2, BOOL arg3, id arg4) {
((void (*)(id, SEL, void*, BOOL, BOOL, BOOL, id))original)(me, selector, arg0, TRUE, arg2, arg3, arg4);
});
} else {
// iOS 9.0 - 11.3.0
SEL selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:userObject:");
method = class_getInstanceMethod(class, selector);
IMP original = method_getImplementation(method);
override = imp_implementationWithBlock(^void(id me, void* arg0, BOOL arg1, BOOL arg2, id arg3) {
((void (*)(id, SEL, void*, BOOL, BOOL, id))original)(me, selector, arg0, TRUE, arg2, arg3);
});
}

method_setImplementation(method, override);
}

-(void)setHideKeyboardAccessoryView:(BOOL)hideKeyboardAccessoryView
Expand Down Expand Up @@ -393,7 +551,7 @@ - (void)layoutSubviews
{
NSDictionary *event = @{
@"url": _webView.URL.absoluteString ?: @"",
@"title": _webView.title,
@"title": _webView.title ?: @"",
@"loading" : @(_webView.loading),
@"canGoBack": @(_webView.canGoBack),
@"canGoForward" : @(_webView.canGoForward)
Expand Down Expand Up @@ -656,11 +814,11 @@ - (void)reload
* [_webView reload] doesn't reload the webpage. Therefore, we must
* manually call [_webView loadRequest:request].
*/
NSURLRequest *request = [RCTConvert NSURLRequest:self.source];
NSURLRequest *request = [self requestForSource:self.source];

if (request.URL && !_webView.URL.absoluteString.length) {
[_webView loadRequest:request];
}
else {
} else {
[_webView reload];
}
}
Expand All @@ -675,4 +833,25 @@ - (void)setBounces:(BOOL)bounces
_bounces = bounces;
_webView.scrollView.bounces = bounces;
}

- (NSURLRequest *)requestForSource:(id)json {
NSURLRequest *request = [RCTConvert NSURLRequest:self.source];

// If sharedCookiesEnabled we automatically add all application cookies to the
// http request. This is automatically done on iOS 11+ in the WebView constructor.
// Se we need to manually add these shared cookies here only for iOS versions < 11.
if (_sharedCookiesEnabled) {
if (@available(iOS 11.0, *)) {
// see WKWebView initialization for added cookies
} else {
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:request.URL];
NSDictionary<NSString *, NSString *> *cookieHeader = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
NSMutableURLRequest *mutableRequest = [request mutableCopy];
[mutableRequest setAllHTTPHeaderFields:cookieHeader];
return mutableRequest;
}
}
return request;
}

@end
Expand Up @@ -95,6 +95,10 @@ - (UIView *)view
view.scrollEnabled = json == nil ? true : [RCTConvert BOOL: json];
}

RCT_CUSTOM_VIEW_PROPERTY(sharedCookiesEnabled, BOOL, RNCWKWebView) {
view.sharedCookiesEnabled = json == nil ? false : [RCTConvert BOOL: json];
}

RCT_CUSTOM_VIEW_PROPERTY(decelerationRate, CGFloat, RNCWKWebView) {
view.decelerationRate = json == nil ? UIScrollViewDecelerationRateNormal : [RCTConvert CGFloat: json];
}
Expand All @@ -111,6 +115,10 @@ - (UIView *)view
view.showsVerticalScrollIndicator = json == nil ? true : [RCTConvert BOOL: json];
}

RCT_CUSTOM_VIEW_PROPERTY(keyboardDisplayRequiresUserAction, BOOL, RNCWKWebView) {
view.keyboardDisplayRequiresUserAction = json == nil ? true : [RCTConvert BOOL: json];
}

RCT_EXPORT_METHOD(injectJavaScript:(nonnull NSNumber *)reactTag script:(NSString *)script)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RNCWKWebView *> *viewRegistry) {
Expand Down