diff options
author | tapted@chromium.org <tapted@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-12 01:13:44 +0000 |
---|---|---|
committer | tapted@chromium.org <tapted@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-12 01:13:44 +0000 |
commit | 36ec017b4ff1f2ab493ed0c0c9d3780532a32dd6 (patch) | |
tree | d1382b0eb4b5f27a5c34fb3c48446028587e5fa9 /ui/base | |
parent | 24a28539bde15b14c8070a71055b17f4938af256 (diff) | |
download | chromium_src-36ec017b4ff1f2ab493ed0c0c9d3780532a32dd6.zip chromium_src-36ec017b4ff1f2ab493ed0c0c9d3780532a32dd6.tar.gz chromium_src-36ec017b4ff1f2ab493ed0c0c9d3780532a32dd6.tar.bz2 |
Move CrTrackingArea from chrome/browser/ui/cocoa to ui/base/cocoa.
This change moves tracking_area.h, its implementation and tests to
ui/base so that Chrome components outside of the browser can use it.
BUG=138633
TEST=No functional changes
Review URL: https://chromiumcodereview.appspot.com/12763002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@187441 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/base')
-rw-r--r-- | ui/base/cocoa/tracking_area.h | 64 | ||||
-rw-r--r-- | ui/base/cocoa/tracking_area.mm | 130 | ||||
-rw-r--r-- | ui/base/cocoa/tracking_area_unittest.mm | 101 |
3 files changed, 295 insertions, 0 deletions
diff --git a/ui/base/cocoa/tracking_area.h b/ui/base/cocoa/tracking_area.h new file mode 100644 index 0000000..fa49a37 --- /dev/null +++ b/ui/base/cocoa/tracking_area.h @@ -0,0 +1,64 @@ +// 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. + +#ifndef UI_BASE_COCOA_TRACKING_AREA_H_ +#define UI_BASE_COCOA_TRACKING_AREA_H_ + +#import <AppKit/AppKit.h> + +#include "base/memory/scoped_nsobject.h" +#include "ui/base/ui_export.h" + +@class CrTrackingAreaOwnerProxy; + +// The CrTrackingArea can be used in place of an NSTrackingArea to shut off +// messaging to the |owner| at a specific point in time. +@interface CrTrackingArea : NSTrackingArea { + @private + scoped_nsobject<CrTrackingAreaOwnerProxy> ownerProxy_; +} + +// Designated initializer. Forwards all arguments to the superclass, but wraps +// |owner| in a proxy object. +- (id)initWithRect:(NSRect)rect + options:(NSTrackingAreaOptions)options + owner:(id)owner + userInfo:(NSDictionary*)userInfo; + +// Prevents any future messages from being delivered to the |owner|. +- (void)clearOwner; + +// Watches |window| for its NSWindowWillCloseNotification and calls +// |-clearOwner| when the notification is observed. +- (void)clearOwnerWhenWindowWillClose:(NSWindow*)window; + +@end + +// Scoper ////////////////////////////////////////////////////////////////////// + +namespace ui { + +// Use an instance of this class to call |-clearOwner| on the |tracking_area_| +// when this goes out of scope. +class UI_EXPORT ScopedCrTrackingArea { + public: + // Takes ownership of |tracking_area| without retaining it. + explicit ScopedCrTrackingArea(CrTrackingArea* tracking_area = nil); + ~ScopedCrTrackingArea(); + + // This will call |scoped_nsobject<>::reset()| to take ownership of the new + // tracking area. Note that -clearOwner is NOT called on the existing + // tracking area. + void reset(CrTrackingArea* tracking_area = nil); + + CrTrackingArea* get() const; + + private: + scoped_nsobject<CrTrackingArea> tracking_area_; + DISALLOW_COPY_AND_ASSIGN(ScopedCrTrackingArea); +}; + +} // namespace ui + +#endif // UI_BASE_COCOA_TRACKING_AREA_H_ diff --git a/ui/base/cocoa/tracking_area.mm b/ui/base/cocoa/tracking_area.mm new file mode 100644 index 0000000..474d120 --- /dev/null +++ b/ui/base/cocoa/tracking_area.mm @@ -0,0 +1,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 diff --git a/ui/base/cocoa/tracking_area_unittest.mm b/ui/base/cocoa/tracking_area_unittest.mm new file mode 100644 index 0000000..487aa89 --- /dev/null +++ b/ui/base/cocoa/tracking_area_unittest.mm @@ -0,0 +1,101 @@ +// 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. + +#include "base/memory/scoped_nsobject.h" +#import "ui/base/cocoa/tracking_area.h" +#import "ui/base/test/ui_cocoa_test_helper.h" + +// A test object that counts the number of times a message is sent to it. +@interface TestTrackingAreaOwner : NSObject { + @private + NSUInteger messageCount_; +} +@property(nonatomic, assign) NSUInteger messageCount; +- (void)performMessage; +@end + +@implementation TestTrackingAreaOwner +@synthesize messageCount = messageCount_; +- (void)performMessage { + ++messageCount_; +} +@end + +namespace ui { + +class CrTrackingAreaTest : public CocoaTest { + public: + CrTrackingAreaTest() + : owner_([[TestTrackingAreaOwner alloc] init]), + trackingArea_([[CrTrackingArea alloc] + initWithRect:NSMakeRect(0, 0, 100, 100) + options:NSTrackingMouseMoved | NSTrackingActiveInKeyWindow + owner:owner_.get() + userInfo:nil]) { + } + + scoped_nsobject<TestTrackingAreaOwner> owner_; + scoped_nsobject<CrTrackingArea> trackingArea_; +}; + +TEST_F(CrTrackingAreaTest, OwnerForwards) { + [[trackingArea_ owner] performMessage]; + EXPECT_EQ(1U, [owner_ messageCount]); + + [[trackingArea_ owner] performMessage]; + EXPECT_EQ(2U, [owner_ messageCount]); +} + +TEST_F(CrTrackingAreaTest, OwnerStopsForwarding) { + [[trackingArea_ owner] performMessage]; + EXPECT_EQ(1U, [owner_ messageCount]); + + [trackingArea_ clearOwner]; + + [[trackingArea_ owner] performMessage]; + EXPECT_EQ(1U, [owner_ messageCount]); +} + +TEST_F(CrTrackingAreaTest, OwnerAutomaticallyStopsForwardingOnClose) { + [test_window() orderFront:nil]; + [trackingArea_ clearOwnerWhenWindowWillClose:test_window()]; + + [[trackingArea_ owner] performMessage]; + EXPECT_EQ(1U, [owner_ messageCount]); + + [test_window() close]; + + [[trackingArea_ owner] performMessage]; + EXPECT_EQ(1U, [owner_ messageCount]); +} + +TEST_F(CrTrackingAreaTest, ScoperInit) { + { + ScopedCrTrackingArea scoper([trackingArea_ retain]); + [[scoper.get() owner] performMessage]; + EXPECT_EQ(1U, [owner_ messageCount]); + } + + [[trackingArea_ owner] performMessage]; + EXPECT_EQ(1U, [owner_ messageCount]); +} + +TEST_F(CrTrackingAreaTest, ScoperReset) { + { + ScopedCrTrackingArea scoper; + EXPECT_FALSE(scoper.get()); + + scoper.reset([trackingArea_ retain]); + [[scoper.get() owner] performMessage]; + EXPECT_EQ(1U, [owner_ messageCount]); + + [[scoper.get() owner] performMessage]; + EXPECT_EQ(2U, [owner_ messageCount]); + } + + [[trackingArea_ owner] performMessage]; + EXPECT_EQ(2U, [owner_ messageCount]); +} + +} // namespace ui |