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 "ui/base/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 owner:(id)owner 73 userInfo:(NSDictionary*)userInfo { 74 base::scoped_nsobject<CrTrackingAreaOwnerProxy> ownerProxy( 75 [[CrTrackingAreaOwnerProxy alloc] initWithOwner:owner]); 76 if ((self = [super initWithRect:rect 77 options:options 78 owner:ownerProxy.get() 79 userInfo:userInfo])) { 80 ownerProxy_.swap(ownerProxy); 81 } 82 return self; 83 } 84 85 - (void)dealloc { 86 [self clearOwner]; 87 [[NSNotificationCenter defaultCenter] removeObserver:self]; 88 [super dealloc]; 89 } 90 91 - (void)clearOwner { 92 [ownerProxy_ setAlive:NO]; 93 } 94 95 - (void)clearOwnerWhenWindowWillClose:(NSWindow*)window { 96 DCHECK(window); 97 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; 98 [center addObserver:self 99 selector:@selector(windowWillClose:) 100 name:NSWindowWillCloseNotification 101 object:window]; 102 } 103 104 - (void)windowWillClose:(NSNotification*)notif { 105 [self clearOwner]; 106 } 107 108 @end 109 110 // Scoper ////////////////////////////////////////////////////////////////////// 111 112 namespace ui { 113 114 ScopedCrTrackingArea::ScopedCrTrackingArea(CrTrackingArea* tracking_area) 115 : tracking_area_(tracking_area) { 116 } 117 118 ScopedCrTrackingArea::~ScopedCrTrackingArea() { 119 [tracking_area_ clearOwner]; 120 } 121 122 void ScopedCrTrackingArea::reset(CrTrackingArea* tracking_area) { 123 tracking_area_.reset(tracking_area); 124 } 125 126 CrTrackingArea* ScopedCrTrackingArea::get() const { 127 return tracking_area_.get(); 128 } 129 130 } // namespace ui 131