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 112namespace ui { 113 114ScopedCrTrackingArea::ScopedCrTrackingArea(CrTrackingArea* tracking_area) 115 : tracking_area_(tracking_area) { 116} 117 118ScopedCrTrackingArea::~ScopedCrTrackingArea() { 119 [tracking_area_ clearOwner]; 120} 121 122void ScopedCrTrackingArea::reset(CrTrackingArea* tracking_area) { 123 tracking_area_.reset(tracking_area); 124} 125 126CrTrackingArea* ScopedCrTrackingArea::get() const { 127 return tracking_area_.get(); 128} 129 130} // namespace ui 131