2015-03-23 23:28:42 +03:00
/ * *
2018-09-12 01:27:47 +03:00
* Copyright ( c ) Facebook , Inc . and its affiliates .
2015-03-23 23:28:42 +03:00
*
2018-02-17 05:24:55 +03:00
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree .
2015-03-23 23:28:42 +03:00
* /
2015-02-20 07:10:52 +03:00
# import "RCTEventDispatcher.h"
# import "RCTAssert.h"
# import "RCTBridge.h"
2016-04-03 09:30:26 +03:00
# import "RCTBridge+Private.h"
2019-03-27 21:17:05 +03:00
# import "RCTComponentEvent.h"
2016-04-03 09:30:26 +03:00
# import "RCTProfile.h"
2019-03-27 21:17:05 +03:00
# import "RCTUtils.h"
2015-05-28 06:15:33 +03:00
2015-07-21 22:37:24 +03:00
const NSInteger RCTTextUpdateLagWarningThreshold = 3 ;
2015-08-11 16:37:12 +03:00
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 ;
}
2019-05-09 20:19:30 +03:00
static NSNumber * RCTGetEventID ( NSNumber * viewTag , NSString * eventName , uint16_t coalescingKey )
2015-05-28 06:15:33 +03:00
{
2015-06-02 12:58:49 +03:00
return @ (
2019-05-09 20:19:30 +03:00
viewTag . intValue |
( ( ( uint64_t ) eventName . hash & 0 xFFFF ) < < 32 ) |
( ( ( uint64_t ) coalescingKey ) < < 48 )
2016-04-01 16:53:04 +03:00
) ;
2015-05-28 06:15:33 +03:00
}
2019-05-09 20:19:30 +03:00
static uint16_t RCTUniqueCoalescingKeyGenerator = 0 ;
2015-02-20 07:10:52 +03:00
@ implementation RCTEventDispatcher
{
2016-04-05 19:16:54 +03:00
// We need this lock to protect access to _events , _eventQueue and _eventsDispatchScheduled . It ' s filled in on main thread and consumed on js thread .
2016-04-02 00:25:51 +03:00
NSLock * _eventQueueLock ;
2016-04-05 19:16:54 +03:00
// 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 ;
2016-04-03 09:30:26 +03:00
BOOL _eventsDispatchScheduled ;
2017-10-23 23:16:24 +03:00
NSHashTable < id < RCTEventDispatcherObserver > > * _observers ;
2016-11-01 13:56:50 +03:00
NSLock * _observersLock ;
2015-02-20 07:10:52 +03:00
}
2015-05-28 06:15:33 +03:00
@ synthesize bridge = _bridge ;
RCT_EXPORT _MODULE ( )
2015-11-25 14:09:00 +03:00
- ( void ) setBridge : ( RCTBridge * ) bridge
2015-02-20 07:10:52 +03:00
{
2015-11-25 14:09:00 +03:00
_bridge = bridge ;
2016-04-05 19:16:54 +03:00
_events = [ NSMutableDictionary new ] ;
_eventQueue = [ NSMutableArray new ] ;
2015-11-25 14:09:00 +03:00
_eventQueueLock = [ NSLock new ] ;
2016-04-03 09:30:26 +03:00
_eventsDispatchScheduled = NO ;
2017-10-23 23:16:24 +03:00
_observers = [ NSHashTable weakObjectsHashTable ] ;
2016-11-01 13:56:50 +03:00
_observersLock = [ NSLock new ] ;
2015-09-29 15:26:01 +03:00
}
2015-02-24 20:06:57 +03:00
- ( void ) sendAppEventWithName : ( NSString * ) name body : ( id ) body
{
2016-08-02 21:06:19 +03:00
[ _bridge enqueueJSCall : @ "RCTNativeAppEventEmitter"
method : @ "emit"
args : body ? @ [ name , body ] : @ [ name ]
completion : NULL ] ;
2015-02-20 07:10:52 +03:00
}
2015-02-24 20:06:57 +03:00
- ( void ) sendDeviceEventWithName : ( NSString * ) name body : ( id ) body
{
2016-08-02 21:06:19 +03:00
[ _bridge enqueueJSCall : @ "RCTDeviceEventEmitter"
method : @ "emit"
args : body ? @ [ name , body ] : @ [ name ]
completion : NULL ] ;
2015-02-24 20:06:57 +03:00
}
2015-02-20 07:10:52 +03:00
- ( void ) sendTextEventWithType : ( RCTTextEventType ) type
reactTag : ( NSNumber * ) reactTag
text : ( NSString * ) text
2015-11-02 20:13:41 +03:00
key : ( NSString * ) key
2015-07-21 22:37:24 +03:00
eventCount : ( NSInteger ) eventCount
2015-02-20 07:10:52 +03:00
{
static NSString * events [ ] = {
2015-08-11 16:37:12 +03:00
@ "focus" ,
@ "blur" ,
@ "change" ,
@ "submitEditing" ,
@ "endEditing" ,
2015-11-02 20:13:41 +03:00
@ "keyPress"
2015-02-20 07:10:52 +03:00
} ;
2015-03-23 23:28:42 +03:00
2015-11-02 20:13:41 +03:00
NSMutableDictionary * body = [ [ NSMutableDictionary alloc ] initWithDictionary : @ {
2015-07-21 22:37:24 +03:00
@ "eventCount" : @ ( eventCount ) ,
2015-02-20 07:10:52 +03:00
} ] ;
2015-11-02 20:13:41 +03:00
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 ;
}
2019-03-27 21:17:05 +03:00
RCTComponentEvent * event = [ [ RCTComponentEvent alloc ] initWithName : events [ type ]
viewTag : reactTag
body : body ] ;
[ self sendEvent : event ] ;
2015-02-20 07:10:52 +03:00
}
2015-05-28 06:15:33 +03:00
- ( void ) sendEvent : ( id < RCTEvent > ) event
2015-02-20 07:10:52 +03:00
{
2016-11-01 13:56:50 +03:00
[ _observersLock lock ] ;
for ( id < RCTEventDispatcherObserver > observer in _observers ) {
2016-11-23 16:35:34 +03:00
[ observer eventDispatcherWillDispatchEvent : event ] ;
2016-11-01 13:56:50 +03:00
}
[ _observersLock unlock ] ;
2019-05-09 20:19:30 +03:00
[ _eventQueueLock lock ] ;
2016-04-02 00:25:51 +03:00
2019-05-09 20:19:30 +03:00
NSNumber * eventID ;
if ( event . canCoalesce ) {
eventID = RCTGetEventID ( event . viewTag , event . eventName , event . coalescingKey ) ;
2019-03-27 21:17:05 +03:00
id < RCTEvent > previousEvent = _events [ eventID ] ;
if ( previousEvent ) {
event = [ previousEvent coalesceWithEvent : event ] ;
} else {
[ _eventQueue addObject : eventID ] ;
}
2019-05-09 20:19:30 +03:00
} 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 ] ;
}
2015-05-28 06:15:33 +03:00
2019-05-09 20:19:30 +03:00
_events [ eventID ] = event ;
BOOL scheduleEventsDispatch = NO ;
if ( ! _eventsDispatchScheduled ) {
_eventsDispatchScheduled = YES ;
scheduleEventsDispatch = YES ;
}
2016-04-03 09:30:26 +03:00
2019-05-09 20:19:30 +03:00
// 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 ] ;
2016-04-03 09:30:26 +03:00
2019-05-09 20:19:30 +03:00
if ( scheduleEventsDispatch ) {
2016-04-03 09:30:26 +03:00
[ _bridge dispatchBlock : ^ {
2019-05-09 20:19:30 +03:00
[ self flushEventsQueue ] ;
2016-04-03 09:30:26 +03:00
} queue : RCTJSThread ] ;
}
2015-05-28 06:15:33 +03:00
}
2016-11-01 13:56:50 +03:00
- ( void ) addDispatchObserver : ( id < RCTEventDispatcherObserver > ) observer
{
[ _observersLock lock ] ;
[ _observers addObject : observer ] ;
[ _observersLock unlock ] ;
}
- ( void ) removeDispatchObserver : ( id < RCTEventDispatcherObserver > ) observer
{
[ _observersLock lock ] ;
[ _observers removeObject : observer ] ;
[ _observersLock unlock ] ;
}
2015-05-28 06:15:33 +03:00
- ( void ) dispatchEvent : ( id < RCTEvent > ) event
{
2016-02-03 16:22:14 +03:00
[ _bridge enqueueJSCall : [ [ event class ] moduleDotMethod ] args : [ event arguments ] ] ;
2015-05-28 06:15:33 +03:00
}
- ( dispatch_queue _t ) methodQueue
{
return RCTJSThread ;
}
2018-01-13 09:03:51 +03:00
// js thread only ( which surprisingly can be the main thread , depends on used JS executor )
2016-02-03 16:22:15 +03:00
- ( void ) flushEventsQueue
2015-05-28 06:15:33 +03:00
{
[ _eventQueueLock lock ] ;
2016-04-05 19:16:54 +03:00
NSDictionary * events = _events ;
_events = [ NSMutableDictionary new ] ;
NSMutableArray * eventQueue = _eventQueue ;
_eventQueue = [ NSMutableArray new ] ;
2016-04-03 09:30:26 +03:00
_eventsDispatchScheduled = NO ;
2015-05-28 06:15:33 +03:00
[ _eventQueueLock unlock ] ;
2016-04-05 19:16:54 +03:00
for ( NSNumber * eventId in eventQueue ) {
[ self dispatchEvent : events [ eventId ] ] ;
2015-05-28 06:15:33 +03:00
}
}
@ end
2016-04-28 15:47:29 +03:00
@ implementation RCTBridge ( RCTEventDispatcher )
- ( RCTEventDispatcher * ) eventDispatcher
{
return [ self moduleForClass : [ RCTEventDispatcher class ] ] ;
}
@ end