123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- /*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
- #import "RCTEventDispatcher.h"
- #import "RCTAssert.h"
- #import "RCTBridge+Private.h"
- #import "RCTBridge.h"
- #import "RCTComponentEvent.h"
- #import "RCTProfile.h"
- #import "RCTUtils.h"
- const NSInteger RCTTextUpdateLagWarningThreshold = 3;
- NSString *RCTNormalizeInputEventName(NSString *eventName)
- {
- if ([eventName hasPrefix:@"on"]) {
- eventName = [eventName stringByReplacingCharactersInRange:(NSRange){0, 2} withString:@"top"];
- } else if (![eventName hasPrefix:@"top"]) {
- eventName = [[@"top" stringByAppendingString:[eventName substringToIndex:1].uppercaseString]
- stringByAppendingString:[eventName substringFromIndex:1]];
- }
- return eventName;
- }
- static NSNumber *RCTGetEventID(NSNumber *viewTag, NSString *eventName, uint16_t coalescingKey)
- {
- return @(viewTag.intValue | (((uint64_t)eventName.hash & 0xFFFF) << 32) | (((uint64_t)coalescingKey) << 48));
- }
- static uint16_t RCTUniqueCoalescingKeyGenerator = 0;
- @implementation RCTEventDispatcher {
- // We need this lock to protect access to _events, _eventQueue and _eventsDispatchScheduled. It's filled in on main
- // thread and consumed on js thread.
- NSLock *_eventQueueLock;
- // We have this id -> event mapping so we coalesce effectively.
- NSMutableDictionary<NSNumber *, id<RCTEvent>> *_events;
- // This array contains ids of events in order they come in, so we can emit them to JS in the exact same order.
- NSMutableArray<NSNumber *> *_eventQueue;
- BOOL _eventsDispatchScheduled;
- NSHashTable<id<RCTEventDispatcherObserver>> *_observers;
- NSLock *_observersLock;
- }
- @synthesize bridge = _bridge;
- RCT_EXPORT_MODULE()
- - (void)setBridge:(RCTBridge *)bridge
- {
- _bridge = bridge;
- _events = [NSMutableDictionary new];
- _eventQueue = [NSMutableArray new];
- _eventQueueLock = [NSLock new];
- _eventsDispatchScheduled = NO;
- _observers = [NSHashTable weakObjectsHashTable];
- _observersLock = [NSLock new];
- }
- - (void)sendAppEventWithName:(NSString *)name body:(id)body
- {
- [_bridge enqueueJSCall:@"RCTNativeAppEventEmitter"
- method:@"emit"
- args:body ? @[ name, body ] : @[ name ]
- completion:NULL];
- }
- - (void)sendDeviceEventWithName:(NSString *)name body:(id)body
- {
- [_bridge enqueueJSCall:@"RCTDeviceEventEmitter"
- method:@"emit"
- args:body ? @[ name, body ] : @[ name ]
- completion:NULL];
- }
- - (void)sendTextEventWithType:(RCTTextEventType)type
- reactTag:(NSNumber *)reactTag
- text:(NSString *)text
- key:(NSString *)key
- eventCount:(NSInteger)eventCount
- {
- static NSString *events[] = {@"focus", @"blur", @"change", @"submitEditing", @"endEditing", @"keyPress"};
- NSMutableDictionary *body = [[NSMutableDictionary alloc] initWithDictionary:@{
- @"eventCount" : @(eventCount),
- }];
- if (text) {
- body[@"text"] = text;
- }
- if (key) {
- if (key.length == 0) {
- key = @"Backspace"; // backspace
- } else {
- switch ([key characterAtIndex:0]) {
- case '\t':
- key = @"Tab";
- break;
- case '\n':
- key = @"Enter";
- default:
- break;
- }
- }
- body[@"key"] = key;
- }
- RCTComponentEvent *event = [[RCTComponentEvent alloc] initWithName:events[type] viewTag:reactTag body:body];
- [self sendEvent:event];
- }
- - (void)sendEvent:(id<RCTEvent>)event
- {
- [_observersLock lock];
- for (id<RCTEventDispatcherObserver> observer in _observers) {
- [observer eventDispatcherWillDispatchEvent:event];
- }
- [_observersLock unlock];
- [_eventQueueLock lock];
- NSNumber *eventID;
- if (event.canCoalesce) {
- eventID = RCTGetEventID(event.viewTag, event.eventName, event.coalescingKey);
- id<RCTEvent> previousEvent = _events[eventID];
- if (previousEvent) {
- event = [previousEvent coalesceWithEvent:event];
- } else {
- [_eventQueue addObject:eventID];
- }
- } else {
- id<RCTEvent> previousEvent = _events[eventID];
- eventID = RCTGetEventID(event.viewTag, event.eventName, RCTUniqueCoalescingKeyGenerator++);
- RCTAssert(
- previousEvent == nil,
- @"Got event %@ which cannot be coalesced, but has the same eventID %@ as the previous event %@",
- event,
- eventID,
- previousEvent);
- [_eventQueue addObject:eventID];
- }
- _events[eventID] = event;
- BOOL scheduleEventsDispatch = NO;
- if (!_eventsDispatchScheduled) {
- _eventsDispatchScheduled = YES;
- scheduleEventsDispatch = YES;
- }
- // We have to release the lock before dispatching block with events,
- // since dispatchBlock: can be executed synchronously on the same queue.
- // (This is happening when chrome debugging is turned on.)
- [_eventQueueLock unlock];
- if (scheduleEventsDispatch) {
- [_bridge
- dispatchBlock:^{
- [self flushEventsQueue];
- }
- queue:RCTJSThread];
- }
- }
- - (void)addDispatchObserver:(id<RCTEventDispatcherObserver>)observer
- {
- [_observersLock lock];
- [_observers addObject:observer];
- [_observersLock unlock];
- }
- - (void)removeDispatchObserver:(id<RCTEventDispatcherObserver>)observer
- {
- [_observersLock lock];
- [_observers removeObject:observer];
- [_observersLock unlock];
- }
- - (void)dispatchEvent:(id<RCTEvent>)event
- {
- [_bridge enqueueJSCall:[[event class] moduleDotMethod] args:[event arguments]];
- }
- - (dispatch_queue_t)methodQueue
- {
- return RCTJSThread;
- }
- // js thread only (which surprisingly can be the main thread, depends on used JS executor)
- - (void)flushEventsQueue
- {
- [_eventQueueLock lock];
- NSDictionary *events = _events;
- _events = [NSMutableDictionary new];
- NSMutableArray *eventQueue = _eventQueue;
- _eventQueue = [NSMutableArray new];
- _eventsDispatchScheduled = NO;
- [_eventQueueLock unlock];
- for (NSNumber *eventId in eventQueue) {
- [self dispatchEvent:events[eventId]];
- }
- }
- @end
- @implementation RCTBridge (RCTEventDispatcher)
- - (RCTEventDispatcher *)eventDispatcher
- {
- return [self moduleForClass:[RCTEventDispatcher class]];
- }
- @end
|