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