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 121ScopedCrTrackingArea::ScopedCrTrackingArea(CrTrackingArea* tracking_area) 122 : tracking_area_(tracking_area) { 123} 124 125ScopedCrTrackingArea::~ScopedCrTrackingArea() { 126 [tracking_area_ clearOwner]; 127} 128 129void ScopedCrTrackingArea::reset(CrTrackingArea* tracking_area) { 130 tracking_area_.reset(tracking_area); 131} 132 133CrTrackingArea* ScopedCrTrackingArea::get() const { 134 return tracking_area_.get(); 135} 136