2015-03-23 20:28:42 +00:00
/ * *
* 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 .
* /
2015-02-20 04:10:52 +00:00
# import "RCTEventDispatcher.h"
# import "RCTAssert.h"
# import "RCTBridge.h"
2016-04-03 06:30:26 +00:00
# import "RCTBridge+Private.h"
2015-08-11 13:37:12 +00:00
# import "RCTUtils.h"
2016-04-03 06:30:26 +00:00
# import "RCTProfile.h"
2015-05-28 03:15:33 +00:00
2015-07-21 19:37:24 +00:00
const NSInteger RCTTextUpdateLagWarningThreshold = 3 ;
2015-08-11 13:37:12 +00: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 ;
}
2015-06-02 09:58:49 +00:00
static NSNumber * RCTGetEventID ( id < RCTEvent > event )
2015-05-28 03:15:33 +00:00
{
2015-06-02 09:58:49 +00:00
return @ (
2015-08-24 10:14:33 +00:00
event . viewTag . intValue |
2016-04-01 13:53:04 +00:00
( ( ( uint64_t ) event . eventName . hash & 0 xFFFF ) < < 32 ) |
( ( ( uint64_t ) event . coalescingKey ) < < 48 )
) ;
2015-05-28 03:15:33 +00:00
}
2015-02-20 04:10:52 +00:00
@ implementation RCTEventDispatcher
{
2016-04-05 16:16:54 +00: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-01 21:25:51 +00:00
NSLock * _eventQueueLock ;
2016-04-05 16:16:54 +00: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 06:30:26 +00:00
BOOL _eventsDispatchScheduled ;
2017-10-23 20:16:24 +00:00
NSHashTable < id < RCTEventDispatcherObserver > > * _observers ;
2016-11-01 10:56:50 +00:00
NSLock * _observersLock ;
2015-02-20 04:10:52 +00:00
}
2015-05-28 03:15:33 +00:00
@ synthesize bridge = _bridge ;
RCT_EXPORT _MODULE ( )
2015-11-25 11:09:00 +00:00
- ( void ) setBridge : ( RCTBridge * ) bridge
2015-02-20 04:10:52 +00:00
{
2015-11-25 11:09:00 +00:00
_bridge = bridge ;
2016-04-05 16:16:54 +00:00
_events = [ NSMutableDictionary new ] ;
_eventQueue = [ NSMutableArray new ] ;
2015-11-25 11:09:00 +00:00
_eventQueueLock = [ NSLock new ] ;
2016-04-03 06:30:26 +00:00
_eventsDispatchScheduled = NO ;
2017-10-23 20:16:24 +00:00
_observers = [ NSHashTable weakObjectsHashTable ] ;
2016-11-01 10:56:50 +00:00
_observersLock = [ NSLock new ] ;
2015-09-29 12:26:01 +00:00
}
2015-02-24 17:06:57 +00:00
- ( void ) sendAppEventWithName : ( NSString * ) name body : ( id ) body
{
2016-08-02 18:06:19 +00:00
[ _bridge enqueueJSCall : @ "RCTNativeAppEventEmitter"
method : @ "emit"
args : body ? @ [ name , body ] : @ [ name ]
completion : NULL ] ;
2015-02-20 04:10:52 +00:00
}
2015-02-24 17:06:57 +00:00
- ( void ) sendDeviceEventWithName : ( NSString * ) name body : ( id ) body
{
2016-08-02 18:06:19 +00:00
[ _bridge enqueueJSCall : @ "RCTDeviceEventEmitter"
method : @ "emit"
args : body ? @ [ name , body ] : @ [ name ]
completion : NULL ] ;
2015-02-24 17:06:57 +00:00
}
2015-02-20 04:10:52 +00:00
- ( void ) sendInputEventWithName : ( NSString * ) name body : ( NSDictionary * ) body
{
2015-08-11 13:37:12 +00:00
if ( RCT_DEBUG ) {
RCTAssert ( [ body [ @ "target" ] isKindOfClass : [ NSNumber class ] ] ,
@ "Event body dictionary must include a 'target' property containing a React tag" ) ;
}
2015-03-23 20:28:42 +00:00
2015-08-11 13:37:12 +00:00
name = RCTNormalizeInputEventName ( name ) ;
2016-08-02 18:06:19 +00:00
[ _bridge enqueueJSCall : @ "RCTEventEmitter"
method : @ "receiveEvent"
args : body ? @ [ body [ @ "target" ] , name , body ] : @ [ body [ @ "target" ] , name ]
completion : NULL ] ;
2015-02-20 04:10:52 +00:00
}
- ( void ) sendTextEventWithType : ( RCTTextEventType ) type
reactTag : ( NSNumber * ) reactTag
text : ( NSString * ) text
2015-11-02 17:13:41 +00:00
key : ( NSString * ) key
2015-07-21 19:37:24 +00:00
eventCount : ( NSInteger ) eventCount
2015-02-20 04:10:52 +00:00
{
static NSString * events [ ] = {
2015-08-11 13:37:12 +00:00
@ "focus" ,
@ "blur" ,
@ "change" ,
@ "submitEditing" ,
@ "endEditing" ,
2015-11-02 17:13:41 +00:00
@ "keyPress"
2015-02-20 04:10:52 +00:00
} ;
2015-03-23 20:28:42 +00:00
2015-11-02 17:13:41 +00:00
NSMutableDictionary * body = [ [ NSMutableDictionary alloc ] initWithDictionary : @ {
2015-07-21 19:37:24 +00:00
@ "eventCount" : @ ( eventCount ) ,
2015-02-24 17:06:57 +00:00
@ "target" : reactTag
2015-02-20 04:10:52 +00:00
} ] ;
2015-11-02 17:13:41 +00: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 ;
}
2016-05-23 16:08:51 +00:00
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
2015-11-02 17:13:41 +00:00
[ self sendInputEventWithName : events [ type ] body : body ] ;
2016-05-23 16:08:51 +00:00
# pragma clang diagnostic pop
2015-02-20 04:10:52 +00:00
}
2015-05-28 03:15:33 +00:00
- ( void ) sendEvent : ( id < RCTEvent > ) event
2015-02-20 04:10:52 +00:00
{
2016-11-01 10:56:50 +00:00
[ _observersLock lock ] ;
for ( id < RCTEventDispatcherObserver > observer in _observers ) {
2016-11-23 13:35:34 +00:00
[ observer eventDispatcherWillDispatchEvent : event ] ;
2016-11-01 10:56:50 +00:00
}
[ _observersLock unlock ] ;
2015-05-28 03:15:33 +00:00
[ _eventQueueLock lock ] ;
2015-06-02 09:58:49 +00:00
NSNumber * eventID = RCTGetEventID ( event ) ;
2016-04-01 21:25:51 +00:00
2016-04-05 16:16:54 +00:00
id < RCTEvent > previousEvent = _events [ eventID ] ;
2015-05-28 03:15:33 +00:00
if ( previousEvent ) {
2016-04-03 06:30:26 +00:00
RCTAssert ( [ event canCoalesce ] , @ "Got event %@ which cannot be coalesced, but has the same eventID %@ as the previous event %@" , event , eventID , previousEvent ) ;
2015-05-28 03:15:33 +00:00
event = [ previousEvent coalesceWithEvent : event ] ;
2016-04-05 16:16:54 +00:00
} else {
[ _eventQueue addObject : eventID ] ;
2015-05-28 03:15:33 +00:00
}
2016-04-05 16:16:54 +00:00
_events [ eventID ] = event ;
2015-05-28 03:15:33 +00:00
2016-04-03 06:30:26 +00:00
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 . )
2015-05-28 03:15:33 +00:00
[ _eventQueueLock unlock ] ;
2016-04-03 06:30:26 +00:00
if ( scheduleEventsDispatch ) {
[ _bridge dispatchBlock : ^ {
[ self flushEventsQueue ] ;
} queue : RCTJSThread ] ;
}
2015-05-28 03:15:33 +00:00
}
2016-11-01 10:56:50 +00: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 03:15:33 +00:00
- ( void ) dispatchEvent : ( id < RCTEvent > ) event
{
2016-02-03 13:22:14 +00:00
[ _bridge enqueueJSCall : [ [ event class ] moduleDotMethod ] args : [ event arguments ] ] ;
2015-05-28 03:15:33 +00:00
}
- ( dispatch_queue _t ) methodQueue
{
return RCTJSThread ;
}
2016-04-03 06:30:26 +00:00
// js thread only ( which suprisingly can be the main thread , depends on used JS executor )
2016-02-03 13:22:15 +00:00
- ( void ) flushEventsQueue
2015-05-28 03:15:33 +00:00
{
[ _eventQueueLock lock ] ;
2016-04-05 16:16:54 +00:00
NSDictionary * events = _events ;
_events = [ NSMutableDictionary new ] ;
NSMutableArray * eventQueue = _eventQueue ;
_eventQueue = [ NSMutableArray new ] ;
2016-04-03 06:30:26 +00:00
_eventsDispatchScheduled = NO ;
2015-05-28 03:15:33 +00:00
[ _eventQueueLock unlock ] ;
2016-04-05 16:16:54 +00:00
for ( NSNumber * eventId in eventQueue ) {
[ self dispatchEvent : events [ eventId ] ] ;
2015-05-28 03:15:33 +00:00
}
}
@ end
2016-04-28 12:47:29 +00:00
@ implementation RCTBridge ( RCTEventDispatcher )
- ( RCTEventDispatcher * ) eventDispatcher
{
return [ self moduleForClass : [ RCTEventDispatcher class ] ] ;
}
@ end