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 |