Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(545)

Side by Side Diff: ui/views/controls/menu/menu_runner_impl_cocoa.mm

Issue 2863883002: Tracing for NSMenu timelines
Patch Set: big CL with more trace points Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ui/views/controls/menu/menu_runner_impl_cocoa.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import "ui/views/controls/menu/menu_runner_impl_cocoa.h" 5 #import "ui/views/controls/menu/menu_runner_impl_cocoa.h"
6 6
7 #import "base/mac/foundation_util.h"
7 #include "base/mac/sdk_forward_declarations.h" 8 #include "base/mac/sdk_forward_declarations.h"
9 #include "base/strings/sys_string_conversions.h"
8 #import "ui/base/cocoa/menu_controller.h" 10 #import "ui/base/cocoa/menu_controller.h"
9 #include "ui/base/models/menu_model.h" 11 #include "ui/base/models/menu_model.h"
10 #include "ui/events/base_event_utils.h" 12 #include "ui/events/base_event_utils.h"
11 #include "ui/events/event_utils.h" 13 #include "ui/events/event_utils.h"
12 #include "ui/gfx/geometry/rect.h" 14 #include "ui/gfx/geometry/rect.h"
13 #include "ui/gfx/mac/coordinate_conversion.h" 15 #include "ui/gfx/mac/coordinate_conversion.h"
16 #import "ui/views/cocoa/bridged_native_widget.h"
14 #include "ui/views/controls/menu/menu_runner_impl_adapter.h" 17 #include "ui/views/controls/menu/menu_runner_impl_adapter.h"
18 #include "ui/views/widget/native_widget_mac.h"
15 #include "ui/views/widget/widget.h" 19 #include "ui/views/widget/widget.h"
16 20
21 // Extension of base's MenuController to observe when menu items are activated.
22 @interface ViewsMenuController : MenuController
23 // Sets an NSWindow to pump compositor frames before returning from
24 // -[MenuController itemSelected:]. NSMenu blocks the UI thread until the NSMenu
25 // highlight and fade animation completes, so this allows the compositing work
26 // to be interleaved with the animation delay.
27 - (void)setWindowToUpdate:(NSWindow*)window;
28 @end
29
17 namespace views { 30 namespace views {
18 namespace internal { 31 namespace internal {
19 namespace { 32 namespace {
20 33
21 // The menu run types that should show a native NSMenu rather than a toolkit- 34 // The menu run types that should show a native NSMenu rather than a toolkit-
22 // views menu. Only supported when the menu is backed by a ui::MenuModel. 35 // views menu. Only supported when the menu is backed by a ui::MenuModel.
23 const int kNativeRunTypes = MenuRunner::CONTEXT_MENU | MenuRunner::COMBOBOX; 36 const int kNativeRunTypes = MenuRunner::CONTEXT_MENU | MenuRunner::COMBOBOX;
24 37
25 const CGFloat kNativeCheckmarkWidth = 18; 38 const CGFloat kNativeCheckmarkWidth = 18;
26 const CGFloat kNativeMenuItemHeight = 18; 39 const CGFloat kNativeMenuItemHeight = 18;
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
94 return new MenuRunnerImplAdapter(menu_model, on_menu_closed_callback); 107 return new MenuRunnerImplAdapter(menu_model, on_menu_closed_callback);
95 } 108 }
96 109
97 MenuRunnerImplCocoa::MenuRunnerImplCocoa( 110 MenuRunnerImplCocoa::MenuRunnerImplCocoa(
98 ui::MenuModel* menu, 111 ui::MenuModel* menu,
99 const base::Closure& on_menu_closed_callback) 112 const base::Closure& on_menu_closed_callback)
100 : running_(false), 113 : running_(false),
101 delete_after_run_(false), 114 delete_after_run_(false),
102 closing_event_time_(base::TimeTicks()), 115 closing_event_time_(base::TimeTicks()),
103 on_menu_closed_callback_(on_menu_closed_callback) { 116 on_menu_closed_callback_(on_menu_closed_callback) {
104 menu_controller_.reset( 117 menu_controller_.reset([[ViewsMenuController alloc] initWithModel:menu
105 [[MenuController alloc] initWithModel:menu useWithPopUpButtonCell:NO]); 118 useWithPopUpButtonCell:NO]);
106 } 119 }
107 120
108 bool MenuRunnerImplCocoa::IsRunning() const { 121 bool MenuRunnerImplCocoa::IsRunning() const {
109 return running_; 122 return running_;
110 } 123 }
111 124
112 void MenuRunnerImplCocoa::Release() { 125 void MenuRunnerImplCocoa::Release() {
113 if (IsRunning()) { 126 if (IsRunning()) {
114 if (delete_after_run_) 127 if (delete_after_run_)
115 return; // We already canceled. 128 return; // We already canceled.
116 129
117 delete_after_run_ = true; 130 delete_after_run_ = true;
118 [menu_controller_ cancel]; 131 [menu_controller_ cancel];
119 } else { 132 } else {
120 delete this; 133 delete this;
121 } 134 }
122 } 135 }
123 136
137 CGEventRef Callback(CGEventTapProxy proxy,
138 CGEventType type,
139 CGEventRef event,
140 void* userInfo) {
141 DLOG(INFO) << "event: " << type;
142 return NULL;
143 }
144
145 void MyRunLoopObserver(CFRunLoopObserverRef observer,
146 CFRunLoopActivity activity,
147 void* info) {
148 NSString* name = static_cast<NSString*>(info);
149 DLOG(INFO) << base::SysNSStringToUTF8(name) << " flags: " << activity;
150 }
151
152 class ScopedRunLoopSpy {
153 public:
154 ScopedRunLoopSpy() {
155 base::ScopedCFTypeRef<CFArrayRef> names(
156 CFRunLoopCopyAllModes(CFRunLoopGetCurrent()));
157 const CFIndex count = CFArrayGetCount(names);
158 for (CFIndex i = 0; i < count; ++i) {
159 base::scoped_nsobject<NSString> name(
160 base::mac::CFToNSCast(
161 static_cast<CFStringRef>(CFArrayGetValueAtIndex(names, i))),
162 base::scoped_policy::RETAIN);
163 NSLog(@"%@", name.get());
164 CFRunLoopObserverContext context = {0};
165 context.info = name;
166 CFRunLoopObserverRef observer = CFRunLoopObserverCreate(
167 NULL, kCFRunLoopAllActivities, YES, /* repeat */
168 0, &MyRunLoopObserver, &context);
169 modes[name] = observer;
170 CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer,
171 base::mac::NSToCFCast(name.get()));
172 }
173 }
174
175 ~ScopedRunLoopSpy() {
176 for (const auto& entry : modes) {
177 CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), entry.second,
178 base::mac::NSToCFCast(entry.first.get()));
179 CFRelease(entry.second);
180 }
181 }
182
183 private:
184 std::map<base::scoped_nsobject<NSString>, CFRunLoopObserverRef> modes;
185
186 DISALLOW_COPY_AND_ASSIGN(ScopedRunLoopSpy);
187 };
188
124 void MenuRunnerImplCocoa::RunMenuAt(Widget* parent, 189 void MenuRunnerImplCocoa::RunMenuAt(Widget* parent,
125 MenuButton* button, 190 MenuButton* button,
126 const gfx::Rect& bounds, 191 const gfx::Rect& bounds,
127 MenuAnchorPosition anchor, 192 MenuAnchorPosition anchor,
128 int32_t run_types) { 193 int32_t run_types) {
129 DCHECK(run_types & kNativeRunTypes); 194 DCHECK(run_types & kNativeRunTypes);
130 DCHECK(!IsRunning()); 195 DCHECK(!IsRunning());
131 DCHECK(parent); 196 DCHECK(parent);
132 closing_event_time_ = base::TimeTicks(); 197 closing_event_time_ = base::TimeTicks();
133 running_ = true; 198 running_ = true;
134 199
200 CGEventMask event_mask = CGEventMaskBit(kCGEventLeftMouseUp);
201 CFMachPortRef port =
202 CGEventTapCreate(kCGAnnotatedSessionEventTap, kCGHeadInsertEventTap,
203 kCGEventTapOptionListenOnly, // passive listener.
204 event_mask, &Callback, this);
205 auto* source = CFMachPortCreateRunLoopSource(NULL, port, 0);
206 CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopCommonModes);
207 CGEventTapEnable(port, true);
208
135 if (run_types & MenuRunner::CONTEXT_MENU) { 209 if (run_types & MenuRunner::CONTEXT_MENU) {
136 [NSMenu popUpContextMenu:[menu_controller_ menu] 210 [NSMenu popUpContextMenu:[menu_controller_ menu]
137 withEvent:[NSApp currentEvent] 211 withEvent:[NSApp currentEvent]
138 forView:parent->GetNativeView()]; 212 forView:parent->GetNativeView()];
139 } else if (run_types & MenuRunner::COMBOBOX) { 213 } else if (run_types & MenuRunner::COMBOBOX) {
214 [menu_controller_ setWindowToUpdate:parent->GetNativeWindow()];
140 NSMenuItem* checked_item = FirstCheckedItem(menu_controller_); 215 NSMenuItem* checked_item = FirstCheckedItem(menu_controller_);
141 base::scoped_nsobject<NSView> anchor_view( 216 base::scoped_nsobject<NSView> anchor_view(
142 CreateMenuAnchorView(parent->GetNativeWindow(), bounds, checked_item)); 217 CreateMenuAnchorView(parent->GetNativeWindow(), bounds, checked_item));
143 NSMenu* menu = [menu_controller_ menu]; 218 NSMenu* menu = [menu_controller_ menu];
144 [menu setMinimumWidth:bounds.width() + kNativeCheckmarkWidth]; 219 [menu setMinimumWidth:bounds.width() + kNativeCheckmarkWidth];
220 ScopedRunLoopSpy spy;
145 [menu popUpMenuPositioningItem:checked_item 221 [menu popUpMenuPositioningItem:checked_item
146 atLocation:NSZeroPoint 222 atLocation:NSZeroPoint
147 inView:anchor_view]; 223 inView:anchor_view];
224 DLOG(INFO) << "method return";
148 [anchor_view removeFromSuperview]; 225 [anchor_view removeFromSuperview];
149 } else { 226 } else {
150 NOTREACHED(); 227 NOTREACHED();
151 } 228 }
152 229
230 CGEventTapEnable(port, false);
231 CFRelease(source);
232 CFRelease(port);
233
153 closing_event_time_ = ui::EventTimeForNow(); 234 closing_event_time_ = ui::EventTimeForNow();
154 running_ = false; 235 running_ = false;
155 236
156 if (delete_after_run_) { 237 if (delete_after_run_) {
157 delete this; 238 delete this;
158 return; 239 return;
159 } 240 }
160 241
161 // Don't invoke the callback if Release() was called, since that usually means 242 // Don't invoke the callback if Release() was called, since that usually means
162 // the owning instance is being destroyed. 243 // the owning instance is being destroyed.
163 if (!on_menu_closed_callback_.is_null()) 244 if (!on_menu_closed_callback_.is_null())
164 on_menu_closed_callback_.Run(); 245 on_menu_closed_callback_.Run();
165 } 246 }
166 247
167 void MenuRunnerImplCocoa::Cancel() { 248 void MenuRunnerImplCocoa::Cancel() {
168 [menu_controller_ cancel]; 249 [menu_controller_ cancel];
169 } 250 }
170 251
171 base::TimeTicks MenuRunnerImplCocoa::GetClosingEventTime() const { 252 base::TimeTicks MenuRunnerImplCocoa::GetClosingEventTime() const {
172 return closing_event_time_; 253 return closing_event_time_;
173 } 254 }
174 255
175 MenuRunnerImplCocoa::~MenuRunnerImplCocoa() { 256 MenuRunnerImplCocoa::~MenuRunnerImplCocoa() {
176 } 257 }
177 258
178 } // namespace internal 259 } // namespace internal
179 } // namespace views 260 } // namespace views
261
262 @implementation ViewsMenuController {
263 base::scoped_nsobject<NSWindow> windowToUpdate_;
264 BOOL compositorPumped_;
265 }
266
267 - (void)setWindowToUpdate:(NSWindow*)window {
268 windowToUpdate_.reset(window, base::scoped_policy::RETAIN);
269 }
270
271 - (views::BridgedNativeWidget*)widget {
272 return windowToUpdate_
273 ? views::NativeWidgetMac::GetBridgeForNativeWindow(windowToUpdate_)
274 : nullptr;
275 }
276
277 // MenuController overrides.
278
279 - (BOOL)processItemSelectedEarly:(id)sender {
280 // Items that end in "..." indicate that a new window will be opened. Process
281 // those "late" so the windows transition at the correct times.
282 if ([[sender title] hasSuffix:@"…"])
283 return NO;
284
285 [self itemSelected:sender];
286 if (views::BridgedNativeWidget* nativeWidget = [self widget])
287 nativeWidget->PumpCompositor();
288 return YES;
289 }
290
291 @end
OLDNEW
« no previous file with comments | « ui/views/controls/menu/menu_runner_impl_cocoa.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698