summaryrefslogtreecommitdiffstats
path: root/ui/base/cocoa/tracking_area.mm
blob: 474d120345182cab1ad4d00ccb95cc1f578fea94 (plain)
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 {
  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