• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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