RCTEventDispatcher.m 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #import "RCTEventDispatcher.h"
  8. #import "RCTAssert.h"
  9. #import "RCTBridge+Private.h"
  10. #import "RCTBridge.h"
  11. #import "RCTComponentEvent.h"
  12. #import "RCTProfile.h"
  13. #import "RCTUtils.h"
  14. const NSInteger RCTTextUpdateLagWarningThreshold = 3;
  15. NSString *RCTNormalizeInputEventName(NSString *eventName)
  16. {
  17. if ([eventName hasPrefix:@"on"]) {
  18. eventName = [eventName stringByReplacingCharactersInRange:(NSRange){0, 2} withString:@"top"];
  19. } else if (![eventName hasPrefix:@"top"]) {
  20. eventName = [[@"top" stringByAppendingString:[eventName substringToIndex:1].uppercaseString]
  21. stringByAppendingString:[eventName substringFromIndex:1]];
  22. }
  23. return eventName;
  24. }
  25. static NSNumber *RCTGetEventID(NSNumber *viewTag, NSString *eventName, uint16_t coalescingKey)
  26. {
  27. return @(viewTag.intValue | (((uint64_t)eventName.hash & 0xFFFF) << 32) | (((uint64_t)coalescingKey) << 48));
  28. }
  29. static uint16_t RCTUniqueCoalescingKeyGenerator = 0;
  30. @implementation RCTEventDispatcher {
  31. // We need this lock to protect access to _events, _eventQueue and _eventsDispatchScheduled. It's filled in on main
  32. // thread and consumed on js thread.
  33. NSLock *_eventQueueLock;
  34. // We have this id -> event mapping so we coalesce effectively.
  35. NSMutableDictionary<NSNumber *, id<RCTEvent>> *_events;
  36. // This array contains ids of events in order they come in, so we can emit them to JS in the exact same order.
  37. NSMutableArray<NSNumber *> *_eventQueue;
  38. BOOL _eventsDispatchScheduled;
  39. NSHashTable<id<RCTEventDispatcherObserver>> *_observers;
  40. NSLock *_observersLock;
  41. }
  42. @synthesize bridge = _bridge;
  43. RCT_EXPORT_MODULE()
  44. - (void)setBridge:(RCTBridge *)bridge
  45. {
  46. _bridge = bridge;
  47. _events = [NSMutableDictionary new];
  48. _eventQueue = [NSMutableArray new];
  49. _eventQueueLock = [NSLock new];
  50. _eventsDispatchScheduled = NO;
  51. _observers = [NSHashTable weakObjectsHashTable];
  52. _observersLock = [NSLock new];
  53. }
  54. - (void)sendAppEventWithName:(NSString *)name body:(id)body
  55. {
  56. [_bridge enqueueJSCall:@"RCTNativeAppEventEmitter"
  57. method:@"emit"
  58. args:body ? @[ name, body ] : @[ name ]
  59. completion:NULL];
  60. }
  61. - (void)sendDeviceEventWithName:(NSString *)name body:(id)body
  62. {
  63. [_bridge enqueueJSCall:@"RCTDeviceEventEmitter"
  64. method:@"emit"
  65. args:body ? @[ name, body ] : @[ name ]
  66. completion:NULL];
  67. }
  68. - (void)sendTextEventWithType:(RCTTextEventType)type
  69. reactTag:(NSNumber *)reactTag
  70. text:(NSString *)text
  71. key:(NSString *)key
  72. eventCount:(NSInteger)eventCount
  73. {
  74. static NSString *events[] = {@"focus", @"blur", @"change", @"submitEditing", @"endEditing", @"keyPress"};
  75. NSMutableDictionary *body = [[NSMutableDictionary alloc] initWithDictionary:@{
  76. @"eventCount" : @(eventCount),
  77. }];
  78. if (text) {
  79. body[@"text"] = text;
  80. }
  81. if (key) {
  82. if (key.length == 0) {
  83. key = @"Backspace"; // backspace
  84. } else {
  85. switch ([key characterAtIndex:0]) {
  86. case '\t':
  87. key = @"Tab";
  88. break;
  89. case '\n':
  90. key = @"Enter";
  91. default:
  92. break;
  93. }
  94. }
  95. body[@"key"] = key;
  96. }
  97. RCTComponentEvent *event = [[RCTComponentEvent alloc] initWithName:events[type] viewTag:reactTag body:body];
  98. [self sendEvent:event];
  99. }
  100. - (void)sendEvent:(id<RCTEvent>)event
  101. {
  102. [_observersLock lock];
  103. for (id<RCTEventDispatcherObserver> observer in _observers) {
  104. [observer eventDispatcherWillDispatchEvent:event];
  105. }
  106. [_observersLock unlock];
  107. [_eventQueueLock lock];
  108. NSNumber *eventID;
  109. if (event.canCoalesce) {
  110. eventID = RCTGetEventID(event.viewTag, event.eventName, event.coalescingKey);
  111. id<RCTEvent> previousEvent = _events[eventID];
  112. if (previousEvent) {
  113. event = [previousEvent coalesceWithEvent:event];
  114. } else {
  115. [_eventQueue addObject:eventID];
  116. }
  117. } else {
  118. id<RCTEvent> previousEvent = _events[eventID];
  119. eventID = RCTGetEventID(event.viewTag, event.eventName, RCTUniqueCoalescingKeyGenerator++);
  120. RCTAssert(
  121. previousEvent == nil,
  122. @"Got event %@ which cannot be coalesced, but has the same eventID %@ as the previous event %@",
  123. event,
  124. eventID,
  125. previousEvent);
  126. [_eventQueue addObject:eventID];
  127. }
  128. _events[eventID] = event;
  129. BOOL scheduleEventsDispatch = NO;
  130. if (!_eventsDispatchScheduled) {
  131. _eventsDispatchScheduled = YES;
  132. scheduleEventsDispatch = YES;
  133. }
  134. // We have to release the lock before dispatching block with events,
  135. // since dispatchBlock: can be executed synchronously on the same queue.
  136. // (This is happening when chrome debugging is turned on.)
  137. [_eventQueueLock unlock];
  138. if (scheduleEventsDispatch) {
  139. [_bridge
  140. dispatchBlock:^{
  141. [self flushEventsQueue];
  142. }
  143. queue:RCTJSThread];
  144. }
  145. }
  146. - (void)addDispatchObserver:(id<RCTEventDispatcherObserver>)observer
  147. {
  148. [_observersLock lock];
  149. [_observers addObject:observer];
  150. [_observersLock unlock];
  151. }
  152. - (void)removeDispatchObserver:(id<RCTEventDispatcherObserver>)observer
  153. {
  154. [_observersLock lock];
  155. [_observers removeObject:observer];
  156. [_observersLock unlock];
  157. }
  158. - (void)dispatchEvent:(id<RCTEvent>)event
  159. {
  160. [_bridge enqueueJSCall:[[event class] moduleDotMethod] args:[event arguments]];
  161. }
  162. - (dispatch_queue_t)methodQueue
  163. {
  164. return RCTJSThread;
  165. }
  166. // js thread only (which surprisingly can be the main thread, depends on used JS executor)
  167. - (void)flushEventsQueue
  168. {
  169. [_eventQueueLock lock];
  170. NSDictionary *events = _events;
  171. _events = [NSMutableDictionary new];
  172. NSMutableArray *eventQueue = _eventQueue;
  173. _eventQueue = [NSMutableArray new];
  174. _eventsDispatchScheduled = NO;
  175. [_eventQueueLock unlock];
  176. for (NSNumber *eventId in eventQueue) {
  177. [self dispatchEvent:events[eventId]];
  178. }
  179. }
  180. @end
  181. @implementation RCTBridge (RCTEventDispatcher)
  182. - (RCTEventDispatcher *)eventDispatcher
  183. {
  184. return [self moduleForClass:[RCTEventDispatcher class]];
  185. }
  186. @end