OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 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 #include "ui/views/cocoa/cocoa_window_move_loop.h" |
| 6 |
| 7 #include "base/run_loop.h" |
| 8 #include "ui/display/screen.h" |
| 9 #import "ui/gfx/mac/coordinate_conversion.mm" |
| 10 #import "ui/views/cocoa/bridged_native_widget.h" |
| 11 |
| 12 // When event monitors process the events the full list of monitors is cached, |
| 13 // and if we unregister the event monitor that's at the end of the list while |
| 14 // processing the first monitor's handler -- the callback for the unregistered |
| 15 // monitor will still be called even though it's unregistered. This will result |
| 16 // in dereferencing an invalid pointer. |
| 17 // |
| 18 // WeakCocoaWindowMoveLoop is retained by the event monitor and stores weak |
| 19 // pointer for the CocoaWindowMoveLoop, so there will be no invalid memory |
| 20 // access. |
| 21 @interface WeakCocoaWindowMoveLoop : NSObject { |
| 22 @private |
| 23 base::WeakPtr<views::CocoaWindowMoveLoop> weak_; |
| 24 } |
| 25 @end |
| 26 |
| 27 @implementation WeakCocoaWindowMoveLoop |
| 28 - (id)initWithWeakPtr:(const base::WeakPtr<views::CocoaWindowMoveLoop>&)weak { |
| 29 if ((self = [super init])) { |
| 30 weak_ = weak; |
| 31 } |
| 32 return self; |
| 33 } |
| 34 |
| 35 - (base::WeakPtr<views::CocoaWindowMoveLoop>&)weak { |
| 36 return weak_; |
| 37 } |
| 38 @end |
| 39 |
| 40 namespace views { |
| 41 |
| 42 CocoaWindowMoveLoop::CocoaWindowMoveLoop( |
| 43 BridgedNativeWidget* owner, |
| 44 const NSPoint& initial_mouse_in_screen) |
| 45 : owner_(owner), |
| 46 initial_mouse_in_screen_(initial_mouse_in_screen), |
| 47 weak_factory_(this) { |
| 48 } |
| 49 |
| 50 CocoaWindowMoveLoop::~CocoaWindowMoveLoop() { |
| 51 // Handle the pathological case, where |this| is destroyed while running. |
| 52 if (exit_reason_ref_) { |
| 53 *exit_reason_ref_ = WINDOW_DESTROYED; |
| 54 quit_closure_.Run(); |
| 55 } |
| 56 |
| 57 owner_ = nullptr; |
| 58 } |
| 59 |
| 60 Widget::MoveLoopResult CocoaWindowMoveLoop::Run() { |
| 61 LoopExitReason exit_reason = ENDED_EXTERNALLY; |
| 62 exit_reason_ref_ = &exit_reason; |
| 63 NSWindow* window = owner_->ns_window(); |
| 64 const NSRect initial_frame = [window frame]; |
| 65 |
| 66 base::RunLoop run_loop; |
| 67 quit_closure_ = run_loop.QuitClosure(); |
| 68 |
| 69 // Will be retained by the monitor handler block. |
| 70 WeakCocoaWindowMoveLoop* weak_cocoa_window_move_loop = |
| 71 [[[WeakCocoaWindowMoveLoop alloc] |
| 72 initWithWeakPtr:weak_factory_.GetWeakPtr()] autorelease]; |
| 73 |
| 74 // Esc keypress is handled by EscapeTracker, which is installed by |
| 75 // TabDragController. |
| 76 NSEventMask mask = NSLeftMouseUpMask | NSLeftMouseDraggedMask; |
| 77 auto handler = ^NSEvent*(NSEvent* event) { |
| 78 CocoaWindowMoveLoop* strong = [weak_cocoa_window_move_loop weak].get(); |
| 79 if (!strong || !strong->exit_reason_ref_) { |
| 80 // By this point CocoaWindowMoveLoop was deleted while processing this |
| 81 // same event, and this event monitor was not unregistered in time. See |
| 82 // the WeakCocoaWindowMoveLoop comment above. |
| 83 // Continue processing the event. |
| 84 return event; |
| 85 } |
| 86 |
| 87 if ([event type] == NSLeftMouseDragged) { |
| 88 const NSPoint mouse_in_screen = [NSEvent mouseLocation]; |
| 89 |
| 90 const NSRect ns_frame = NSOffsetRect( |
| 91 initial_frame, mouse_in_screen.x - initial_mouse_in_screen_.x, |
| 92 mouse_in_screen.y - initial_mouse_in_screen_.y); |
| 93 [window setFrame:ns_frame display:NO animate:NO]; |
| 94 |
| 95 return event; |
| 96 } |
| 97 |
| 98 DCHECK_EQ([event type], NSLeftMouseUp); |
| 99 *strong->exit_reason_ref_ = MOUSE_UP; |
| 100 strong->quit_closure_.Run(); |
| 101 return event; // Process the MouseUp. |
| 102 }; |
| 103 id monitor = |
| 104 [NSEvent addLocalMonitorForEventsMatchingMask:mask handler:handler]; |
| 105 |
| 106 run_loop.Run(); |
| 107 [NSEvent removeMonitor:monitor]; |
| 108 |
| 109 if (exit_reason != WINDOW_DESTROYED && exit_reason != ENDED_EXTERNALLY) { |
| 110 exit_reason_ref_ = nullptr; // Ensure End() doesn't replace the reason. |
| 111 owner_->EndMoveLoop(); // Deletes |this|. |
| 112 } |
| 113 |
| 114 return exit_reason != MOUSE_UP ? Widget::MOVE_LOOP_CANCELED |
| 115 : Widget::MOVE_LOOP_SUCCESSFUL; |
| 116 } |
| 117 |
| 118 void CocoaWindowMoveLoop::End() { |
| 119 if (exit_reason_ref_) { |
| 120 DCHECK_EQ(*exit_reason_ref_, ENDED_EXTERNALLY); |
| 121 // Ensure the destructor doesn't replace the reason. |
| 122 exit_reason_ref_ = nullptr; |
| 123 quit_closure_.Run(); |
| 124 } |
| 125 } |
| 126 |
| 127 } // namespace views |
OLD | NEW |