diff --git a/iphone/Classes/KrollBridge.h b/iphone/Classes/KrollBridge.h index 80f23bf1f4a..2d0eb0c437a 100644 --- a/iphone/Classes/KrollBridge.h +++ b/iphone/Classes/KrollBridge.h @@ -49,5 +49,8 @@ - (id)preloadForKey:(id)key name:(id)name; - (KrollContext*)krollContext; ++ (NSArray *)krollBridgesUsingProxy:(id)proxy; +-(void)enqueueEvent:(NSString*)type forProxy:(TiProxy *)proxy withObject:(id)obj withSource:(id)source; + @end diff --git a/iphone/Classes/KrollBridge.mm b/iphone/Classes/KrollBridge.mm index ac8e87bfa11..6cc68b0270e 100644 --- a/iphone/Classes/KrollBridge.mm +++ b/iphone/Classes/KrollBridge.mm @@ -13,6 +13,7 @@ #import "TiUtils.h" #import "TiApp.h" #import "ApplicationMods.h" +#import #ifdef DEBUGGER_ENABLED #import "TiDebuggerContext.h" @@ -146,9 +147,22 @@ -(TiModule*)moduleNamed:(NSString*)name context:(id)context @end +OSSpinLock krollBridgeRegistryLock = OS_SPINLOCK_INIT; +CFMutableSetRef krollBridgeRegistry = nil; @implementation KrollBridge ++(void)initialize +{ + if (krollBridgeRegistry == nil) + { + CFSetCallBacks doNotRetain = kCFTypeSetCallBacks; + doNotRetain.retain = NULL; + doNotRetain.release = NULL; + krollBridgeRegistry = CFSetCreateMutable(NULL, 3, &doNotRetain); + } +} + -(id)init { if (self = [super init]) @@ -163,6 +177,9 @@ -(id)init object:nil]; proxyLock = [[NSRecursiveLock alloc] init]; + OSSpinLockLock(&krollBridgeRegistryLock); + CFSetAddValue(krollBridgeRegistry, self); + OSSpinLockUnlock(&krollBridgeRegistryLock); } return self; } @@ -238,6 +255,9 @@ -(void)dealloc RELEASE_TO_NIL(titanium); RELEASE_TO_NIL(modules); RELEASE_TO_NIL(proxyLock); + OSSpinLockLock(&krollBridgeRegistryLock); + CFSetRemoveValue(krollBridgeRegistry, self); + OSSpinLockUnlock(&krollBridgeRegistryLock); [super dealloc]; } @@ -406,6 +426,20 @@ - (void)fireEvent:(id)listener withObject:(id)obj remove:(BOOL)yn thisObject:(Ti } +-(void)enqueueEvent:(NSString*)type forProxy:(TiProxy *)proxy withObject:(id)obj withSource:(id)source +{ + KrollObject * eventKrollObject = [self krollObjectForProxy:proxy]; + KrollObject * sourceObject = [self krollObjectForProxy:source]; + if (sourceObject == nil) + { + sourceObject = eventKrollObject; + } + KrollEvent * newEvent = [[KrollEvent alloc] initWithType:type ForKrollObject:eventKrollObject + eventObject:obj thisObject:sourceObject]; + [context enqueue:newEvent]; + [newEvent release]; +} + -(void)injectPatches { // called to inject any Titanium patches in JS before a context is loaded... nice for @@ -547,7 +581,10 @@ - (void)registerProxy:(id)proxy //NOTE: Do NOT treat registeredProxies like a mutableDictionary; mutable dictionaries copy keys, //CFMutableDictionaryRefs only retain keys, which lets them work with proxies properly. KrollObject * ourKrollObject = [[KrollObject alloc] initWithTarget:proxy context:context]; -// CFDictionaryAddValue(registeredProxies, proxy, ourKrollObject); + + VerboseLog(@"%@ is adding %@ %X for %@ %X",self,proxy,proxy,ourKrollObject,ourKrollObject); + CFDictionaryAddValue(registeredProxies, proxy, ourKrollObject); + [ourKrollObject release]; [proxyLock unlock]; } @@ -565,6 +602,7 @@ - (void)unregisterProxy:(id)proxy } if (registeredProxies != NULL) { + VerboseLog(@"%@ is removing %@ %X for",self,proxy,proxy); CFDictionaryRemoveValue(registeredProxies, proxy); //Don't bother with removing the empty registry. It's small and leaves on dealloc anyways. } @@ -573,6 +611,10 @@ - (void)unregisterProxy:(id)proxy - (BOOL)usesProxy:(id)proxy { + if (proxy == nil) + { + return NO; + } BOOL result=NO; [proxyLock lock]; if (registeredProxies != NULL) @@ -704,4 +746,34 @@ -(id)require:(KrollContext*)kroll path:(NSString*)path @throw [NSException exceptionWithName:@"org.appcelerator.kroll" reason:[NSString stringWithFormat:@"Couldn't find module: %@",path] userInfo:nil]; } ++ (NSArray *)krollBridgesUsingProxy:(id)proxy +{ + NSMutableArray * results = nil; + + OSSpinLockLock(&krollBridgeRegistryLock); + int bridgeCount = CFSetGetCount(krollBridgeRegistry); + KrollBridge * registryObjects[bridgeCount]; + CFSetGetValues(krollBridgeRegistry, (const void **)registryObjects); + + for (int currentBridgeIndex = 0; currentBridgeIndex < bridgeCount; currentBridgeIndex++) + { + KrollBridge * currentBridge = registryObjects[currentBridgeIndex]; + if (![currentBridge usesProxy:proxy]) + { + continue; + } + if (results == nil) + { + results = [NSMutableArray arrayWithObject:currentBridge]; + continue; + } + [results addObject:currentBridge]; + } + + //Why do we wait so long? In case someone tries to dealloc the krollBridge while we're looking at it. + //registryObjects nor the registry does a retain here! + OSSpinLockUnlock(&krollBridgeRegistryLock); + return results; +} + @end \ No newline at end of file diff --git a/iphone/Classes/KrollContext.h b/iphone/Classes/KrollContext.h index a3249b2621a..ae2ef0f5a7f 100644 --- a/iphone/Classes/KrollContext.h +++ b/iphone/Classes/KrollContext.h @@ -67,6 +67,9 @@ -(void)invokeOnThread:(id)callback_ method:(SEL)method_ withObject:(id)obj callback:(id)callback selector:(SEL)selector_; -(void)evalJS:(NSString*)code; -(id)evalJSAndWait:(NSString*)code; + +-(void)enqueue:(id)obj; + -(void)invokeEvent:(KrollCallback*)callback_ args:(NSArray*)args_ thisObject:(id)thisObject_; -(void)registerTimer:(id)timer timerId:(double)timerId; -(void)unregisterTimer:(double)timerId; @@ -98,13 +101,15 @@ -(id)invokeWithResult:(KrollContext*)context; @end +@class KrollObject; @interface KrollEvent : NSObject { @private - KrollCallback *callback; - NSArray *args; + NSString * type; + KrollObject * callbackObject; + NSDictionary *eventObject; id thisObject; } --(id)initWithCallback:(KrollCallback*)callback_ args:(NSArray*)args_ thisObject:(id)thisObject_; +-(id)initWithType:(NSString *)newType ForKrollObject:(KrollObject*)newCallbackObject eventObject:(NSDictionary*)newEventObject thisObject:(id)newThisObject; -(void)invoke:(KrollContext*)context; @end diff --git a/iphone/Classes/KrollContext.mm b/iphone/Classes/KrollContext.mm index 0ef49badb17..d5cf48bb47a 100644 --- a/iphone/Classes/KrollContext.mm +++ b/iphone/Classes/KrollContext.mm @@ -523,26 +523,28 @@ -(id)invokeWithResult:(KrollContext*)context @implementation KrollEvent --(id)initWithCallback:(KrollCallback*)callback_ args:(NSArray*)args_ thisObject:(id)thisObject_ +-(id)initWithType:(NSString *)newType ForKrollObject:(KrollObject*)newCallbackObject eventObject:(NSDictionary*)newEventObject thisObject:(id)newThisObject; { if (self = [super init]) { - callback = [callback_ retain]; - args = [args_ retain]; - thisObject = [thisObject_ retain]; + type = [newType copy]; + callbackObject = [newCallbackObject retain]; + eventObject = [newEventObject retain]; + thisObject = [newThisObject retain]; } return self; } -(void)dealloc { + [type release]; [thisObject release]; - [callback release]; - [args release]; + [callbackObject release]; + [eventObject release]; [super dealloc]; } -(void)invoke:(KrollContext*)context { - [callback call:args thisObject:thisObject]; + [callbackObject triggerEvent:type withObject:eventObject thisObject:thisObject]; } @end @@ -823,6 +825,7 @@ -(void)invokeOnThread:(id)callback_ method:(SEL)method_ withObject:(id)obj callb -(void)invokeEvent:(KrollCallback*)callback_ args:(NSArray*)args_ thisObject:(id)thisObject_ { + NSLog(@"We shouldn't be here!"); KrollEvent *event = [[KrollEvent alloc] initWithCallback:callback_ args:args_ thisObject:thisObject_]; [self enqueue:event]; [event release]; @@ -1016,12 +1019,14 @@ -(void)main // we have a pending GC request to try and reclaim memory if (gcrequest) { + NSAutoreleasePool * garbagePool = [[NSAutoreleasePool alloc] init]; #if CONTEXT_DEBUG == 1 NSLog(@"CONTEXT<%@>: forced garbage collection requested",self); #endif TiGarbageCollect(context); loopCount = 0; gcrequest = NO; + [garbagePool drain]; } BOOL stuff_in_queue = YES; @@ -1079,11 +1084,13 @@ -(void)main // TODO: experiment, attempt to collect more often than usual given our environment if (loopCount == GC_LOOP_COUNT) { + NSAutoreleasePool * garbagePool = [[NSAutoreleasePool alloc] init]; #if CONTEXT_DEBUG == 1 NSLog(@"CONTEXT<%@>: garbage collecting after loop count of %d exceeded (count=%d)",self,loopCount,KrollContextCount); #endif TiGarbageCollect(context); loopCount = 0; + [garbagePool drain]; } // check to see if we're already stopped and in the flush queue state, in which case, @@ -1107,7 +1114,7 @@ -(void)main { // wait only 10 seconds and then loop, this will allow us to garbage // collect every so often - [condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]]; + [condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; } [condition unlock]; diff --git a/iphone/Classes/KrollObject.h b/iphone/Classes/KrollObject.h index fb545b08f3c..294997f7831 100644 --- a/iphone/Classes/KrollObject.h +++ b/iphone/Classes/KrollObject.h @@ -8,7 +8,7 @@ #import "TiCore.h" #import "TiBase.h" -@class KrollContext; +@class KrollContext, KrollCallback; extern TiClassRef KrollObjectClassRef; void KrollFinalizer(TiObjectRef ref); @@ -54,5 +54,10 @@ bool KrollDeleteProperty(TiContextRef ctx, TiObjectRef object, TiStringRef prope -(id)target; -(TiObjectRef)jsobject; + +-(void)storeCallback:(KrollCallback *)eventCallback forEvent:(NSString *)eventName; +-(void)removeCallback:(KrollCallback *)eventCallback forEvent:(NSString *)eventName; +-(void)triggerEvent:(NSString *)eventName withObject:(NSDictionary *)eventData thisObject:(KrollObject *)thisObject; + @end diff --git a/iphone/Classes/KrollObject.m b/iphone/Classes/KrollObject.m index aef4ef1019c..bfa32e035a9 100644 --- a/iphone/Classes/KrollObject.m +++ b/iphone/Classes/KrollObject.m @@ -299,6 +299,7 @@ TiValueRef ConvertIdTiValue(KrollContext *context, id obj) KrollContext * ourContext = [(KrollMethod *)obj context]; if (context == ourContext) { + VerboseLog(@""); // return [(KrollMethod *)obj jsobject]; } VerboseLog(@"[WARN] Generating a new TiObject for KrollMethod %@ because the contexts %@ and its context %@ differed.",obj,context,ourContext); @@ -334,10 +335,10 @@ TiValueRef ConvertIdTiValue(KrollContext *context, id obj) if (![ourBridge usesProxy:obj]) { NSLog(@"[INFO] Registering %@ to %@",obj,ourBridge); -// [ourBridge registerProxy:obj]; + [ourBridge registerProxy:obj]; } KrollObject * objKrollObject = [ourBridge krollObjectForProxy:obj]; -// return [objKrollObject jsobject]; + return [objKrollObject jsobject]; } VerboseLog(@"[WARN] Generating a new TiObject for KrollObject %@ because the contexts %@ and its context %@ differed.",obj,context,ourContext); @@ -367,14 +368,18 @@ void KrollFinalizer(TiObjectRef ref) NSLog(@"KROLL FINALIZER: %@, retain:%d",o,[o retainCount]); #endif - KrollContext * ourContext = [(KrollObject *)o context]; - KrollBridge * ourBridge = (KrollBridge *)[ourContext delegate]; - if (ourBridge != nil) + if ([o isMemberOfClass:[KrollObject class]]) { - if ([ourBridge usesProxy:o]) + KrollContext * ourContext = [(KrollObject *)o context]; + KrollBridge * ourBridge = (KrollBridge *)[ourContext delegate]; + if (ourBridge != nil) { - NSLog(@"[INFO] Unregistering %@ to %@",o,ourBridge); -// [ourBridge unregisterProxy:o]; + TiProxy * ourTarget = [o target]; + if ([ourBridge usesProxy:ourTarget]) + { + NSLog(@"[INFO] Unregistering %@ to %@",o,ourBridge); + [ourBridge unregisterProxy:ourTarget]; + } } } @@ -491,7 +496,15 @@ -(id)initWithTarget:(id)target_ context:(KrollContext*)context_ { if (self = [self init]) { - target = [target_ retain]; + NSLog(@"%@ is retaining target %@",[self class],target_); + if ([(KrollBridge *)[context_ delegate] usesProxy:target_] && [self isMemberOfClass:[KrollObject class]]) + { + NSLog(@"[WARN] %@ already has %@!",[context_ delegate],target_); + } + + +// target = [target_ retain]; + target = target_; context = context_; // don't retain jsobject = TiObjectMake([context context],KrollObjectClassRef,self); targetable = [target conformsToProtocol:@protocol(KrollTargetable)]; @@ -520,8 +533,10 @@ -(void)dealloc #if KOBJECT_MEMORY_DEBUG == 1 NSLog(@"DEALLOC KROLLOBJECT: %@",[self description]); #endif + NSLog(@"%@ is releasing target %@",[self class],target); + RELEASE_TO_NIL(properties); - RELEASE_TO_NIL(target); +// RELEASE_TO_NIL(target); RELEASE_TO_NIL(statics); // [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; [super dealloc]; @@ -867,5 +882,139 @@ -(void)setStaticValue:(id)value forKey:(NSString*)key purgable:(BOOL)purgable } } +-(void)storeCallback:(KrollCallback *)eventCallback forEvent:(NSString *)eventName +{ + TiContextRef jsContext = [context context]; + TiStringRef jsEventHashString = TiStringCreateWithUTF8CString("_EVT"); + TiObjectRef jsEventHash = TiObjectGetProperty(jsContext, jsobject, jsEventHashString, NULL); + jsEventHash = TiValueToObject(jsContext, jsEventHash, NULL); + if ((jsEventHash == NULL) || (TiValueGetType(jsContext,jsEventHash) != kTITypeObject)) + { + NSLog(@"New hash!"); + jsEventHash = TiObjectMake(jsContext, NULL, NULL); + TiObjectSetProperty(jsContext, jsobject, jsEventHashString, jsEventHash, + kTiPropertyAttributeDontEnum | kTiPropertyAttributeDontDelete, NULL); + jsEventHash = TiObjectGetProperty(jsContext, jsobject, jsEventHashString, NULL); + } + TiStringRelease(jsEventHashString); + + TiStringRef jsEventTypeString = TiStringCreateWithUTF8CString([eventName UTF8String]); + TiObjectRef jsCallbackArray = TiObjectGetProperty(jsContext, jsEventHash, jsEventTypeString, NULL); + TiObjectRef callbackFunction = [eventCallback function]; + jsCallbackArray = TiValueToObject(jsContext, jsCallbackArray, NULL); + + if ((jsCallbackArray == NULL) || (TiValueGetType(jsContext,jsCallbackArray) != kTITypeObject)) + { + NSLog(@"New Array!"); + jsCallbackArray = TiObjectMakeArray(jsContext, 1, &callbackFunction, NULL); + TiObjectSetProperty(jsContext, jsEventHash, jsEventTypeString, jsCallbackArray, + kTiPropertyAttributeDontEnum | kTiPropertyAttributeDontDelete, NULL); + } + else + { + TiStringRef jsLengthString = TiStringCreateWithUTF8CString("length"); + TiValueRef jsCallbackArrayLength = TiObjectGetProperty(jsContext, jsCallbackArray, jsLengthString, NULL); + int arrayLength = (int)TiValueToNumber(jsContext, jsCallbackArrayLength, NULL); + TiStringRelease(jsLengthString); + + TiObjectSetPropertyAtIndex(jsContext, jsCallbackArray, arrayLength, callbackFunction, NULL); + } + + //TODO: Call back to the proxy? + TiStringRelease(jsEventTypeString); + NSLog(@"Added %@ %X(%X)%X event: %X, %X, %X",eventName,self,target,jsobject,jsEventHash,jsCallbackArray,callbackFunction); +} + +-(void)removeCallback:(KrollCallback *)eventCallback forEvent:(NSString *)eventName +{ + TiContextRef jsContext = [context context]; + TiStringRef jsEventHashString = TiStringCreateWithUTF8CString("_EVT"); + TiObjectRef jsEventHash = TiObjectGetProperty(jsContext, jsobject, jsEventHashString, NULL); + if ((jsEventHash == NULL) || (TiValueGetType(jsContext,jsEventHash) != kTITypeObject)) + { + NSLog(@"[WARN] Trying to remove an event listener for %@, which never had it.",[self target]); + return; + } + TiStringRelease(jsEventHashString); + + TiStringRef jsEventTypeString = TiStringCreateWithUTF8CString([eventName UTF8String]); + TiObjectRef jsCallbackArray = TiObjectGetProperty(jsContext, jsEventHash, jsEventTypeString, NULL); + TiObjectRef callbackFunction = [eventCallback function]; + + if ((jsCallbackArray == NULL) || (TiValueGetType(jsContext,jsCallbackArray) != kTITypeObject)) + { + NSLog(@"[WARN] Trying to remove an event listener for %@, which never had it.",[self target]); + return; + } + else + { + NSLog(@"TODO: REMOVE THE EVENTLISTENER!"); +// TiStringRef jsLengthString = TiStringCreateWithUTF8CString("length"); +// TiValueRef jsCallbackArrayLength = TiObjectGetProperty(jsContext, jsCallbackArray, jsLengthString, NULL); +// int arrayLength = (int)TiValueToNumber(jsContext, jsCallbackArrayLength, NULL); +// TiStringRelease(jsLengthString); + +// TiObjectSetPropertyAtIndex(jsContext, jsCallbackArray, arrayLength, callbackFunction, NULL); + } + + //TODO: Call back to the proxy? + + +} + +-(void)triggerEvent:(NSString *)eventName withObject:(NSDictionary *)eventData thisObject:(KrollObject *)thisObject +{ + TiContextRef jsContext = [context context]; + TiStringRef jsEventHashString = TiStringCreateWithUTF8CString("_EVT"); + TiObjectRef jsEventHash = TiObjectGetProperty(jsContext, jsobject, jsEventHashString, NULL); + + NSLog(@"Calling %@ %X(%X)%X event: %X...",eventName,self,target,jsobject,jsEventHash); + if ((jsEventHash == NULL) || (TiValueGetType(jsContext,jsEventHash) != kTITypeObject)) + { //We did not have any event listeners on this proxy. Perfectly normal. + return; + } + TiStringRelease(jsEventHashString); + + TiStringRef jsEventTypeString = TiStringCreateWithUTF8CString([eventName UTF8String]); + TiObjectRef jsCallbackArray = TiObjectGetProperty(jsContext, jsEventHash, jsEventTypeString, NULL); + + NSLog(@"...%X...",jsCallbackArray); + if ((jsCallbackArray == NULL) || (TiValueGetType(jsContext,jsCallbackArray) != kTITypeObject)) + { //We did not have any event listeners on this proxy. Perfectly normal. + return; + } + + + TiStringRef jsLengthString = TiStringCreateWithUTF8CString("length"); + TiValueRef jsCallbackArrayLength = TiObjectGetProperty(jsContext, jsCallbackArray, jsLengthString, NULL); + int arrayLength = (int)TiValueToNumber(jsContext, jsCallbackArrayLength, NULL); + TiStringRelease(jsLengthString); + + if (arrayLength < 1) + { + return; + } + + TiValueRef jsEventData = ConvertIdTiValue(jsContext, eventData); + + for (int currentCallbackIndex=0; currentCallbackIndex * target -(void)setReproxying:(BOOL)yn; -(BOOL)inReproxy; +#pragma mark Utility +-(KrollObject *)krollObjectForContext:(KrollContext *)context; + #pragma mark Public -(id)allKeys; -(NSArray *)keySequence; diff --git a/iphone/Classes/TiProxy.m b/iphone/Classes/TiProxy.m index 84b1a03ab23..1308cd943a8 100644 --- a/iphone/Classes/TiProxy.m +++ b/iphone/Classes/TiProxy.m @@ -10,6 +10,7 @@ #import "TiProxy.h" #import "TiHost.h" #import "KrollCallback.h" +#import "KrollContext.h" #import "KrollBridge.h" #import "TiModule.h" #import "ListenerEntry.h" @@ -218,7 +219,7 @@ -(id)_initWithPageContext:(id)context if (self = [self init]) { pageContext = (id)context; // do not retain - [pageContext registerProxy:self]; +// [pageContext registerProxy:self]; // allow subclasses to configure themselves [self _configure]; } @@ -240,17 +241,6 @@ -(void)contextShutdown:(id)sender id context = (id)sender; // remove any listeners that match this context being destroyed that we have registered //TODO: This listeners needs a lock around it, but not deadlock with the removeEventListener inside. - if (listeners!=nil) - { - for (id type in listeners) - { - NSArray *a = [listeners objectForKey:type]; - for (KrollCallback *callback in a) - { - [self removeEventListener:[NSArray arrayWithObjects:type,callback,type,nil]]; - } - } - } [self _destroy]; [self contextWasShutdown:context]; @@ -339,25 +329,7 @@ -(void)_destroy // remove all listeners JS side proxy pthread_rwlock_wrlock(&listenerLock); - if (listeners!=nil) - { - if (pageContext!=nil) - { - TiHost *host = [self _host]; - if (host!=nil) - { - for (id type in listeners) - { - NSArray *array = [listeners objectForKey:type]; - for (id listener in array) - { - [host removeListener:listener context:pageContext]; - } - } - } - } - RELEASE_TO_NIL(listeners); - } + RELEASE_TO_NIL(listeners); pthread_rwlock_unlock(&listenerLock); pthread_rwlock_wrlock(&dynpropsLock); @@ -453,7 +425,7 @@ -(BOOL)_hasListeners:(NSString*)type { pthread_rwlock_rdlock(&listenerLock); //If listeners is nil at this point, result is still false. - BOOL result = [listeners objectForKey:type]!=nil; + BOOL result = [[listeners objectForKey:type] intValue]>0; pthread_rwlock_unlock(&listenerLock); return result; } @@ -525,88 +497,61 @@ -(NSArray *)keySequence return nil; } +-(KrollObject *)krollObjectForContext:(KrollContext *)context +{ + KrollBridge * ourBridge = (KrollBridge *)[context delegate]; +#ifdef DEBUG + if(![ourBridge usesProxy:self]) + { + NSLog(@"[ERROR] Adding an event listener to a proxy that isn't already in the context!!!"); + } +#endif + return [ourBridge krollObjectForProxy:self]; +} + -(void)addEventListener:(NSArray*)args { - pthread_rwlock_wrlock(&listenerLock); - NSString *type = [args objectAtIndex:0]; KrollCallback* listener = [args objectAtIndex:1]; ENSURE_TYPE(listener,KrollCallback); - - listener.type = type; - - if (listeners==nil) - { - listeners = [[NSMutableDictionary alloc] init]; - } - NSMutableArray *l = [listeners objectForKey:type]; - if (l==nil) - { -// l = TiCreateNonRetainingArray(); - l = [[NSMutableArray alloc] init]; - [listeners setObject:l forKey:type]; - [l release]; - } + KrollObject * ourObject = [self krollObjectForContext:[listener context]]; + [ourObject storeCallback:listener forEvent:type]; - [l addObject:listener]; - + //TODO: You know, we can probably nip this in the bud and do this at a lower level, + //Or make this less onerous. + int ourCallbackCount = 0; + + pthread_rwlock_wrlock(&listenerLock); + ourCallbackCount = [[listeners objectForKey:type] intValue] + 1; + if(listeners==nil){ + listeners = [[NSMutableDictionary alloc] initWithCapacity:3]; + } + [listeners setObject:NUMINT(ourCallbackCount) forKey:type]; pthread_rwlock_unlock(&listenerLock); - - [self _listenerAdded:type count:[l count]]; + + [self _listenerAdded:type count:ourCallbackCount]; } -(void)removeEventListener:(NSArray*)args { NSString *type = [args objectAtIndex:0]; - KrollCallback *listener = [args objectAtIndex:1]; - - // if we pass a third-arg, that means we shouldn't - // mutate the array and we're in a destroy - BOOL inDestroy = [args count] > 2; - int count = 0; - - if (inDestroy==NO) - { - // hold during event - [listener retain]; + KrollCallback* listener = [args objectAtIndex:1]; + ENSURE_TYPE(listener,KrollCallback); - pthread_rwlock_wrlock(&listenerLock); - - NSMutableArray *l = [listeners objectForKey:type]; + KrollObject * ourObject = [self krollObjectForContext:[listener context]]; + [ourObject removeCallback:listener forEvent:type]; - if (l!=nil && [l count]>0) - { - [l removeObject:listener]; - - count = [l count]; - - // once empty, remove the object - if (count==0) - { - [listeners removeObjectForKey:type]; - } - - // once we have no more listeners, release memory! - if ([listeners count]==0) - { - [listeners autorelease]; - listeners = nil; - } - } - pthread_rwlock_unlock(&listenerLock); + //TODO: You know, we can probably nip this in the bud and do this at a lower level, + //Or make this less onerous. + int ourCallbackCount = 0; - } - - id ctx = (id)[listener context]; - [[self _host] removeListener:listener context:ctx]; - [self _listenerRemoved:type count:count]; - - - if (inDestroy==NO) - { - [listener release]; - } + pthread_rwlock_wrlock(&listenerLock); + ourCallbackCount = [[listeners objectForKey:type] intValue] - 1; + [listeners setObject:NUMINT(ourCallbackCount) forKey:type]; + pthread_rwlock_unlock(&listenerLock); + + [self _listenerRemoved:type count:ourCallbackCount]; } -(void)fireEvent:(id)args @@ -650,35 +595,26 @@ -(void)fireEvent:(NSString*)type withObject:(id)obj withSource:(id)source propag return; } - pthread_rwlock_rdlock(&listenerLock); - if (listeners!=nil) + //TODO: This can be optimized later on. + NSMutableDictionary* eventObject = nil; + if ([obj isKindOfClass:[NSDictionary class]]) { - NSMutableArray *l = [listeners objectForKey:type]; - if (l!=nil) - { - TiHost *host = [self _host]; - - NSMutableDictionary* eventObject = nil; - if ([obj isKindOfClass:[NSDictionary class]]) - { - eventObject = [NSMutableDictionary dictionaryWithDictionary:obj]; - } - else - { - eventObject = [NSMutableDictionary dictionary]; - } - - // common event properties for all events we fire - [eventObject setObject:type forKey:@"type"]; - [eventObject setObject:source forKey:@"source"]; - - for (KrollCallback *listener in l) - { - [host fireEvent:listener withObject:eventObject remove:NO context:self.pageContext thisObject:nil]; - } - } + eventObject = [NSMutableDictionary dictionaryWithDictionary:obj]; + [eventObject setObject:type forKey:@"type"]; + [eventObject setObject:source forKey:@"source"]; + } + else + { + eventObject = [NSMutableDictionary dictionaryWithObjectsAndKeys:type,@"type",source,@"source",nil]; + } + + //Since listeners are now at the object level, we have to wait in line. + NSArray * bridges = [KrollBridge krollBridgesUsingProxy:self]; + + for (KrollBridge * currentBridge in bridges) + { + [currentBridge enqueueEvent:type forProxy:self withObject:eventObject withSource:source]; } - pthread_rwlock_unlock(&listenerLock); } - (void)setValuesForKeysWithDictionary:(NSDictionary *)keyedValues @@ -730,6 +666,7 @@ -(BOOL)_propertyInitRequiresUIThread - (id) valueForUndefinedKey: (NSString *) key { + VerboseLog(@"Value for Undefined Key: %@",key); if ([key isEqualToString:@"toString"] || [key isEqualToString:@"valueOf"]) { return [self description];