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