diff --git a/Examples/UIExplorer/UIExplorerList.js b/Examples/UIExplorer/UIExplorerList.js index 43d733506..a8050b33b 100644 --- a/Examples/UIExplorer/UIExplorerList.js +++ b/Examples/UIExplorer/UIExplorerList.js @@ -44,6 +44,7 @@ var COMMON_APIS = [ require('./GeolocationExample'), require('./LayoutExample'), require('./PanResponderExample'), + require('./PointerEventsExample'), ]; if (Platform.OS === 'ios') { @@ -80,7 +81,6 @@ if (Platform.OS === 'ios') { require('./CameraRollExample.ios'), require('./LayoutEventsExample'), require('./NetInfoExample'), - require('./PointerEventsExample'), require('./PushNotificationIOSExample'), require('./StatusBarIOSExample'), require('./TimerExample'), diff --git a/Libraries/Network/NetInfo.js b/Libraries/Network/NetInfo.js index c62cb209a..63df3802b 100644 --- a/Libraries/Network/NetInfo.js +++ b/Libraries/Network/NetInfo.js @@ -11,6 +11,7 @@ */ 'use strict'; +var Map = require('Map'); var NativeModules = require('NativeModules'); var Platform = require('Platform'); var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); @@ -140,30 +141,32 @@ type ConnectivityStateAndroid = $Enum<{ * ``` */ -var _subscriptions = {}; +var _subscriptions = new Map(); var NetInfo = { addEventListener: function ( eventName: ChangeEventName, handler: Function ): void { - _subscriptions[String(handler)] = RCTDeviceEventEmitter.addListener( + var listener = RCTDeviceEventEmitter.addListener( DEVICE_REACHABILITY_EVENT, (appStateData) => { handler(appStateData.network_reachability); } ); + _subscriptions.set(handler, listener); }, removeEventListener: function( eventName: ChangeEventName, handler: Function ): void { - if (!_subscriptions[String(handler)]) { + var listener = _subscriptions.get(handler); + if (!listener) { return; } - _subscriptions[String(handler)].remove(); - _subscriptions[String(handler)] = null; + listener.remove(); + _subscriptions.delete(handler); }, fetch: function(): Promise { @@ -197,19 +200,20 @@ if (Platform.OS === 'ios') { }; } -var _isConnectedSubscriptions = {}; +var _isConnectedSubscriptions = new Map(); NetInfo.isConnected = { addEventListener: function ( eventName: ChangeEventName, handler: Function ): void { - _isConnectedSubscriptions[String(handler)] = (connection) => { + var listener = (connection) => { handler(_isConnected(connection)); }; + _isConnectedSubscriptions.set(handler, listener); NetInfo.addEventListener( eventName, - _isConnectedSubscriptions[String(handler)] + listener ); }, @@ -217,10 +221,12 @@ NetInfo.isConnected = { eventName: ChangeEventName, handler: Function ): void { + var listener = _isConnectedSubscriptions.get(handler); NetInfo.removeEventListener( eventName, - _isConnectedSubscriptions[String(handler)] + listener ); + _isConnectedSubscriptions.delete(handler); }, fetch: function(): Promise { diff --git a/Libraries/Text/RCTShadowText.m b/Libraries/Text/RCTShadowText.m index 511697f89..2dc275912 100644 --- a/Libraries/Text/RCTShadowText.m +++ b/Libraries/Text/RCTShadowText.m @@ -79,6 +79,9 @@ static css_dim_t RCTMeasure(void *context, float width) - (NSTextStorage *)buildTextStorageForWidth:(CGFloat)width { + UIEdgeInsets padding = self.paddingAsInsets; + width -= (padding.left + padding.right); + if (_cachedTextStorage && width == _cachedTextStorageWidth) { return _cachedTextStorage; } @@ -92,16 +95,13 @@ static css_dim_t RCTMeasure(void *context, float width) textContainer.lineFragmentPadding = 0.0; textContainer.lineBreakMode = _numberOfLines > 0 ? NSLineBreakByTruncatingTail : NSLineBreakByClipping; textContainer.maximumNumberOfLines = _numberOfLines; - - UIEdgeInsets padding = self.paddingAsInsets; - width -= (padding.left + padding.right); textContainer.size = (CGSize){isnan(width) ? CGFLOAT_MAX : width, CGFLOAT_MAX}; [layoutManager addTextContainer:textContainer]; [layoutManager ensureLayoutForTextContainer:textContainer]; - _cachedTextStorage = textStorage; _cachedTextStorageWidth = width; + _cachedTextStorage = textStorage; return textStorage; } diff --git a/Libraries/WebSocket/RCTWebSocketExecutor.m b/Libraries/WebSocket/RCTWebSocketExecutor.m index a8cf9ae5e..c5c216fc7 100644 --- a/Libraries/WebSocket/RCTWebSocketExecutor.m +++ b/Libraries/WebSocket/RCTWebSocketExecutor.m @@ -185,6 +185,15 @@ RCT_EXPORT_MODULE() } - (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block +{ + if ([NSThread isMainThread]) { + block(); + } else { + dispatch_async(dispatch_get_main_queue(), block); + } +} + +- (void)executeAsyncBlockOnJavaScriptQueue:(dispatch_block_t)block { dispatch_async(dispatch_get_main_queue(), block); } diff --git a/React/Base/RCTLog.m b/React/Base/RCTLog.m index d99bb4fe1..92c255659 100644 --- a/React/Base/RCTLog.m +++ b/React/Base/RCTLog.m @@ -171,7 +171,22 @@ void _RCTLogFormat( // Log to red box if (level >= RCTLOG_REDBOX_LEVEL) { - [[RCTRedBox sharedInstance] showErrorMessage:message]; + NSArray *stackSymbols = [NSThread callStackSymbols]; + NSMutableArray *stack = [NSMutableArray arrayWithCapacity:(stackSymbols.count - 1)]; + [stackSymbols enumerateObjectsUsingBlock:^(NSString *frameSymbols, NSUInteger idx, BOOL *stop) { + if (idx != 0) { // don't include the current frame + NSString *address = [[frameSymbols componentsSeparatedByString:@"0x"][1] componentsSeparatedByString:@" "][0]; + NSRange addressRange = [frameSymbols rangeOfString:address]; + NSString *methodName = [frameSymbols substringFromIndex:(addressRange.location + addressRange.length + 1)]; + if (idx == 1) { + NSString *file = [[@(fileName) componentsSeparatedByString:@"/"] lastObject]; + stack[0] = @{@"methodName": methodName, @"file": file, @"lineNumber": @(lineNumber)}; + } else { + stack[idx - 1] = @{@"methodName": methodName}; + } + } + }]; + [[RCTRedBox sharedInstance] showErrorMessage:message withStack:stack]; } // Log to JS executor diff --git a/React/Executors/RCTWebViewExecutor.m b/React/Executors/RCTWebViewExecutor.m index fb3c6f031..57c8d25db 100644 --- a/React/Executors/RCTWebViewExecutor.m +++ b/React/Executors/RCTWebViewExecutor.m @@ -182,25 +182,19 @@ RCT_EXPORT_MODULE() [_webView loadHTMLString:runScript baseURL:url]; } -/** - * In order to avoid `UIWebView` thread locks, all JS executions should be - * performed outside of the event loop that notifies the `UIWebViewDelegate` - * that the page has loaded. This is only an issue with the remote debug mode of - * `UIWebView`. For a production `UIWebView` deployment, this delay is - * unnecessary and possibly harmful (or helpful?) - * - * The delay might not be needed as soon as the following change lands into - * iOS7. (Review the patch linked here and search for "crash" - * https://bugs.webkit.org/show_bug.cgi?id=125746). - */ - (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block { - dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_MSEC); - dispatch_after(when, dispatch_get_main_queue(), ^{ - RCTAssertMainThread(); + if ([NSThread isMainThread]) { block(); - }); + } else { + dispatch_async(dispatch_get_main_queue(), block); + } +} + +- (void)executeAsyncBlockOnJavaScriptQueue:(dispatch_block_t)block +{ + dispatch_async(dispatch_get_main_queue(), block); } /** diff --git a/React/Modules/RCTTiming.m b/React/Modules/RCTTiming.m index 4f92cfb05..2f3f6d40b 100644 --- a/React/Modules/RCTTiming.m +++ b/React/Modules/RCTTiming.m @@ -182,6 +182,11 @@ RCT_EXPORT_METHOD(createTimer:(NSNumber *)callbackID NSTimeInterval jsSchedulingOverhead = -jsSchedulingTime.timeIntervalSinceNow; if (jsSchedulingOverhead < 0) { RCTLogWarn(@"jsSchedulingOverhead (%ims) should be positive", (int)(jsSchedulingOverhead * 1000)); + + /** + * Probably debugging on device, set to 0 so we don't ignore the interval + */ + jsSchedulingOverhead = 0; } NSTimeInterval targetTime = jsDuration - jsSchedulingOverhead; diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 2de427ed0..366087480 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -371,9 +371,7 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewNa rootShadowView.frame = frame; [rootShadowView updateLayout]; - RCTViewManagerUIBlock uiBlock = [self uiBlockWithLayoutUpdateForRootView:rootShadowView]; - [self addUIBlock:uiBlock]; - [self flushUIBlocks]; + [self batchDidComplete]; }); }