mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-29 04:35:36 +08:00
Improved logging and dev menu
This commit is contained in:
@@ -264,7 +264,9 @@ RCT_EXPORT_METHOD(stopAnimation:(NSNumber *)animationTag)
|
|||||||
RCTAnimationExperimentalManager *strongSelf = weakSelf;
|
RCTAnimationExperimentalManager *strongSelf = weakSelf;
|
||||||
|
|
||||||
NSNumber *reactTag = strongSelf->_animationRegistry[animationTag];
|
NSNumber *reactTag = strongSelf->_animationRegistry[animationTag];
|
||||||
if (!reactTag) return;
|
if (!reactTag) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
UIView *view = viewRegistry[reactTag];
|
UIView *view = viewRegistry[reactTag];
|
||||||
for (NSString *animationKey in view.layer.animationKeys) {
|
for (NSString *animationKey in view.layer.animationKeys) {
|
||||||
|
|||||||
@@ -9,11 +9,50 @@
|
|||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
@class RCTBridge;
|
#import "RCTBridge.h"
|
||||||
|
#import "RCTBridgeModule.h"
|
||||||
|
#import "RCTInvalidating.h"
|
||||||
|
|
||||||
@interface RCTDevMenu : NSObject
|
/**
|
||||||
|
* Developer menu, useful for exposing extra functionality when debugging.
|
||||||
|
*/
|
||||||
|
@interface RCTDevMenu : NSObject <RCTBridgeModule, RCTInvalidating>
|
||||||
|
|
||||||
- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER;
|
/**
|
||||||
|
* Is the menu enabled. The menu is enabled by default in debug mode, but
|
||||||
|
* you may wish to disable it so that you can provide your own shake handler.
|
||||||
|
*/
|
||||||
|
@property (nonatomic, assign) BOOL shakeToShow;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables performance profiling.
|
||||||
|
*/
|
||||||
|
@property (nonatomic, assign) BOOL profilingEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables automatic polling for JS code changes. Only applicable when
|
||||||
|
* running the app from a server.
|
||||||
|
*/
|
||||||
|
@property (nonatomic, assign) BOOL liveReloadEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The time between checks for code changes. Defaults to 1 second.
|
||||||
|
*/
|
||||||
|
@property (nonatomic, assign) NSTimeInterval liveReloadPeriod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manually show the menu. This will.
|
||||||
|
*/
|
||||||
- (void)show;
|
- (void)show;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This category makes the developer menu instance available via the
|
||||||
|
* RCTBridge, which is useful for any class that needs to access the menu.
|
||||||
|
*/
|
||||||
|
@interface RCTBridge (RCTDevMenu)
|
||||||
|
|
||||||
|
@property (nonatomic, readonly) RCTDevMenu *devMenu;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|||||||
@@ -9,12 +9,13 @@
|
|||||||
|
|
||||||
#import "RCTDevMenu.h"
|
#import "RCTDevMenu.h"
|
||||||
|
|
||||||
#import "RCTRedBox.h"
|
#import "RCTBridge.h"
|
||||||
|
#import "RCTLog.h"
|
||||||
#import "RCTRootView.h"
|
#import "RCTRootView.h"
|
||||||
#import "RCTSourceCode.h"
|
#import "RCTSourceCode.h"
|
||||||
#import "RCTWebViewExecutor.h"
|
#import "RCTUtils.h"
|
||||||
|
|
||||||
@interface RCTBridge (RCTDevMenu)
|
@interface RCTBridge (Profiling)
|
||||||
|
|
||||||
@property (nonatomic, copy, readonly) NSArray *profile;
|
@property (nonatomic, copy, readonly) NSArray *profile;
|
||||||
|
|
||||||
@@ -23,87 +24,206 @@
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
static NSString *const RCTShowDevMenuNotification = @"RCTShowDevMenuNotification";
|
||||||
|
|
||||||
|
@implementation UIWindow (RCTDevMenu)
|
||||||
|
|
||||||
|
- (void)RCT_motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
|
||||||
|
{
|
||||||
|
if (event.subtype == UIEventSubtypeMotionShake) {
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTShowDevMenuNotification object:nil];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@interface RCTDevMenu () <UIActionSheetDelegate>
|
@interface RCTDevMenu () <UIActionSheetDelegate>
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation RCTDevMenu
|
@implementation RCTDevMenu
|
||||||
{
|
{
|
||||||
BOOL _liveReload;
|
NSTimer *_updateTimer;
|
||||||
__weak RCTBridge *_bridge;
|
UIActionSheet *_actionSheet;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
@synthesize bridge = _bridge;
|
||||||
|
|
||||||
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
|
+ (void)initialize
|
||||||
{
|
{
|
||||||
if (self = [super init]) {
|
// We're swizzling here because it's poor form to override methods in a category,
|
||||||
_bridge = bridge;
|
// however UIWindow doesn't actually implement motionEnded:withEvent:, so there's
|
||||||
|
// no need to call the original implementation.
|
||||||
|
RCTSwapInstanceMethods([UIWindow class], @selector(motionEnded:withEvent:), @selector(RCT_motionEnded:withEvent:));
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)init
|
||||||
|
{
|
||||||
|
if ((self = [super init])) {
|
||||||
|
|
||||||
|
_shakeToShow = YES;
|
||||||
|
_liveReloadPeriod = 1.0; // 1 second
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
|
selector:@selector(showOnShake)
|
||||||
|
name:RCTShowDevMenuNotification
|
||||||
|
object:nil];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)showOnShake
|
||||||
|
{
|
||||||
|
if (_shakeToShow) {
|
||||||
|
[self show];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (void)show
|
- (void)show
|
||||||
{
|
{
|
||||||
NSString *debugTitleChrome = _bridge.executorClass != Nil && _bridge.executorClass == NSClassFromString(@"RCTWebSocketExecutor") ? @"Disable Chrome Debugging" : @"Enable Chrome Debugging";
|
if (_actionSheet) {
|
||||||
NSString *debugTitleSafari = _bridge.executorClass == [RCTWebViewExecutor class] ? @"Disable Safari Debugging" : @"Enable Safari Debugging";
|
return;
|
||||||
NSString *liveReloadTitle = _liveReload ? @"Disable Live Reload" : @"Enable Live Reload";
|
}
|
||||||
|
|
||||||
|
NSString *debugTitleChrome = _bridge.executorClass && _bridge.executorClass == NSClassFromString(@"RCTWebSocketExecutor") ? @"Disable Chrome Debugging" : @"Enable Chrome Debugging";
|
||||||
|
NSString *debugTitleSafari = _bridge.executorClass && _bridge.executorClass == NSClassFromString(@"RCTWebViewExecutor") ? @"Disable Safari Debugging" : @"Enable Safari Debugging";
|
||||||
|
NSString *liveReloadTitle = _liveReloadEnabled ? @"Disable Live Reload" : @"Enable Live Reload";
|
||||||
NSString *profilingTitle = _bridge.profile ? @"Stop Profiling" : @"Start Profiling";
|
NSString *profilingTitle = _bridge.profile ? @"Stop Profiling" : @"Start Profiling";
|
||||||
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"React Native: Development"
|
|
||||||
delegate:self
|
UIActionSheet *actionSheet =
|
||||||
cancelButtonTitle:@"Cancel"
|
[[UIActionSheet alloc] initWithTitle:@"React Native: Development"
|
||||||
destructiveButtonTitle:nil
|
delegate:self
|
||||||
otherButtonTitles:@"Reload", debugTitleChrome, debugTitleSafari, liveReloadTitle, profilingTitle, nil];
|
cancelButtonTitle:@"Cancel"
|
||||||
|
destructiveButtonTitle:nil
|
||||||
|
otherButtonTitles:@"Reload", debugTitleChrome, debugTitleSafari, liveReloadTitle, profilingTitle, nil];
|
||||||
|
|
||||||
actionSheet.actionSheetStyle = UIBarStyleBlack;
|
actionSheet.actionSheetStyle = UIBarStyleBlack;
|
||||||
[actionSheet showInView:[[[[UIApplication sharedApplication] keyWindow] rootViewController] view]];
|
[actionSheet showInView:[UIApplication sharedApplication].keyWindow.rootViewController.view];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
|
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
|
||||||
{
|
{
|
||||||
if (buttonIndex == 0) {
|
_actionSheet = nil;
|
||||||
[_bridge reload];
|
|
||||||
} else if (buttonIndex == 1) {
|
|
||||||
Class cls = NSClassFromString(@"RCTWebSocketExecutor");
|
|
||||||
_bridge.executorClass = (_bridge.executorClass != cls) ? cls : nil;
|
|
||||||
[_bridge reload];
|
|
||||||
} else if (buttonIndex == 2) {
|
|
||||||
Class cls = [RCTWebViewExecutor class];
|
|
||||||
_bridge.executorClass = (_bridge.executorClass != cls) ? cls : Nil;
|
|
||||||
[_bridge reload];
|
|
||||||
} else if (buttonIndex == 3) {
|
|
||||||
_liveReload = !_liveReload;
|
|
||||||
[self _pollAndReload];
|
|
||||||
} else if (buttonIndex == 4) {
|
|
||||||
if (_bridge.profile) {
|
|
||||||
[_bridge stopProfiling];
|
|
||||||
} else {
|
|
||||||
[_bridge startProfiling];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_pollAndReload
|
switch (buttonIndex) {
|
||||||
{
|
case 0: {
|
||||||
if (_liveReload) {
|
|
||||||
RCTSourceCode *sourceCodeModule = _bridge.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])];
|
|
||||||
NSURL *url = sourceCodeModule.scriptURL;
|
|
||||||
NSURL *longPollURL = [[NSURL alloc] initWithString:@"/onchange" relativeToURL:url];
|
|
||||||
[self performSelectorInBackground:@selector(_checkForUpdates:) withObject:longPollURL];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_checkForUpdates:(NSURL *)URL
|
|
||||||
{
|
|
||||||
NSMutableURLRequest *longPollRequest = [NSMutableURLRequest requestWithURL:URL];
|
|
||||||
longPollRequest.timeoutInterval = 30;
|
|
||||||
NSHTTPURLResponse *response;
|
|
||||||
[NSURLConnection sendSynchronousRequest:longPollRequest returningResponse:&response error:nil];
|
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
if (_liveReload && response.statusCode == 205) {
|
|
||||||
[[RCTRedBox sharedInstance] dismiss];
|
|
||||||
[_bridge reload];
|
[_bridge reload];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
[self _pollAndReload];
|
case 1: {
|
||||||
});
|
Class cls = NSClassFromString(@"RCTWebSocketExecutor");
|
||||||
|
_bridge.executorClass = (_bridge.executorClass != cls) ? cls : nil;
|
||||||
|
[_bridge reload];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
Class cls = NSClassFromString(@"RCTWebViewExecutor");
|
||||||
|
_bridge.executorClass = (_bridge.executorClass != cls) ? cls : Nil;
|
||||||
|
[_bridge reload];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
self.liveReloadEnabled = !_liveReloadEnabled;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
self.profilingEnabled = !_profilingEnabled;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setProfilingEnabled:(BOOL)enabled
|
||||||
|
{
|
||||||
|
if (_profilingEnabled == enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_profilingEnabled = enabled;
|
||||||
|
if (_bridge.profile) {
|
||||||
|
[_bridge stopProfiling];
|
||||||
|
} else {
|
||||||
|
[_bridge startProfiling];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setLiveReloadEnabled:(BOOL)enabled
|
||||||
|
{
|
||||||
|
if (_liveReloadEnabled == enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_liveReloadEnabled = enabled;
|
||||||
|
if (_liveReloadEnabled) {
|
||||||
|
|
||||||
|
_updateTimer = [NSTimer scheduledTimerWithTimeInterval:_liveReloadPeriod
|
||||||
|
target:self
|
||||||
|
selector:@selector(pollForUpdates)
|
||||||
|
userInfo:nil
|
||||||
|
repeats:YES];
|
||||||
|
} else {
|
||||||
|
|
||||||
|
[_updateTimer invalidate];
|
||||||
|
_updateTimer = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setLiveReloadPeriod:(NSTimeInterval)liveReloadPeriod
|
||||||
|
{
|
||||||
|
_liveReloadPeriod = liveReloadPeriod;
|
||||||
|
if (_liveReloadEnabled) {
|
||||||
|
self.liveReloadEnabled = NO;
|
||||||
|
self.liveReloadEnabled = YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)pollForUpdates
|
||||||
|
{
|
||||||
|
RCTSourceCode *sourceCodeModule = _bridge.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])];
|
||||||
|
if (!sourceCodeModule) {
|
||||||
|
RCTLogError(@"RCTSourceCode module not found");
|
||||||
|
self.liveReloadEnabled = NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSURL *longPollURL = [[NSURL alloc] initWithString:@"/onchange" relativeToURL:sourceCodeModule.scriptURL];
|
||||||
|
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:longPollURL]
|
||||||
|
queue:[[NSOperationQueue alloc] init]
|
||||||
|
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
|
||||||
|
|
||||||
|
NSHTTPURLResponse *HTTPResponse = (NSHTTPURLResponse *)response;
|
||||||
|
if (_liveReloadEnabled && HTTPResponse.statusCode == 205) {
|
||||||
|
[_bridge reload];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isValid
|
||||||
|
{
|
||||||
|
return !_liveReloadEnabled || _updateTimer != nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)invalidate
|
||||||
|
{
|
||||||
|
[_actionSheet dismissWithClickedButtonIndex:_actionSheet.cancelButtonIndex animated:YES];
|
||||||
|
[_updateTimer invalidate];
|
||||||
|
_updateTimer = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation RCTBridge (RCTDevMenu)
|
||||||
|
|
||||||
|
- (RCTDevMenu *)devMenu
|
||||||
|
{
|
||||||
|
return self.modules[RCTBridgeModuleNameForClass([RCTDevMenu class])];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
#import "RCTJavaScriptLoader.h"
|
#import "RCTJavaScriptLoader.h"
|
||||||
|
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ const char *RCTLogLevels[] = {
|
|||||||
static RCTLogFunction RCTCurrentLogFunction;
|
static RCTLogFunction RCTCurrentLogFunction;
|
||||||
static RCTLogLevel RCTCurrentLogThreshold;
|
static RCTLogLevel RCTCurrentLogThreshold;
|
||||||
|
|
||||||
void RCTLogSetup(void) __attribute__((constructor));
|
__attribute__((constructor))
|
||||||
void RCTLogSetup()
|
static void RCTLogSetup()
|
||||||
{
|
{
|
||||||
RCTCurrentLogFunction = RCTDefaultLogFunction;
|
RCTCurrentLogFunction = RCTDefaultLogFunction;
|
||||||
|
|
||||||
|
|||||||
@@ -57,12 +57,6 @@
|
|||||||
*/
|
*/
|
||||||
@property (nonatomic, strong) Class executorClass;
|
@property (nonatomic, strong) Class executorClass;
|
||||||
|
|
||||||
/**
|
|
||||||
* If YES will watch for shake gestures and show development menu
|
|
||||||
* with options like "Reload", "Enable Debugging", etc.
|
|
||||||
*/
|
|
||||||
@property (nonatomic, assign) BOOL enableDevMenu;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The backing view controller of the root view.
|
* The backing view controller of the root view.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
|
|
||||||
#import "RCTBridge.h"
|
#import "RCTBridge.h"
|
||||||
#import "RCTContextExecutor.h"
|
#import "RCTContextExecutor.h"
|
||||||
#import "RCTDevMenu.h"
|
|
||||||
#import "RCTEventDispatcher.h"
|
#import "RCTEventDispatcher.h"
|
||||||
#import "RCTKeyCommands.h"
|
#import "RCTKeyCommands.h"
|
||||||
#import "RCTLog.h"
|
#import "RCTLog.h"
|
||||||
@@ -42,7 +41,6 @@
|
|||||||
|
|
||||||
@implementation RCTRootView
|
@implementation RCTRootView
|
||||||
{
|
{
|
||||||
RCTDevMenu *_devMenu;
|
|
||||||
RCTBridge *_bridge;
|
RCTBridge *_bridge;
|
||||||
RCTTouchHandler *_touchHandler;
|
RCTTouchHandler *_touchHandler;
|
||||||
NSString *_moduleName;
|
NSString *_moduleName;
|
||||||
@@ -60,12 +58,6 @@
|
|||||||
|
|
||||||
self.backgroundColor = [UIColor whiteColor];
|
self.backgroundColor = [UIColor whiteColor];
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
|
|
||||||
_enableDevMenu = YES;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
_bridge = bridge;
|
_bridge = bridge;
|
||||||
_moduleName = moduleName;
|
_moduleName = moduleName;
|
||||||
|
|
||||||
@@ -120,18 +112,6 @@
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
|
|
||||||
{
|
|
||||||
if (motion == UIEventSubtypeMotionShake && self.enableDevMenu) {
|
|
||||||
if (!_devMenu) {
|
|
||||||
_devMenu = [[RCTDevMenu alloc] initWithBridge:_bridge];
|
|
||||||
}
|
|
||||||
[_devMenu show];
|
|
||||||
} else {
|
|
||||||
[super motionEnded:motion withEvent:event];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RCT_IMPORT_METHOD(AppRegistry, runApplication)
|
RCT_IMPORT_METHOD(AppRegistry, runApplication)
|
||||||
RCT_IMPORT_METHOD(ReactIOS, unmountComponentAtNodeAndRemoveContainer)
|
RCT_IMPORT_METHOD(ReactIOS, unmountComponentAtNodeAndRemoveContainer)
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,14 @@ static JSValueRef RCTNativeLoggingHook(JSContextRef context, JSObjectRef object,
|
|||||||
range:(NSRange){0, message.length}
|
range:(NSRange){0, message.length}
|
||||||
withTemplate:@"[$4$5] \t$2"];
|
withTemplate:@"[$4$5] \t$2"];
|
||||||
|
|
||||||
_RCTLogFormat(RCTLogLevelInfo, NULL, -1, @"%@", message);
|
// TODO: it would be good if log level was sent as a param, instead of this hack
|
||||||
|
RCTLogLevel level = RCTLogLevelInfo;
|
||||||
|
if ([message rangeOfString:@"error" options:NSCaseInsensitiveSearch].length) {
|
||||||
|
level = RCTLogLevelError;
|
||||||
|
} else if ([message rangeOfString:@"warning" options:NSCaseInsensitiveSearch].length) {
|
||||||
|
level = RCTLogLevelWarning;
|
||||||
|
}
|
||||||
|
_RCTLogFormat(level, NULL, -1, @"%@", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
return JSValueMakeUndefined(context);
|
return JSValueMakeUndefined(context);
|
||||||
@@ -126,8 +133,6 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
|
|||||||
|
|
||||||
+ (void)runRunLoopThread
|
+ (void)runRunLoopThread
|
||||||
{
|
{
|
||||||
// TODO (#5906496): Investigate exactly what this does and why
|
|
||||||
|
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
// copy thread name to pthread name
|
// copy thread name to pthread name
|
||||||
pthread_setname_np([[[NSThread currentThread] name] UTF8String]);
|
pthread_setname_np([[[NSThread currentThread] name] UTF8String]);
|
||||||
@@ -273,11 +278,11 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)executeApplicationScript:(NSString *)script
|
- (void)executeApplicationScript:(NSString *)script
|
||||||
sourceURL:(NSURL *)url
|
sourceURL:(NSURL *)sourceURL
|
||||||
onComplete:(RCTJavaScriptCompleteBlock)onComplete
|
onComplete:(RCTJavaScriptCompleteBlock)onComplete
|
||||||
{
|
{
|
||||||
RCTAssert(url != nil, @"url should not be nil");
|
RCTAssert(sourceURL != nil, @"url should not be nil");
|
||||||
RCTAssert(onComplete != nil, @"onComplete block should not be nil");
|
|
||||||
__weak RCTContextExecutor *weakSelf = self;
|
__weak RCTContextExecutor *weakSelf = self;
|
||||||
[self executeBlockOnJavaScriptQueue:^{
|
[self executeBlockOnJavaScriptQueue:^{
|
||||||
RCTContextExecutor *strongSelf = weakSelf;
|
RCTContextExecutor *strongSelf = weakSelf;
|
||||||
@@ -286,17 +291,18 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
|
|||||||
}
|
}
|
||||||
JSValueRef jsError = NULL;
|
JSValueRef jsError = NULL;
|
||||||
JSStringRef execJSString = JSStringCreateWithCFString((__bridge CFStringRef)script);
|
JSStringRef execJSString = JSStringCreateWithCFString((__bridge CFStringRef)script);
|
||||||
JSStringRef sourceURL = JSStringCreateWithCFString((__bridge CFStringRef)url.absoluteString);
|
JSStringRef jsURL = JSStringCreateWithCFString((__bridge CFStringRef)sourceURL.absoluteString);
|
||||||
JSValueRef result = JSEvaluateScript(strongSelf->_context.ctx, execJSString, NULL, sourceURL, 0, &jsError);
|
JSValueRef result = JSEvaluateScript(strongSelf->_context.ctx, execJSString, NULL, jsURL, 0, &jsError);
|
||||||
JSStringRelease(sourceURL);
|
JSStringRelease(jsURL);
|
||||||
JSStringRelease(execJSString);
|
JSStringRelease(execJSString);
|
||||||
|
|
||||||
NSError *error;
|
if (onComplete) {
|
||||||
if (!result) {
|
NSError *error;
|
||||||
error = RCTNSErrorFromJSError(strongSelf->_context.ctx, jsError);
|
if (!result) {
|
||||||
|
error = RCTNSErrorFromJSError(strongSelf->_context.ctx, jsError);
|
||||||
|
}
|
||||||
|
onComplete(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
onComplete(error);
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,7 +320,7 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
|
|||||||
asGlobalObjectNamed:(NSString *)objectName
|
asGlobalObjectNamed:(NSString *)objectName
|
||||||
callback:(RCTJavaScriptCompleteBlock)onComplete
|
callback:(RCTJavaScriptCompleteBlock)onComplete
|
||||||
{
|
{
|
||||||
RCTAssert(onComplete != nil, @"onComplete block should not be nil");
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
RCTAssert(RCTJSONParse(script, NULL) != nil, @"%@ wasn't valid JSON!", script);
|
RCTAssert(RCTJSONParse(script, NULL) != nil, @"%@ wasn't valid JSON!", script);
|
||||||
#endif
|
#endif
|
||||||
@@ -333,19 +339,21 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
|
|||||||
NSString *errorDesc = [NSString stringWithFormat:@"Can't make JSON value from script '%@'", script];
|
NSString *errorDesc = [NSString stringWithFormat:@"Can't make JSON value from script '%@'", script];
|
||||||
RCTLogError(@"%@", errorDesc);
|
RCTLogError(@"%@", errorDesc);
|
||||||
|
|
||||||
NSError *error = [NSError errorWithDomain:@"JS" code:2 userInfo:@{NSLocalizedDescriptionKey: errorDesc}];
|
if (onComplete) {
|
||||||
onComplete(error);
|
NSError *error = [NSError errorWithDomain:@"JS" code:2 userInfo:@{NSLocalizedDescriptionKey: errorDesc}];
|
||||||
|
onComplete(error);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSObjectRef globalObject = JSContextGetGlobalObject(strongSelf->_context.ctx);
|
JSObjectRef globalObject = JSContextGetGlobalObject(strongSelf->_context.ctx);
|
||||||
|
|
||||||
JSStringRef JSName = JSStringCreateWithCFString((__bridge CFStringRef)objectName);
|
JSStringRef JSName = JSStringCreateWithCFString((__bridge CFStringRef)objectName);
|
||||||
JSObjectSetProperty(strongSelf->_context.ctx, globalObject, JSName, valueToInject, kJSPropertyAttributeNone, NULL);
|
JSObjectSetProperty(strongSelf->_context.ctx, globalObject, JSName, valueToInject, kJSPropertyAttributeNone, NULL);
|
||||||
JSStringRelease(JSName);
|
JSStringRelease(JSName);
|
||||||
onComplete(nil);
|
if (onComplete) {
|
||||||
|
onComplete(nil);
|
||||||
|
}
|
||||||
}];
|
}];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -19,10 +19,6 @@
|
|||||||
NSUInteger _reloadRetries;
|
NSUInteger _reloadRetries;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef DEBUG
|
|
||||||
static NSUInteger RCTReloadRetries = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
RCT_EXPORT_MODULE()
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
- (instancetype)initWithDelegate:(id<RCTExceptionsManagerDelegate>)delegate
|
- (instancetype)initWithDelegate:(id<RCTExceptionsManagerDelegate>)delegate
|
||||||
@@ -47,27 +43,32 @@ RCT_EXPORT_METHOD(reportUnhandledException:(NSString *)message
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
#if DEBUG
|
||||||
|
|
||||||
[[RCTRedBox sharedInstance] showErrorMessage:message withStack:stack];
|
[[RCTRedBox sharedInstance] showErrorMessage:message withStack:stack];
|
||||||
|
|
||||||
#else
|
#else
|
||||||
if (RCTReloadRetries < _maxReloadAttempts) {
|
|
||||||
RCTReloadRetries++;
|
static NSUInteger reloadRetries = 0;
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification object:nil];
|
const NSUInteger maxMessageLength = 75;
|
||||||
|
|
||||||
|
if (reloadRetries < _maxReloadAttempts) {
|
||||||
|
|
||||||
|
reloadRetries++;
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification
|
||||||
|
object:nil];
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
NSError *error;
|
|
||||||
const NSUInteger MAX_SANITIZED_LENGTH = 75;
|
|
||||||
// Filter out numbers so the same base errors are mapped to the same categories independent of incorrect values.
|
// Filter out numbers so the same base errors are mapped to the same categories independent of incorrect values.
|
||||||
NSString *pattern = @"[+-]?\\d+[,.]?\\d*";
|
NSString *pattern = @"[+-]?\\d+[,.]?\\d*";
|
||||||
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&error];
|
NSString *sanitizedMessage = [message stringByReplacingOccurrencesOfString:pattern withString:@"<num>" options:NSRegularExpressionSearch range:(NSRange){0, message.length}];
|
||||||
RCTAssert(error == nil, @"Bad regex pattern: %@", pattern);
|
|
||||||
NSString *sanitizedMessage = [regex stringByReplacingMatchesInString:message
|
if (sanitizedMessage.length > maxMessageLength) {
|
||||||
options:0
|
sanitizedMessage = [[sanitizedMessage substringToIndex:maxMessageLength] stringByAppendingString:@"..."];
|
||||||
range:NSMakeRange(0, message.length)
|
|
||||||
withTemplate:@"<num>"];
|
|
||||||
if (sanitizedMessage.length > MAX_SANITIZED_LENGTH) {
|
|
||||||
sanitizedMessage = [[sanitizedMessage substringToIndex:MAX_SANITIZED_LENGTH] stringByAppendingString:@"..."];
|
|
||||||
}
|
}
|
||||||
NSMutableString *prettyStack = [@"\n" mutableCopy];
|
|
||||||
|
NSMutableString *prettyStack = [NSMutableString stringWithString:@"\n"];
|
||||||
for (NSDictionary *frame in stack) {
|
for (NSDictionary *frame in stack) {
|
||||||
[prettyStack appendFormat:@"%@@%@:%@\n", frame[@"methodName"], frame[@"lineNumber"], frame[@"column"]];
|
[prettyStack appendFormat:@"%@@%@:%@\n", frame[@"methodName"], frame[@"lineNumber"], frame[@"column"]];
|
||||||
}
|
}
|
||||||
@@ -75,13 +76,21 @@ RCT_EXPORT_METHOD(reportUnhandledException:(NSString *)message
|
|||||||
NSString *name = [@"Unhandled JS Exception: " stringByAppendingString:sanitizedMessage];
|
NSString *name = [@"Unhandled JS Exception: " stringByAppendingString:sanitizedMessage];
|
||||||
[NSException raise:name format:@"Message: %@, stack: %@", message, prettyStack];
|
[NSException raise:name format:@"Message: %@, stack: %@", message, prettyStack];
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RCT_EXPORT_METHOD(updateExceptionMessage:(NSString *)message
|
RCT_EXPORT_METHOD(updateExceptionMessage:(NSString *)message
|
||||||
stack:(NSArray *)stack)
|
stack:(NSArray *)stack)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
|
||||||
[[RCTRedBox sharedInstance] updateErrorMessage:message withStack:stack];
|
[[RCTRedBox sharedInstance] updateErrorMessage:message withStack:stack];
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -999,7 +999,7 @@ RCT_EXPORT_METHOD(measureLayoutRelativeToParent:(NSNumber *)reactTag
|
|||||||
* Only layouts for views that are within the rect passed in are returned. Invokes the error callback if the
|
* Only layouts for views that are within the rect passed in are returned. Invokes the error callback if the
|
||||||
* passed in parent view does not exist. Invokes the supplied callback with the array of computed layouts.
|
* passed in parent view does not exist. Invokes the supplied callback with the array of computed layouts.
|
||||||
*/
|
*/
|
||||||
RCT_EXPORT_METHOD(measureViewsInRect:(NSDictionary *)rect
|
RCT_EXPORT_METHOD(measureViewsInRect:(CGRect)rect
|
||||||
parentView:(NSNumber *)reactTag
|
parentView:(NSNumber *)reactTag
|
||||||
errorCallback:(RCTResponseSenderBlock)errorCallback
|
errorCallback:(RCTResponseSenderBlock)errorCallback
|
||||||
callback:(RCTResponseSenderBlock)callback)
|
callback:(RCTResponseSenderBlock)callback)
|
||||||
@@ -1011,7 +1011,7 @@ RCT_EXPORT_METHOD(measureViewsInRect:(NSDictionary *)rect
|
|||||||
}
|
}
|
||||||
NSArray *childShadowViews = [shadowView reactSubviews];
|
NSArray *childShadowViews = [shadowView reactSubviews];
|
||||||
NSMutableArray *results = [[NSMutableArray alloc] initWithCapacity:[childShadowViews count]];
|
NSMutableArray *results = [[NSMutableArray alloc] initWithCapacity:[childShadowViews count]];
|
||||||
CGRect layoutRect = [RCTConvert CGRect:rect];
|
|
||||||
|
|
||||||
[childShadowViews enumerateObjectsUsingBlock:^(RCTShadowView *childShadowView, NSUInteger idx, BOOL *stop) {
|
[childShadowViews enumerateObjectsUsingBlock:^(RCTShadowView *childShadowView, NSUInteger idx, BOOL *stop) {
|
||||||
CGRect childLayout = [childShadowView measureLayoutRelativeToAncestor:shadowView];
|
CGRect childLayout = [childShadowView measureLayoutRelativeToAncestor:shadowView];
|
||||||
@@ -1026,10 +1026,11 @@ RCT_EXPORT_METHOD(measureViewsInRect:(NSDictionary *)rect
|
|||||||
CGFloat width = childLayout.size.width;
|
CGFloat width = childLayout.size.width;
|
||||||
CGFloat height = childLayout.size.height;
|
CGFloat height = childLayout.size.height;
|
||||||
|
|
||||||
if (leftOffset <= layoutRect.origin.x + layoutRect.size.width &&
|
if (leftOffset <= rect.origin.x + rect.size.width &&
|
||||||
leftOffset + width >= layoutRect.origin.x &&
|
leftOffset + width >= rect.origin.x &&
|
||||||
topOffset <= layoutRect.origin.y + layoutRect.size.height &&
|
topOffset <= rect.origin.y + rect.size.height &&
|
||||||
topOffset + height >= layoutRect.origin.y) {
|
topOffset + height >= rect.origin.y) {
|
||||||
|
|
||||||
// This view is within the layout rect
|
// This view is within the layout rect
|
||||||
NSDictionary *result = @{@"index": @(idx),
|
NSDictionary *result = @{@"index": @(idx),
|
||||||
@"left": @(leftOffset),
|
@"left": @(leftOffset),
|
||||||
|
|||||||
Reference in New Issue
Block a user