1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #import "chrome/browser/ui/cocoa/tracking_area.h" 6 7 #include "base/logging.h" 8 9 // NSTrackingArea does not retain its |owner| so CrTrackingArea wraps the real 10 // owner in this proxy, which can stop forwarding messages to the owner when 11 // it is no longer |alive_|. 12 @interface CrTrackingAreaOwnerProxy : NSObject { 13 @private 14 // Whether or not the owner is "alive" and should forward calls to the real 15 // owner object. 16 BOOL alive_; 17 18 // The real object for which this is a proxy. Weak. 19 id owner_; 20 21 // The Class of |owner_|. When the actual object is no longer alive (and could 22 // be zombie), this allows for introspection. 23 Class ownerClass_; 24 } 25 @property(nonatomic, assign) BOOL alive; 26 - (id)initWithOwner:(id)owner; 27 @end 28 29 @implementation CrTrackingAreaOwnerProxy 30 31 @synthesize alive = alive_; 32 33 - (id)initWithOwner:(id)owner { 34 if ((self = [super init])) { 35 alive_ = YES; 36 owner_ = owner; 37 ownerClass_ = [owner class]; 38 } 39 return self; 40 } 41 42 - (void)forwardInvocation:(NSInvocation*)invocation { 43 if (!alive_) 44 return; 45 [invocation invokeWithTarget:owner_]; 46 } 47 48 - (NSMethodSignature*)methodSignatureForSelector:(SEL)sel { 49 // This can be called if |owner_| is not |alive_|, so use the Class to 50 // generate the signature. |-forwardInvocation:| will block the actual call. 51 return [ownerClass_ instanceMethodSignatureForSelector:sel]; 52 } 53 54 - (BOOL)respondsToSelector:(SEL)aSelector { 55 return [ownerClass_ instancesRespondToSelector:aSelector]; 56 } 57 58 @end 59 60 // Private Interface /////////////////////////////////////////////////////////// 61 62 @interface CrTrackingArea (Private) 63 - (void)windowWillClose:(NSNotification*)notif; 64 @end 65 66 //////////////////////////////////////////////////////////////////////////////// 67 68 @implementation CrTrackingArea 69 70 - (id)initWithRect:(NSRect)rect 71 options:(NSTrackingAreaOptions)options 72 proxiedOwner:(id)owner 73 userInfo:(NSDictionary*)userInfo { 74 scoped_nsobject<CrTrackingAreaOwnerProxy> ownerProxy( 75 [[CrTrackingAreaOwnerProxy alloc] initWithOwner:owner]); 76 if ((self = static_cast<id>([super initWithRect:rect 77 options:options 78 owner:ownerProxy.get() 79 userInfo:userInfo]))) { 80 ownerProxy_.swap(ownerProxy); 81 } 82 return self; 83 } 84 85 - (NSTrackingArea*)initWithRect:(NSRect)rect 86 options:(NSTrackingAreaOptions)options 87 owner:(id)owner 88 userInfo:(NSDictionary*)userInfo { 89 [NSException raise:@"org.chromium.CrTrackingArea" 90 format:@"Cannot init a CrTrackingArea with NSTrackingArea's initializer"]; 91 return nil; 92 } 93 94 - (void)dealloc { 95 [self clearOwner]; 96 [[NSNotificationCenter defaultCenter] removeObserver:self]; 97 [super dealloc]; 98 } 99 100 - (void)clearOwner { 101 [ownerProxy_ setAlive:NO]; 102 } 103 104 - (void)clearOwnerWhenWindowWillClose:(NSWindow*)window { 105 DCHECK(window); 106 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; 107 [center addObserver:self 108 selector:@selector(windowWillClose:) 109 name:NSWindowWillCloseNotification 110 object:window]; 111 } 112 113 - (void)windowWillClose:(NSNotification*)notif { 114 [self clearOwner]; 115 } 116 117 @end 118 119 // Scoper ////////////////////////////////////////////////////////////////////// 120 121 ScopedCrTrackingArea::ScopedCrTrackingArea(CrTrackingArea* tracking_area) 122 : tracking_area_(tracking_area) { 123 } 124 125 ScopedCrTrackingArea::~ScopedCrTrackingArea() { 126 [tracking_area_ clearOwner]; 127 } 128 129 void ScopedCrTrackingArea::reset(CrTrackingArea* tracking_area) { 130 tracking_area_.reset(tracking_area); 131 } 132 133 CrTrackingArea* ScopedCrTrackingArea::get() const { 134 return tracking_area_.get(); 135 } 136