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