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

0.49 stable #15975

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
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
5 changes: 4 additions & 1 deletion Libraries/CameraRoll/RCTPhotoLibraryImageLoader.m
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ - (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
// form of an, NSURL which is what assets-library uses.
NSString *assetID = @"";
PHFetchResult *results;
if ([imageURL.scheme caseInsensitiveCompare:@"assets-library"] == NSOrderedSame) {
if (!imageURL) {
completionHandler(RCTErrorWithMessage(@"Cannot load a photo library asset with no URL"), nil);
return ^{};
} else if ([imageURL.scheme caseInsensitiveCompare:@"assets-library"] == NSOrderedSame) {
assetID = [imageURL absoluteString];
results = [PHAsset fetchAssetsWithALAssetURLs:@[imageURL] options:nil];
} else {
Expand Down
4 changes: 4 additions & 0 deletions React/Base/RCTBatchedBridge.mm
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,10 @@ - (void)handleBuffer:(id)buffer batchEnded:(BOOL)batchEnded
{
RCTAssertJSThread();

if (!self.valid) {
return;
}

if (buffer != nil && buffer != (id)kCFNull) {
_wasBatchActive = YES;
[self handleBuffer:buffer];
Expand Down
2 changes: 2 additions & 0 deletions React/Base/RCTModuleData.mm
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,8 @@ - (NSArray *)config
- (dispatch_queue_t)methodQueue
{
(void)[self instance];
RCTAssert(_methodQueue != nullptr, @"Module %@ has no methodQueue (instance: %@, bridge.valid: %d)",
self, _instance, _bridge.valid);
return _methodQueue;
}

Expand Down
2 changes: 1 addition & 1 deletion React/Base/RCTMultipartStreamReader.m
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ - (void)emitProgress:(NSDictionary *)headers
// Throttle progress events so we don't send more that around 60 per second.
CFTimeInterval currentTime = CACurrentMediaTime();

NSUInteger headersContentLength = headers[@"Content-Length"] != nil ? [headers[@"Content-Length"] unsignedIntValue] : 0;
NSInteger headersContentLength = headers[@"Content-Length"] != nil ? [headers[@"Content-Length"] integerValue] : 0;
if (callback && (currentTime - _lastDownloadProgress > 0.016 || final)) {
_lastDownloadProgress = currentTime;
callback(headers, @(headersContentLength), @(contentLength));
Expand Down
120 changes: 62 additions & 58 deletions React/CxxBridge/RCTCxxBridge.mm
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,12 @@ void onBatchComplete() override {
[bridge_ partialBatchDidFlush];
[bridge_ batchDidComplete];
}
void incrementPendingJSCalls() override {}
void decrementPendingJSCalls() override {}
};

@implementation RCTCxxBridge
{
BOOL _wasBatchActive;
BOOL _didInvalidate;

NSMutableArray<dispatch_block_t> *_pendingCalls;
std::atomic<NSInteger> _pendingCount;
Expand Down Expand Up @@ -169,7 +168,7 @@ - (JSContext *)jsContext

- (JSGlobalContextRef)jsContextRef
{
return (JSGlobalContextRef)self->_reactInstance->getJavaScriptContext();
return (JSGlobalContextRef)(self->_reactInstance ? self->_reactInstance->getJavaScriptContext() : nullptr);
}

- (instancetype)initWithParentBridge:(RCTBridge *)bridge
Expand Down Expand Up @@ -204,7 +203,7 @@ - (instancetype)initWithParentBridge:(RCTBridge *)bridge
return self;
}

- (void)runJSRunLoop
+ (void)runRunLoop
{
@autoreleasepool {
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTCxxBridge runJSRunLoop] setup", nil);
Expand Down Expand Up @@ -267,8 +266,8 @@ - (void)start
object:_parentBridge userInfo:@{@"bridge": self}];

// Set up the JS thread early
_jsThread = [[NSThread alloc] initWithTarget:self
selector:@selector(runJSRunLoop)
_jsThread = [[NSThread alloc] initWithTarget:[self class]
selector:@selector(runRunLoop)
object:nil];
_jsThread.name = RCTJSThreadName;
_jsThread.qualityOfService = NSOperationQualityOfServiceUserInteractive;
Expand Down Expand Up @@ -493,7 +492,7 @@ - (void)_initializeBridge:(std::shared_ptr<JSExecutorFactory>)executorFactory
if (_reactInstance) {
// This is async, but any calls into JS are blocked by the m_syncReady CV in Instance
_reactInstance->initializeBridge(
std::unique_ptr<RCTInstanceCallback>(new RCTInstanceCallback(self)),
std::make_unique<RCTInstanceCallback>(self),
executorFactory,
_jsMessageThread,
[self _buildModuleRegistry]);
Expand Down Expand Up @@ -816,6 +815,7 @@ - (void)handleError:(NSError *)error
}

RCTFatal(error);

// RN will stop, but let the rest of the app keep going.
return;
}
Expand All @@ -826,27 +826,27 @@ - (void)handleError:(NSError *)error

// Hack: once the bridge is invalidated below, it won't initialize any new native
// modules. Initialize the redbox module now so we can still report this error.
[self redBox];
RCTRedBox *redBox = [self redBox];

_loading = NO;
_valid = NO;

dispatch_async(dispatch_get_main_queue(), ^{
if (self->_jsMessageThread) {
auto thread = self->_jsMessageThread;
self->_jsMessageThread->runOnQueue([thread] {
thread->quitSynchronous();
});
self->_jsMessageThread.reset();
// Make sure initializeBridge completed
self->_jsMessageThread->runOnQueueSync([] {});
}

self->_reactInstance.reset();
self->_jsMessageThread.reset();

[[NSNotificationCenter defaultCenter]
postNotificationName:RCTJavaScriptDidFailToLoadNotification
object:self->_parentBridge userInfo:@{@"bridge": self, @"error": error}];

if ([error userInfo][RCTJSRawStackTraceKey]) {
[self.redBox showErrorMessage:[error localizedDescription]
withRawStack:[error userInfo][RCTJSRawStackTraceKey]];
[redBox showErrorMessage:[error localizedDescription]
withRawStack:[error userInfo][RCTJSRawStackTraceKey]];
}

RCTFatal(error);
Expand Down Expand Up @@ -913,63 +913,68 @@ - (void)dispatchBlock:(dispatch_block_t)block

- (void)invalidate
{
if (!_valid) {
if (_didInvalidate) {
return;
}

RCTAssertMainQueue();
RCTAssert(_reactInstance != nil, @"Can't complete invalidation without a react instance");
RCTLogInfo(@"Invalidating %@ (parent: %@, executor: %@)", self, _parentBridge, [self executorClass]);

_loading = NO;
_valid = NO;
_didInvalidate = YES;

if ([RCTBridge currentBridge] == self) {
[RCTBridge setCurrentBridge:nil];
}

// Invalidate modules
dispatch_group_t group = dispatch_group_create();
for (RCTModuleData *moduleData in _moduleDataByID) {
// Be careful when grabbing an instance here, we don't want to instantiate
// any modules just to invalidate them.
if (![moduleData hasInstance]) {
continue;
}
// Stop JS instance and message thread
[self ensureOnJavaScriptThread:^{
[self->_displayLink invalidate];
self->_displayLink = nil;

if ([moduleData.instance respondsToSelector:@selector(invalidate)]) {
dispatch_group_enter(group);
[self dispatchBlock:^{
[(id<RCTInvalidating>)moduleData.instance invalidate];
dispatch_group_leave(group);
} queue:moduleData.methodQueue];
if (RCTProfileIsProfiling()) {
RCTProfileUnhookModules(self);
}
[moduleData invalidate];
}

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
[self ensureOnJavaScriptThread:^{
[self->_displayLink invalidate];
self->_displayLink = nil;

self->_reactInstance.reset();
if (self->_jsMessageThread) {
self->_jsMessageThread->quitSynchronous();
self->_jsMessageThread.reset();
// Invalidate modules
// We're on the JS thread (which we'll be suspending soon), so no new calls will be made to native modules after
// this completes. We must ensure all previous calls were dispatched before deallocating the instance (and module
// wrappers) or we may have invalid pointers still in flight.
dispatch_group_t moduleInvalidation = dispatch_group_create();
for (RCTModuleData *moduleData in self->_moduleDataByID) {
// Be careful when grabbing an instance here, we don't want to instantiate
// any modules just to invalidate them.
if (![moduleData hasInstance]) {
continue;
}

if (RCTProfileIsProfiling()) {
RCTProfileUnhookModules(self);
if ([moduleData.instance respondsToSelector:@selector(invalidate)]) {
dispatch_group_enter(moduleInvalidation);
[self dispatchBlock:^{
[(id<RCTInvalidating>)moduleData.instance invalidate];
dispatch_group_leave(moduleInvalidation);
} queue:moduleData.methodQueue];
}
[moduleData invalidate];
}

self->_moduleDataByName = nil;
self->_moduleDataByID = nil;
self->_moduleClassesByID = nil;
self->_pendingCalls = nil;
if (dispatch_group_wait(moduleInvalidation, dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC))) {
RCTLogError(@"Timed out waiting for modules to be invalidated");
}

[self->_jsThread cancel];
self->_jsThread = nil;
CFRunLoopStop(CFRunLoopGetCurrent());
}];
});
self->_reactInstance.reset();
self->_jsMessageThread.reset();

self->_moduleDataByName = nil;
self->_moduleDataByID = nil;
self->_moduleClassesByID = nil;
self->_pendingCalls = nil;

[self->_jsThread cancel];
self->_jsThread = nil;
CFRunLoopStop(CFRunLoopGetCurrent());
}];
}

- (void)logMessage:(NSString *)message level:(NSString *)level
Expand Down Expand Up @@ -1098,7 +1103,6 @@ - (void)enqueueCallback:(NSNumber *)cbID args:(NSArray *)args
*/

RCTProfileBeginFlowEvent();

[self _runAfterLoad:^{
RCTProfileEndFlowEvent();

Expand Down Expand Up @@ -1189,25 +1193,25 @@ - (JSValue *)callFunctionOnModule:(NSString *)module
if (!_reactInstance) {
if (error) {
*error = RCTErrorWithMessage(
@"Attempt to call sync callFunctionOnModule: on uninitialized bridge");
@"callFunctionOnModule was called on uninitialized bridge");
}
return nil;
} else if (self.executorClass) {
if (error) {
*error = RCTErrorWithMessage(
@"sync callFunctionOnModule: can only be used with JSC executor");
@"callFunctionOnModule can only be used with JSC executor");
}
return nil;
} else if (!self.valid) {
if (error) {
*error = RCTErrorWithMessage(
@"sync callFunctionOnModule: bridge is no longer valid");
@"Bridge is no longer valid");
}
return nil;
} else if (self.loading) {
if (error) {
*error = RCTErrorWithMessage(
@"sync callFunctionOnModule: bridge is still loading");
@"Bridge is still loading");
}
return nil;
}
Expand Down
1 change: 0 additions & 1 deletion React/CxxModule/DispatchMessageQueueThread.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ class DispatchMessageQueueThread : public MessageQueueThread {

void runOnQueue(std::function<void()>&& func) override {
dispatch_queue_t queue = moduleData_.methodQueue;
RCTAssert(queue != nullptr, @"Module %@ provided invalid queue", moduleData_);
dispatch_block_t block = [func=std::move(func)] { func(); };
RCTAssert(block != nullptr, @"Invalid block generated in call to %@", moduleData_);
if (queue && block) {
Expand Down
51 changes: 32 additions & 19 deletions React/Views/RCTFont.mm
Original file line number Diff line number Diff line change
Expand Up @@ -36,33 +36,46 @@
typedef CGFloat RCTFontWeight;
static RCTFontWeight weightOfFont(UIFont *font)
{
static NSDictionary *nameToWeight;
static NSArray *fontNames;
static NSArray *fontWeights;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
nameToWeight = @{
@"normal": @(UIFontWeightRegular),
@"bold": @(UIFontWeightBold),
@"ultralight": @(UIFontWeightUltraLight),
@"thin": @(UIFontWeightThin),
@"light": @(UIFontWeightLight),
@"regular": @(UIFontWeightRegular),
@"medium": @(UIFontWeightMedium),
@"semibold": @(UIFontWeightSemibold),
@"bold": @(UIFontWeightBold),
@"heavy": @(UIFontWeightHeavy),
@"black": @(UIFontWeightBlack),
};
// We use two arrays instead of one map because
// the order is important for suffix matching.
fontNames = @[
@"normal",
@"ultralight",
@"thin",
@"light",
@"regular",
@"medium",
@"semibold",
@"bold",
@"heavy",
@"black"
];
fontWeights = @[
@(UIFontWeightRegular),
@(UIFontWeightUltraLight),
@(UIFontWeightThin),
@(UIFontWeightLight),
@(UIFontWeightRegular),
@(UIFontWeightMedium),
@(UIFontWeightSemibold),
@(UIFontWeightBold),
@(UIFontWeightHeavy),
@(UIFontWeightBlack)
];
});

for (NSString *name in nameToWeight) {
if ([font.fontName.lowercaseString hasSuffix:name]) {
return [nameToWeight[name] doubleValue];
for (NSInteger i = 0; i < fontNames.count; i++) {
if ([font.fontName.lowercaseString hasSuffix:fontNames[i]]) {
return (RCTFontWeight)[fontWeights[i] doubleValue];
}
}

NSDictionary *traits = [font.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute];
RCTFontWeight weight = [traits[UIFontWeightTrait] doubleValue];
return weight;
return (RCTFontWeight)[traits[UIFontWeightTrait] doubleValue];
}

static BOOL isItalicFont(UIFont *font)
Expand Down
2 changes: 1 addition & 1 deletion ReactAndroid/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION_NAME=1000.0.0-master
VERSION_NAME=0.49.0-rc.4
GROUP=com.facebook.react

POM_NAME=ReactNative
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public void setFpsDebugViewVisible(boolean fpsDebugViewVisible) {
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowOverlayCompat.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
PixelFormat.TRANSLUCENT);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ private void setVisible(boolean visible) {
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowOverlayCompat.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.TOP;
Expand Down
Loading