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