summaryrefslogtreecommitdiffstats
path: root/ui/base
diff options
context:
space:
mode:
authortapted@chromium.org <tapted@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-12 01:13:44 +0000
committertapted@chromium.org <tapted@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-12 01:13:44 +0000
commit36ec017b4ff1f2ab493ed0c0c9d3780532a32dd6 (patch)
treed1382b0eb4b5f27a5c34fb3c48446028587e5fa9 /ui/base
parent24a28539bde15b14c8070a71055b17f4938af256 (diff)
downloadchromium_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.h64
-rw-r--r--ui/base/cocoa/tracking_area.mm130
-rw-r--r--ui/base/cocoa/tracking_area_unittest.mm101
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