| Index: ui/views/cocoa/cocoa_window_move_loop.mm
|
| diff --git a/ui/views/cocoa/cocoa_window_move_loop.mm b/ui/views/cocoa/cocoa_window_move_loop.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a306f9cfb5ced21e0dc7c8d259cb515015eac09a
|
| --- /dev/null
|
| +++ b/ui/views/cocoa/cocoa_window_move_loop.mm
|
| @@ -0,0 +1,127 @@
|
| +// Copyright 2016 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 "ui/views/cocoa/cocoa_window_move_loop.h"
|
| +
|
| +#include "base/run_loop.h"
|
| +#include "ui/display/screen.h"
|
| +#import "ui/gfx/mac/coordinate_conversion.h"
|
| +#import "ui/views/cocoa/bridged_native_widget.h"
|
| +
|
| +// When event monitors process the events the full list of monitors is cached,
|
| +// and if we unregister the event monitor that's at the end of the list while
|
| +// processing the first monitor's handler -- the callback for the unregistered
|
| +// monitor will still be called even though it's unregistered. This will result
|
| +// in dereferencing an invalid pointer.
|
| +//
|
| +// WeakCocoaWindowMoveLoop is retained by the event monitor and stores weak
|
| +// pointer for the CocoaWindowMoveLoop, so there will be no invalid memory
|
| +// access.
|
| +@interface WeakCocoaWindowMoveLoop : NSObject {
|
| + @private
|
| + base::WeakPtr<views::CocoaWindowMoveLoop> weak_;
|
| +}
|
| +@end
|
| +
|
| +@implementation WeakCocoaWindowMoveLoop
|
| +- (id)initWithWeakPtr:(const base::WeakPtr<views::CocoaWindowMoveLoop>&)weak {
|
| + if ((self = [super init])) {
|
| + weak_ = weak;
|
| + }
|
| + return self;
|
| +}
|
| +
|
| +- (base::WeakPtr<views::CocoaWindowMoveLoop>&)weak {
|
| + return weak_;
|
| +}
|
| +@end
|
| +
|
| +namespace views {
|
| +
|
| +CocoaWindowMoveLoop::CocoaWindowMoveLoop(
|
| + BridgedNativeWidget* owner,
|
| + const NSPoint& initial_mouse_in_screen)
|
| + : owner_(owner),
|
| + initial_mouse_in_screen_(initial_mouse_in_screen),
|
| + weak_factory_(this) {
|
| +}
|
| +
|
| +CocoaWindowMoveLoop::~CocoaWindowMoveLoop() {
|
| + // Handle the pathological case, where |this| is destroyed while running.
|
| + if (exit_reason_ref_) {
|
| + *exit_reason_ref_ = WINDOW_DESTROYED;
|
| + quit_closure_.Run();
|
| + }
|
| +
|
| + owner_ = nullptr;
|
| +}
|
| +
|
| +Widget::MoveLoopResult CocoaWindowMoveLoop::Run() {
|
| + LoopExitReason exit_reason = ENDED_EXTERNALLY;
|
| + exit_reason_ref_ = &exit_reason;
|
| + NSWindow* window = owner_->ns_window();
|
| + const NSRect initial_frame = [window frame];
|
| +
|
| + base::RunLoop run_loop;
|
| + quit_closure_ = run_loop.QuitClosure();
|
| +
|
| + // Will be retained by the monitor handler block.
|
| + WeakCocoaWindowMoveLoop* weak_cocoa_window_move_loop =
|
| + [[[WeakCocoaWindowMoveLoop alloc]
|
| + initWithWeakPtr:weak_factory_.GetWeakPtr()] autorelease];
|
| +
|
| + // Esc keypress is handled by EscapeTracker, which is installed by
|
| + // TabDragController.
|
| + NSEventMask mask = NSLeftMouseUpMask | NSLeftMouseDraggedMask;
|
| + auto handler = ^NSEvent*(NSEvent* event) {
|
| + CocoaWindowMoveLoop* strong = [weak_cocoa_window_move_loop weak].get();
|
| + if (!strong || !strong->exit_reason_ref_) {
|
| + // By this point CocoaWindowMoveLoop was deleted while processing this
|
| + // same event, and this event monitor was not unregistered in time. See
|
| + // the WeakCocoaWindowMoveLoop comment above.
|
| + // Continue processing the event.
|
| + return event;
|
| + }
|
| +
|
| + if ([event type] == NSLeftMouseDragged) {
|
| + const NSPoint mouse_in_screen = [NSEvent mouseLocation];
|
| +
|
| + const NSRect ns_frame = NSOffsetRect(
|
| + initial_frame, mouse_in_screen.x - initial_mouse_in_screen_.x,
|
| + mouse_in_screen.y - initial_mouse_in_screen_.y);
|
| + [window setFrame:ns_frame display:NO animate:NO];
|
| +
|
| + return event;
|
| + }
|
| +
|
| + DCHECK_EQ([event type], NSLeftMouseUp);
|
| + *strong->exit_reason_ref_ = MOUSE_UP;
|
| + strong->quit_closure_.Run();
|
| + return event; // Process the MouseUp.
|
| + };
|
| + id monitor =
|
| + [NSEvent addLocalMonitorForEventsMatchingMask:mask handler:handler];
|
| +
|
| + run_loop.Run();
|
| + [NSEvent removeMonitor:monitor];
|
| +
|
| + if (exit_reason != WINDOW_DESTROYED && exit_reason != ENDED_EXTERNALLY) {
|
| + exit_reason_ref_ = nullptr; // Ensure End() doesn't replace the reason.
|
| + owner_->EndMoveLoop(); // Deletes |this|.
|
| + }
|
| +
|
| + return exit_reason != MOUSE_UP ? Widget::MOVE_LOOP_CANCELED
|
| + : Widget::MOVE_LOOP_SUCCESSFUL;
|
| +}
|
| +
|
| +void CocoaWindowMoveLoop::End() {
|
| + if (exit_reason_ref_) {
|
| + DCHECK_EQ(*exit_reason_ref_, ENDED_EXTERNALLY);
|
| + // Ensure the destructor doesn't replace the reason.
|
| + exit_reason_ref_ = nullptr;
|
| + quit_closure_.Run();
|
| + }
|
| +}
|
| +
|
| +} // namespace views
|
|
|