Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / ui / views / controls / menu / menu_controller.cc
1 // Copyright (c) 2012 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/controls/menu/menu_controller.h"
6
7 #if defined(OS_WIN)
8 #include <windowsx.h>
9 #endif
10
11 #include "base/i18n/case_conversion.h"
12 #include "base/i18n/rtl.h"
13 #include "base/run_loop.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/time/time.h"
16 #include "ui/aura/client/screen_position_client.h"
17 #include "ui/aura/env.h"
18 #include "ui/aura/window.h"
19 #include "ui/aura/window_event_dispatcher.h"
20 #include "ui/aura/window_tree_host.h"
21 #include "ui/base/dragdrop/drag_utils.h"
22 #include "ui/base/dragdrop/os_exchange_data.h"
23 #include "ui/base/l10n/l10n_util.h"
24 #include "ui/events/event.h"
25 #include "ui/events/event_utils.h"
26 #include "ui/events/platform/platform_event_source.h"
27 #include "ui/events/platform/scoped_event_dispatcher.h"
28 #include "ui/gfx/canvas.h"
29 #include "ui/gfx/native_widget_types.h"
30 #include "ui/gfx/point.h"
31 #include "ui/gfx/screen.h"
32 #include "ui/gfx/vector2d.h"
33 #include "ui/native_theme/native_theme.h"
34 #include "ui/views/controls/button/menu_button.h"
35 #include "ui/views/controls/menu/menu_config.h"
36 #include "ui/views/controls/menu/menu_controller_delegate.h"
37 #include "ui/views/controls/menu/menu_host_root_view.h"
38 #include "ui/views/controls/menu/menu_item_view.h"
39 #include "ui/views/controls/menu/menu_scroll_view_container.h"
40 #include "ui/views/controls/menu/submenu_view.h"
41 #include "ui/views/drag_utils.h"
42 #include "ui/views/focus/view_storage.h"
43 #include "ui/views/mouse_constants.h"
44 #include "ui/views/view.h"
45 #include "ui/views/view_constants.h"
46 #include "ui/views/views_delegate.h"
47 #include "ui/views/widget/root_view.h"
48 #include "ui/views/widget/tooltip_manager.h"
49 #include "ui/views/widget/widget.h"
50 #include "ui/wm/public/activation_change_observer.h"
51 #include "ui/wm/public/activation_client.h"
52 #include "ui/wm/public/dispatcher_client.h"
53 #include "ui/wm/public/drag_drop_client.h"
54
55 #if defined(OS_WIN)
56 #include "ui/base/win/internal_constants.h"
57 #include "ui/views/controls/menu/menu_message_pump_dispatcher_win.h"
58 #include "ui/views/win/hwnd_util.h"
59 #else
60 #include "ui/views/controls/menu/menu_event_dispatcher_linux.h"
61 #endif
62
63 using aura::client::ScreenPositionClient;
64 using base::Time;
65 using base::TimeDelta;
66 using ui::OSExchangeData;
67
68 // Period of the scroll timer (in milliseconds).
69 static const int kScrollTimerMS = 30;
70
71 // Amount of time from when the drop exits the menu and the menu is hidden.
72 static const int kCloseOnExitTime = 1200;
73
74 // If a context menu is invoked by touch, we shift the menu by this offset so
75 // that the finger does not obscure the menu.
76 static const int kCenteredContextMenuYOffset = -15;
77
78 namespace views {
79
80 namespace {
81
82 // When showing context menu on mouse down, the user might accidentally select
83 // the menu item on the subsequent mouse up. To prevent this, we add the
84 // following delay before the user is able to select an item.
85 static int menu_selection_hold_time_ms = kMinimumMsPressedToActivate;
86
87 // The spacing offset for the bubble tip.
88 const int kBubbleTipSizeLeftRight = 12;
89 const int kBubbleTipSizeTopBottom = 11;
90
91 // The maximum distance (in DIPS) that the mouse can be moved before it should
92 // trigger a mouse menu item activation (regardless of how long the menu has
93 // been showing).
94 const float kMaximumLengthMovedToActivate = 4.0f;
95
96 // Returns true if the mnemonic of |menu| matches key.
97 bool MatchesMnemonic(MenuItemView* menu, base::char16 key) {
98   return key != 0 && menu->GetMnemonic() == key;
99 }
100
101 // Returns true if |menu| doesn't have a mnemonic and first character of the its
102 // title is |key|.
103 bool TitleMatchesMnemonic(MenuItemView* menu, base::char16 key) {
104   if (menu->GetMnemonic())
105     return false;
106
107   base::string16 lower_title = base::i18n::ToLower(menu->title());
108   return !lower_title.empty() && lower_title[0] == key;
109 }
110
111 aura::Window* GetOwnerRootWindow(views::Widget* owner) {
112   return owner ? owner->GetNativeWindow()->GetRootWindow() : NULL;
113 }
114
115 // ActivationChangeObserverImpl is used to observe activation changes and close
116 // the menu. Additionally it listens for the root window to be destroyed and
117 // cancel the menu as well.
118 class ActivationChangeObserverImpl
119     : public aura::client::ActivationChangeObserver,
120       public aura::WindowObserver,
121       public ui::EventHandler {
122  public:
123   ActivationChangeObserverImpl(MenuController* controller, aura::Window* root)
124       : controller_(controller),
125         root_(root) {
126     aura::client::GetActivationClient(root_)->AddObserver(this);
127     root_->AddObserver(this);
128     root_->AddPreTargetHandler(this);
129   }
130
131   virtual ~ActivationChangeObserverImpl() {
132     Cleanup();
133   }
134
135   // aura::client::ActivationChangeObserver:
136   virtual void OnWindowActivated(aura::Window* gained_active,
137                                  aura::Window* lost_active) OVERRIDE {
138     if (!controller_->drag_in_progress())
139       controller_->CancelAll();
140   }
141
142   // aura::WindowObserver:
143   virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
144     Cleanup();
145   }
146
147   // ui::EventHandler:
148   virtual void OnCancelMode(ui::CancelModeEvent* event) OVERRIDE {
149     controller_->CancelAll();
150   }
151
152  private:
153   void Cleanup() {
154     if (!root_)
155       return;
156     // The ActivationClient may have been destroyed by the time we get here.
157     aura::client::ActivationClient* client =
158         aura::client::GetActivationClient(root_);
159     if (client)
160       client->RemoveObserver(this);
161     root_->RemovePreTargetHandler(this);
162     root_->RemoveObserver(this);
163     root_ = NULL;
164   }
165
166   MenuController* controller_;
167   aura::Window* root_;
168
169   DISALLOW_COPY_AND_ASSIGN(ActivationChangeObserverImpl);
170 };
171
172 }  // namespace
173
174 // Returns the first descendant of |view| that is hot tracked.
175 static CustomButton* GetFirstHotTrackedView(View* view) {
176   if (!view)
177     return NULL;
178   CustomButton* button = CustomButton::AsCustomButton(view);
179   if (button) {
180     if (button->IsHotTracked())
181       return button;
182   }
183
184   for (int i = 0; i < view->child_count(); ++i) {
185     CustomButton* hot_view = GetFirstHotTrackedView(view->child_at(i));
186     if (hot_view)
187       return hot_view;
188   }
189   return NULL;
190 }
191
192 // Recurses through the child views of |view| returning the first view starting
193 // at |start| that is focusable. A value of -1 for |start| indicates to start at
194 // the first view (if |forward| is false, iterating starts at the last view). If
195 // |forward| is true the children are considered first to last, otherwise last
196 // to first.
197 static View* GetFirstFocusableView(View* view, int start, bool forward) {
198   if (forward) {
199     for (int i = start == -1 ? 0 : start; i < view->child_count(); ++i) {
200       View* deepest = GetFirstFocusableView(view->child_at(i), -1, forward);
201       if (deepest)
202         return deepest;
203     }
204   } else {
205     for (int i = start == -1 ? view->child_count() - 1 : start; i >= 0; --i) {
206       View* deepest = GetFirstFocusableView(view->child_at(i), -1, forward);
207       if (deepest)
208         return deepest;
209     }
210   }
211   return view->IsFocusable() ? view : NULL;
212 }
213
214 // Returns the first child of |start| that is focusable.
215 static View* GetInitialFocusableView(View* start, bool forward) {
216   return GetFirstFocusableView(start, -1, forward);
217 }
218
219 // Returns the next view after |start_at| that is focusable. Returns NULL if
220 // there are no focusable children of |ancestor| after |start_at|.
221 static View* GetNextFocusableView(View* ancestor,
222                                   View* start_at,
223                                   bool forward) {
224   DCHECK(ancestor->Contains(start_at));
225   View* parent = start_at;
226   do {
227     View* new_parent = parent->parent();
228     int index = new_parent->GetIndexOf(parent);
229     index += forward ? 1 : -1;
230     if (forward || index != -1) {
231       View* next = GetFirstFocusableView(new_parent, index, forward);
232       if (next)
233         return next;
234     }
235     parent = new_parent;
236   } while (parent != ancestor);
237   return NULL;
238 }
239
240 // MenuScrollTask --------------------------------------------------------------
241
242 // MenuScrollTask is used when the SubmenuView does not all fit on screen and
243 // the mouse is over the scroll up/down buttons. MenuScrollTask schedules
244 // itself with a RepeatingTimer. When Run is invoked MenuScrollTask scrolls
245 // appropriately.
246
247 class MenuController::MenuScrollTask {
248  public:
249   MenuScrollTask() : submenu_(NULL), is_scrolling_up_(false), start_y_(0) {
250     pixels_per_second_ = MenuItemView::pref_menu_height() * 20;
251   }
252
253   void Update(const MenuController::MenuPart& part) {
254     if (!part.is_scroll()) {
255       StopScrolling();
256       return;
257     }
258     DCHECK(part.submenu);
259     SubmenuView* new_menu = part.submenu;
260     bool new_is_up = (part.type == MenuController::MenuPart::SCROLL_UP);
261     if (new_menu == submenu_ && is_scrolling_up_ == new_is_up)
262       return;
263
264     start_scroll_time_ = base::Time::Now();
265     start_y_ = part.submenu->GetVisibleBounds().y();
266     submenu_ = new_menu;
267     is_scrolling_up_ = new_is_up;
268
269     if (!scrolling_timer_.IsRunning()) {
270       scrolling_timer_.Start(FROM_HERE,
271                              TimeDelta::FromMilliseconds(kScrollTimerMS),
272                              this, &MenuScrollTask::Run);
273     }
274   }
275
276   void StopScrolling() {
277     if (scrolling_timer_.IsRunning()) {
278       scrolling_timer_.Stop();
279       submenu_ = NULL;
280     }
281   }
282
283   // The menu being scrolled. Returns null if not scrolling.
284   SubmenuView* submenu() const { return submenu_; }
285
286  private:
287   void Run() {
288     DCHECK(submenu_);
289     gfx::Rect vis_rect = submenu_->GetVisibleBounds();
290     const int delta_y = static_cast<int>(
291         (base::Time::Now() - start_scroll_time_).InMilliseconds() *
292         pixels_per_second_ / 1000);
293     vis_rect.set_y(is_scrolling_up_ ?
294         std::max(0, start_y_ - delta_y) :
295         std::min(submenu_->height() - vis_rect.height(), start_y_ + delta_y));
296     submenu_->ScrollRectToVisible(vis_rect);
297   }
298
299   // SubmenuView being scrolled.
300   SubmenuView* submenu_;
301
302   // Direction scrolling.
303   bool is_scrolling_up_;
304
305   // Timer to periodically scroll.
306   base::RepeatingTimer<MenuScrollTask> scrolling_timer_;
307
308   // Time we started scrolling at.
309   base::Time start_scroll_time_;
310
311   // How many pixels to scroll per second.
312   int pixels_per_second_;
313
314   // Y-coordinate of submenu_view_ when scrolling started.
315   int start_y_;
316
317   DISALLOW_COPY_AND_ASSIGN(MenuScrollTask);
318 };
319
320 // MenuController:SelectByCharDetails ----------------------------------------
321
322 struct MenuController::SelectByCharDetails {
323   SelectByCharDetails()
324       : first_match(-1),
325         has_multiple(false),
326         index_of_item(-1),
327         next_match(-1) {
328   }
329
330   // Index of the first menu with the specified mnemonic.
331   int first_match;
332
333   // If true there are multiple menu items with the same mnemonic.
334   bool has_multiple;
335
336   // Index of the selected item; may remain -1.
337   int index_of_item;
338
339   // If there are multiple matches this is the index of the item after the
340   // currently selected item whose mnemonic matches. This may remain -1 even
341   // though there are matches.
342   int next_match;
343 };
344
345 // MenuController:State ------------------------------------------------------
346
347 MenuController::State::State()
348     : item(NULL),
349       submenu_open(false),
350       anchor(MENU_ANCHOR_TOPLEFT),
351       context_menu(false) {
352 }
353
354 MenuController::State::~State() {}
355
356 // MenuController ------------------------------------------------------------
357
358 // static
359 MenuController* MenuController::active_instance_ = NULL;
360
361 // static
362 MenuController* MenuController::GetActiveInstance() {
363   return active_instance_;
364 }
365
366 MenuItemView* MenuController::Run(Widget* parent,
367                                   MenuButton* button,
368                                   MenuItemView* root,
369                                   const gfx::Rect& bounds,
370                                   MenuAnchorPosition position,
371                                   bool context_menu,
372                                   int* result_event_flags) {
373   exit_type_ = EXIT_NONE;
374   possible_drag_ = false;
375   drag_in_progress_ = false;
376   closing_event_time_ = base::TimeDelta();
377   menu_start_time_ = base::TimeTicks::Now();
378   menu_start_mouse_press_loc_ = gfx::Point();
379
380   // If we are shown on mouse press, we will eat the subsequent mouse down and
381   // the parent widget will not be able to reset its state (it might have mouse
382   // capture from the mouse down). So we clear its state here.
383   if (parent) {
384     View* root_view = parent->GetRootView();
385     if (root_view) {
386       root_view->SetMouseHandler(NULL);
387       const ui::Event* event =
388           static_cast<internal::RootView*>(root_view)->current_event();
389       if (event && event->type() == ui::ET_MOUSE_PRESSED) {
390         gfx::Point screen_loc(
391             static_cast<const ui::MouseEvent*>(event)->location());
392         View::ConvertPointToScreen(
393             static_cast<View*>(event->target()), &screen_loc);
394         menu_start_mouse_press_loc_ = screen_loc;
395       }
396     }
397   }
398
399   bool nested_menu = showing_;
400   if (showing_) {
401     // Only support nesting of blocking_run menus, nesting of
402     // blocking/non-blocking shouldn't be needed.
403     DCHECK(blocking_run_);
404
405     // We're already showing, push the current state.
406     menu_stack_.push_back(state_);
407
408     // The context menu should be owned by the same parent.
409     DCHECK_EQ(owner_, parent);
410   } else {
411     showing_ = true;
412   }
413
414   // Reset current state.
415   pending_state_ = State();
416   state_ = State();
417   UpdateInitialLocation(bounds, position, context_menu);
418
419   if (owner_)
420     owner_->RemoveObserver(this);
421   owner_ = parent;
422   if (owner_)
423     owner_->AddObserver(this);
424
425   // Set the selection, which opens the initial menu.
426   SetSelection(root, SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
427
428   if (!blocking_run_) {
429     // Start the timer to hide the menu. This is needed as we get no
430     // notification when the drag has finished.
431     StartCancelAllTimer();
432     return NULL;
433   }
434
435   if (button)
436     menu_button_ = button;
437
438   // Make sure Chrome doesn't attempt to shut down while the menu is showing.
439   if (ViewsDelegate::views_delegate)
440     ViewsDelegate::views_delegate->AddRef();
441
442   // We need to turn on nestable tasks as in some situations (pressing alt-f for
443   // one) the menus are run from a task. If we don't do this and are invoked
444   // from a task none of the tasks we schedule are processed and the menu
445   // appears totally broken.
446   message_loop_depth_++;
447   DCHECK_LE(message_loop_depth_, 2);
448   RunMessageLoop(nested_menu);
449   message_loop_depth_--;
450
451   if (ViewsDelegate::views_delegate)
452     ViewsDelegate::views_delegate->ReleaseRef();
453
454   // Close any open menus.
455   SetSelection(NULL, SELECTION_UPDATE_IMMEDIATELY | SELECTION_EXIT);
456
457 #if defined(OS_WIN)
458   // On Windows, if we select the menu item by touch and if the window at the
459   // location is another window on the same thread, that window gets a
460   // WM_MOUSEACTIVATE message and ends up activating itself, which is not
461   // correct. We workaround this by setting a property on the window at the
462   // current cursor location. We check for this property in our
463   // WM_MOUSEACTIVATE handler and don't activate the window if the property is
464   // set.
465   if (item_selected_by_touch_) {
466     item_selected_by_touch_ = false;
467     POINT cursor_pos;
468     ::GetCursorPos(&cursor_pos);
469      HWND window = ::WindowFromPoint(cursor_pos);
470      if (::GetWindowThreadProcessId(window, NULL) ==
471                                     ::GetCurrentThreadId()) {
472        ::SetProp(window, ui::kIgnoreTouchMouseActivateForWindow,
473                  reinterpret_cast<HANDLE>(true));
474      }
475   }
476 #endif
477
478   if (nested_menu) {
479     DCHECK(!menu_stack_.empty());
480     // We're running from within a menu, restore the previous state.
481     // The menus are already showing, so we don't have to show them.
482     state_ = menu_stack_.back();
483     pending_state_ = menu_stack_.back();
484     menu_stack_.pop_back();
485   } else {
486     showing_ = false;
487     did_capture_ = false;
488   }
489
490   MenuItemView* result = result_;
491   // In case we're nested, reset result_.
492   result_ = NULL;
493
494   if (result_event_flags)
495     *result_event_flags = accept_event_flags_;
496
497   if (exit_type_ == EXIT_OUTERMOST) {
498     SetExitType(EXIT_NONE);
499   } else {
500     if (nested_menu && result) {
501       // We're nested and about to return a value. The caller might enter
502       // another blocking loop. We need to make sure all menus are hidden
503       // before that happens otherwise the menus will stay on screen.
504       CloseAllNestedMenus();
505       SetSelection(NULL, SELECTION_UPDATE_IMMEDIATELY | SELECTION_EXIT);
506
507       // Set exit_all_, which makes sure all nested loops exit immediately.
508       if (exit_type_ != EXIT_DESTROYED)
509         SetExitType(EXIT_ALL);
510     }
511   }
512
513   // If we stopped running because one of the menus was destroyed chances are
514   // the button was also destroyed.
515   if (exit_type_ != EXIT_DESTROYED && menu_button_) {
516     menu_button_->SetState(CustomButton::STATE_NORMAL);
517     menu_button_->SchedulePaint();
518   }
519   return result;
520 }
521
522 void MenuController::Cancel(ExitType type) {
523   // If the menu has already been destroyed, no further cancellation is
524   // needed.  We especially don't want to set the |exit_type_| to a lesser
525   // value.
526   if (exit_type_ == EXIT_DESTROYED || exit_type_ == type)
527     return;
528
529   if (!showing_) {
530     // This occurs if we're in the process of notifying the delegate for a drop
531     // and the delegate cancels us.
532     return;
533   }
534
535   MenuItemView* selected = state_.item;
536   SetExitType(type);
537
538   SendMouseCaptureLostToActiveView();
539
540   // Hide windows immediately.
541   SetSelection(NULL, SELECTION_UPDATE_IMMEDIATELY | SELECTION_EXIT);
542
543   if (!blocking_run_) {
544     // If we didn't block the caller we need to notify the menu, which
545     // triggers deleting us.
546     DCHECK(selected);
547     showing_ = false;
548     delegate_->DropMenuClosed(
549         internal::MenuControllerDelegate::NOTIFY_DELEGATE,
550         selected->GetRootMenuItem());
551     // WARNING: the call to MenuClosed deletes us.
552     return;
553   }
554 }
555
556 void MenuController::OnMousePressed(SubmenuView* source,
557                                     const ui::MouseEvent& event) {
558   SetSelectionOnPointerDown(source, event);
559 }
560
561 void MenuController::OnMouseDragged(SubmenuView* source,
562                                     const ui::MouseEvent& event) {
563   MenuPart part = GetMenuPart(source, event.location());
564   UpdateScrolling(part);
565
566   if (!blocking_run_)
567     return;
568
569   if (possible_drag_) {
570     if (View::ExceededDragThreshold(event.location() - press_pt_))
571       StartDrag(source, press_pt_);
572     return;
573   }
574   MenuItemView* mouse_menu = NULL;
575   if (part.type == MenuPart::MENU_ITEM) {
576     if (!part.menu)
577       part.menu = source->GetMenuItem();
578     else
579       mouse_menu = part.menu;
580     SetSelection(part.menu ? part.menu : state_.item, SELECTION_OPEN_SUBMENU);
581   } else if (part.type == MenuPart::NONE) {
582     ShowSiblingMenu(source, event.location());
583   }
584   UpdateActiveMouseView(source, event, mouse_menu);
585 }
586
587 void MenuController::OnMouseReleased(SubmenuView* source,
588                                      const ui::MouseEvent& event) {
589   if (!blocking_run_)
590     return;
591
592   DCHECK(state_.item);
593   possible_drag_ = false;
594   DCHECK(blocking_run_);
595   MenuPart part = GetMenuPart(source, event.location());
596   if (event.IsRightMouseButton() && part.type == MenuPart::MENU_ITEM) {
597     MenuItemView* menu = part.menu;
598     // |menu| is NULL means this event is from an empty menu or a separator.
599     // If it is from an empty menu, use parent context menu instead of that.
600     if (menu == NULL &&
601         part.submenu->child_count() == 1 &&
602         part.submenu->child_at(0)->id() == MenuItemView::kEmptyMenuItemViewID) {
603       menu = part.parent;
604     }
605
606     if (menu != NULL && ShowContextMenu(menu, source, event,
607                                         ui::MENU_SOURCE_MOUSE))
608       return;
609   }
610
611   // We can use Ctrl+click or the middle mouse button to recursively open urls
612   // for selected folder menu items. If it's only a left click, show the
613   // contents of the folder.
614   if (!part.is_scroll() && part.menu &&
615       !(part.menu->HasSubmenu() &&
616         (event.flags() & ui::EF_LEFT_MOUSE_BUTTON))) {
617     if (GetActiveMouseView()) {
618       SendMouseReleaseToActiveView(source, event);
619       return;
620     }
621     // If a mouse release was received quickly after showing.
622     base::TimeDelta time_shown = base::TimeTicks::Now() - menu_start_time_;
623     if (time_shown.InMilliseconds() < menu_selection_hold_time_ms) {
624       // And it wasn't far from the mouse press location.
625       gfx::Point screen_loc(event.location());
626       View::ConvertPointToScreen(source->GetScrollViewContainer(), &screen_loc);
627       gfx::Vector2d moved = screen_loc - menu_start_mouse_press_loc_;
628       if (moved.Length() < kMaximumLengthMovedToActivate) {
629         // Ignore the mouse release as it was likely this menu was shown under
630         // the mouse and the action was just a normal click.
631         return;
632       }
633     }
634     if (part.menu->GetDelegate()->ShouldExecuteCommandWithoutClosingMenu(
635             part.menu->GetCommand(), event)) {
636       part.menu->GetDelegate()->ExecuteCommand(part.menu->GetCommand(),
637                                                event.flags());
638       return;
639     }
640     if (!part.menu->NonIconChildViewsCount() &&
641         part.menu->GetDelegate()->IsTriggerableEvent(part.menu, event)) {
642       base::TimeDelta shown_time = base::TimeTicks::Now() - menu_start_time_;
643       if (!state_.context_menu || !View::ShouldShowContextMenuOnMousePress() ||
644           shown_time.InMilliseconds() > menu_selection_hold_time_ms) {
645         Accept(part.menu, event.flags());
646       }
647       return;
648     }
649   } else if (part.type == MenuPart::MENU_ITEM) {
650     // User either clicked on empty space, or a menu that has children.
651     SetSelection(part.menu ? part.menu : state_.item,
652                  SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
653   }
654   SendMouseCaptureLostToActiveView();
655 }
656
657 void MenuController::OnMouseMoved(SubmenuView* source,
658                                   const ui::MouseEvent& event) {
659   HandleMouseLocation(source, event.location());
660 }
661
662 void MenuController::OnMouseEntered(SubmenuView* source,
663                                     const ui::MouseEvent& event) {
664   // MouseEntered is always followed by a mouse moved, so don't need to
665   // do anything here.
666 }
667
668 bool MenuController::OnMouseWheel(SubmenuView* source,
669                                   const ui::MouseWheelEvent& event) {
670   MenuPart part = GetMenuPart(source, event.location());
671   return part.submenu && part.submenu->OnMouseWheel(event);
672 }
673
674 void MenuController::OnGestureEvent(SubmenuView* source,
675                                     ui::GestureEvent* event) {
676   MenuPart part = GetMenuPart(source, event->location());
677   if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
678     SetSelectionOnPointerDown(source, *event);
679     event->StopPropagation();
680   } else if (event->type() == ui::ET_GESTURE_LONG_PRESS) {
681     if (part.type == MenuPart::MENU_ITEM && part.menu) {
682       if (ShowContextMenu(part.menu, source, *event, ui::MENU_SOURCE_TOUCH))
683         event->StopPropagation();
684     }
685   } else if (event->type() == ui::ET_GESTURE_TAP) {
686     if (!part.is_scroll() && part.menu &&
687         !(part.menu->HasSubmenu())) {
688       if (part.menu->GetDelegate()->IsTriggerableEvent(
689           part.menu, *event)) {
690         Accept(part.menu, event->flags());
691         item_selected_by_touch_ = true;
692       }
693       event->StopPropagation();
694     } else if (part.type == MenuPart::MENU_ITEM) {
695       // User either tapped on empty space, or a menu that has children.
696       SetSelection(part.menu ? part.menu : state_.item,
697                    SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
698       event->StopPropagation();
699     }
700   } else if (event->type() == ui::ET_GESTURE_TAP_CANCEL &&
701              part.menu &&
702              part.type == MenuPart::MENU_ITEM) {
703     // Move the selection to the parent menu so that the selection in the
704     // current menu is unset. Make sure the submenu remains open by sending the
705     // appropriate SetSelectionTypes flags.
706     SetSelection(part.menu->GetParentMenuItem(),
707         SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
708     event->StopPropagation();
709   }
710
711   if (event->stopped_propagation())
712       return;
713
714   if (!part.submenu)
715     return;
716   part.submenu->OnGestureEvent(event);
717 }
718
719 bool MenuController::GetDropFormats(
720       SubmenuView* source,
721       int* formats,
722       std::set<OSExchangeData::CustomFormat>* custom_formats) {
723   return source->GetMenuItem()->GetDelegate()->GetDropFormats(
724       source->GetMenuItem(), formats, custom_formats);
725 }
726
727 bool MenuController::AreDropTypesRequired(SubmenuView* source) {
728   return source->GetMenuItem()->GetDelegate()->AreDropTypesRequired(
729       source->GetMenuItem());
730 }
731
732 bool MenuController::CanDrop(SubmenuView* source, const OSExchangeData& data) {
733   return source->GetMenuItem()->GetDelegate()->CanDrop(source->GetMenuItem(),
734                                                        data);
735 }
736
737 void MenuController::OnDragEntered(SubmenuView* source,
738                                    const ui::DropTargetEvent& event) {
739   valid_drop_coordinates_ = false;
740 }
741
742 int MenuController::OnDragUpdated(SubmenuView* source,
743                                   const ui::DropTargetEvent& event) {
744   StopCancelAllTimer();
745
746   gfx::Point screen_loc(event.location());
747   View::ConvertPointToScreen(source, &screen_loc);
748   if (valid_drop_coordinates_ && screen_loc == drop_pt_)
749     return last_drop_operation_;
750   drop_pt_ = screen_loc;
751   valid_drop_coordinates_ = true;
752
753   MenuItemView* menu_item = GetMenuItemAt(source, event.x(), event.y());
754   bool over_empty_menu = false;
755   if (!menu_item) {
756     // See if we're over an empty menu.
757     menu_item = GetEmptyMenuItemAt(source, event.x(), event.y());
758     if (menu_item)
759       over_empty_menu = true;
760   }
761   MenuDelegate::DropPosition drop_position = MenuDelegate::DROP_NONE;
762   int drop_operation = ui::DragDropTypes::DRAG_NONE;
763   if (menu_item) {
764     gfx::Point menu_item_loc(event.location());
765     View::ConvertPointToTarget(source, menu_item, &menu_item_loc);
766     MenuItemView* query_menu_item;
767     if (!over_empty_menu) {
768       int menu_item_height = menu_item->height();
769       if (menu_item->HasSubmenu() &&
770           (menu_item_loc.y() > kDropBetweenPixels &&
771            menu_item_loc.y() < (menu_item_height - kDropBetweenPixels))) {
772         drop_position = MenuDelegate::DROP_ON;
773       } else {
774         drop_position = (menu_item_loc.y() < menu_item_height / 2) ?
775             MenuDelegate::DROP_BEFORE : MenuDelegate::DROP_AFTER;
776       }
777       query_menu_item = menu_item;
778     } else {
779       query_menu_item = menu_item->GetParentMenuItem();
780       drop_position = MenuDelegate::DROP_ON;
781     }
782     drop_operation = menu_item->GetDelegate()->GetDropOperation(
783         query_menu_item, event, &drop_position);
784
785     // If the menu has a submenu, schedule the submenu to open.
786     SetSelection(menu_item, menu_item->HasSubmenu() ? SELECTION_OPEN_SUBMENU :
787                  SELECTION_DEFAULT);
788
789     if (drop_position == MenuDelegate::DROP_NONE ||
790         drop_operation == ui::DragDropTypes::DRAG_NONE)
791       menu_item = NULL;
792   } else {
793     SetSelection(source->GetMenuItem(), SELECTION_OPEN_SUBMENU);
794   }
795   SetDropMenuItem(menu_item, drop_position);
796   last_drop_operation_ = drop_operation;
797   return drop_operation;
798 }
799
800 void MenuController::OnDragExited(SubmenuView* source) {
801   StartCancelAllTimer();
802
803   if (drop_target_) {
804     StopShowTimer();
805     SetDropMenuItem(NULL, MenuDelegate::DROP_NONE);
806   }
807 }
808
809 int MenuController::OnPerformDrop(SubmenuView* source,
810                                   const ui::DropTargetEvent& event) {
811   DCHECK(drop_target_);
812   // NOTE: the delegate may delete us after invoking OnPerformDrop, as such
813   // we don't call cancel here.
814
815   MenuItemView* item = state_.item;
816   DCHECK(item);
817
818   MenuItemView* drop_target = drop_target_;
819   MenuDelegate::DropPosition drop_position = drop_position_;
820
821   // Close all menus, including any nested menus.
822   SetSelection(NULL, SELECTION_UPDATE_IMMEDIATELY | SELECTION_EXIT);
823   CloseAllNestedMenus();
824
825   // Set state such that we exit.
826   showing_ = false;
827   SetExitType(EXIT_ALL);
828
829   // If over an empty menu item, drop occurs on the parent.
830   if (drop_target->id() == MenuItemView::kEmptyMenuItemViewID)
831     drop_target = drop_target->GetParentMenuItem();
832
833   if (!IsBlockingRun()) {
834     delegate_->DropMenuClosed(
835         internal::MenuControllerDelegate::DONT_NOTIFY_DELEGATE,
836         item->GetRootMenuItem());
837   }
838
839   // WARNING: the call to MenuClosed deletes us.
840
841   return drop_target->GetDelegate()->OnPerformDrop(
842       drop_target, drop_position, event);
843 }
844
845 void MenuController::OnDragEnteredScrollButton(SubmenuView* source,
846                                                bool is_up) {
847   MenuPart part;
848   part.type = is_up ? MenuPart::SCROLL_UP : MenuPart::SCROLL_DOWN;
849   part.submenu = source;
850   UpdateScrolling(part);
851
852   // Do this to force the selection to hide.
853   SetDropMenuItem(source->GetMenuItemAt(0), MenuDelegate::DROP_NONE);
854
855   StopCancelAllTimer();
856 }
857
858 void MenuController::OnDragExitedScrollButton(SubmenuView* source) {
859   StartCancelAllTimer();
860   SetDropMenuItem(NULL, MenuDelegate::DROP_NONE);
861   StopScrolling();
862 }
863
864 void MenuController::UpdateSubmenuSelection(SubmenuView* submenu) {
865   if (submenu->IsShowing()) {
866     gfx::Point point = GetScreen()->GetCursorScreenPoint();
867     const SubmenuView* root_submenu =
868         submenu->GetMenuItem()->GetRootMenuItem()->GetSubmenu();
869     View::ConvertPointFromScreen(
870         root_submenu->GetWidget()->GetRootView(), &point);
871     HandleMouseLocation(submenu, point);
872   }
873 }
874
875 void MenuController::OnWidgetDestroying(Widget* widget) {
876   DCHECK_EQ(owner_, widget);
877   owner_->RemoveObserver(this);
878   owner_ = NULL;
879 }
880
881 // static
882 void MenuController::TurnOffMenuSelectionHoldForTest() {
883   menu_selection_hold_time_ms = -1;
884 }
885
886 void MenuController::SetSelection(MenuItemView* menu_item,
887                                   int selection_types) {
888   size_t paths_differ_at = 0;
889   std::vector<MenuItemView*> current_path;
890   std::vector<MenuItemView*> new_path;
891   BuildPathsAndCalculateDiff(pending_state_.item, menu_item, &current_path,
892                              &new_path, &paths_differ_at);
893
894   size_t current_size = current_path.size();
895   size_t new_size = new_path.size();
896
897   bool pending_item_changed = pending_state_.item != menu_item;
898   if (pending_item_changed && pending_state_.item) {
899     CustomButton* button = GetFirstHotTrackedView(pending_state_.item);
900     if (button)
901       button->SetHotTracked(false);
902   }
903
904   // Notify the old path it isn't selected.
905   MenuDelegate* current_delegate =
906       current_path.empty() ? NULL : current_path.front()->GetDelegate();
907   for (size_t i = paths_differ_at; i < current_size; ++i) {
908     if (current_delegate &&
909         current_path[i]->GetType() == MenuItemView::SUBMENU) {
910       current_delegate->WillHideMenu(current_path[i]);
911     }
912     current_path[i]->SetSelected(false);
913   }
914
915   // Notify the new path it is selected.
916   for (size_t i = paths_differ_at; i < new_size; ++i) {
917     new_path[i]->ScrollRectToVisible(new_path[i]->GetLocalBounds());
918     new_path[i]->SetSelected(true);
919   }
920
921   if (menu_item && menu_item->GetDelegate())
922     menu_item->GetDelegate()->SelectionChanged(menu_item);
923
924   DCHECK(menu_item || (selection_types & SELECTION_EXIT) != 0);
925
926   pending_state_.item = menu_item;
927   pending_state_.submenu_open = (selection_types & SELECTION_OPEN_SUBMENU) != 0;
928
929   // Stop timers.
930   StopCancelAllTimer();
931   // Resets show timer only when pending menu item is changed.
932   if (pending_item_changed)
933     StopShowTimer();
934
935   if (selection_types & SELECTION_UPDATE_IMMEDIATELY)
936     CommitPendingSelection();
937   else if (pending_item_changed)
938     StartShowTimer();
939
940   // Notify an accessibility focus event on all menu items except for the root.
941   if (menu_item &&
942       (MenuDepth(menu_item) != 1 ||
943        menu_item->GetType() != MenuItemView::SUBMENU)) {
944     menu_item->NotifyAccessibilityEvent(
945         ui::AX_EVENT_FOCUS, true);
946   }
947 }
948
949 void MenuController::SetSelectionOnPointerDown(SubmenuView* source,
950                                                const ui::LocatedEvent& event) {
951   if (!blocking_run_)
952     return;
953
954   DCHECK(!GetActiveMouseView());
955
956   MenuPart part = GetMenuPart(source, event.location());
957   if (part.is_scroll())
958     return;  // Ignore presses on scroll buttons.
959
960   // When this menu is opened through a touch event, a simulated right-click
961   // is sent before the menu appears.  Ignore it.
962   if ((event.flags() & ui::EF_RIGHT_MOUSE_BUTTON) &&
963       (event.flags() & ui::EF_FROM_TOUCH))
964     return;
965
966   if (part.type == MenuPart::NONE ||
967       (part.type == MenuPart::MENU_ITEM && part.menu &&
968        part.menu->GetRootMenuItem() != state_.item->GetRootMenuItem())) {
969     // Remember the time when we repost the event. The owner can then use this
970     // to figure out if this menu was finished with the same click which is
971     // sent to it thereafter. Note that the time stamp front he event cannot be
972     // used since the reposting will set a new timestamp when the event gets
973     // processed. As such it is better to take the current time which will be
974     // closer to the time when it arrives again in the menu handler.
975     closing_event_time_ = ui::EventTimeForNow();
976
977     // Mouse wasn't pressed over any menu, or the active menu, cancel.
978
979 #if defined(OS_WIN)
980     // We're going to close and we own the mouse capture. We need to repost the
981     // mouse down, otherwise the window the user clicked on won't get the event.
982     RepostEvent(source, event);
983 #endif
984
985     // And close.
986     ExitType exit_type = EXIT_ALL;
987     if (!menu_stack_.empty()) {
988       // We're running nested menus. Only exit all if the mouse wasn't over one
989       // of the menus from the last run.
990       gfx::Point screen_loc(event.location());
991       View::ConvertPointToScreen(source->GetScrollViewContainer(), &screen_loc);
992       MenuPart last_part = GetMenuPartByScreenCoordinateUsingMenu(
993           menu_stack_.back().item, screen_loc);
994       if (last_part.type != MenuPart::NONE)
995         exit_type = EXIT_OUTERMOST;
996     }
997     Cancel(exit_type);
998
999 #if defined(OS_CHROMEOS)
1000     // We're going to exit the menu and want to repost the event so that is
1001     // is handled normally after the context menu has exited. We call
1002     // RepostEvent after Cancel so that mouse capture has been released so
1003     // that finding the event target is unaffected by the current capture.
1004     RepostEvent(source, event);
1005 #endif
1006     // Do not repost events for Linux Aura because this behavior is more
1007     // consistent with the behavior of other Linux apps.
1008     return;
1009   }
1010
1011   // On a press we immediately commit the selection, that way a submenu
1012   // pops up immediately rather than after a delay.
1013   int selection_types = SELECTION_UPDATE_IMMEDIATELY;
1014   if (!part.menu) {
1015     part.menu = part.parent;
1016     selection_types |= SELECTION_OPEN_SUBMENU;
1017   } else {
1018     if (part.menu->GetDelegate()->CanDrag(part.menu)) {
1019       possible_drag_ = true;
1020       press_pt_ = event.location();
1021     }
1022     if (part.menu->HasSubmenu())
1023       selection_types |= SELECTION_OPEN_SUBMENU;
1024   }
1025   SetSelection(part.menu, selection_types);
1026 }
1027
1028 void MenuController::StartDrag(SubmenuView* source,
1029                                const gfx::Point& location) {
1030   MenuItemView* item = state_.item;
1031   DCHECK(item);
1032   // Points are in the coordinates of the submenu, need to map to that of
1033   // the selected item. Additionally source may not be the parent of
1034   // the selected item, so need to map to screen first then to item.
1035   gfx::Point press_loc(location);
1036   View::ConvertPointToScreen(source->GetScrollViewContainer(), &press_loc);
1037   View::ConvertPointFromScreen(item, &press_loc);
1038   gfx::Point widget_loc(press_loc);
1039   View::ConvertPointToWidget(item, &widget_loc);
1040   scoped_ptr<gfx::Canvas> canvas(GetCanvasForDragImage(
1041       source->GetWidget(), gfx::Size(item->width(), item->height())));
1042   item->PaintButton(canvas.get(), MenuItemView::PB_FOR_DRAG);
1043
1044   OSExchangeData data;
1045   item->GetDelegate()->WriteDragData(item, &data);
1046   drag_utils::SetDragImageOnDataObject(*canvas, item->size(),
1047                                        press_loc.OffsetFromOrigin(),
1048                                        &data);
1049   StopScrolling();
1050   int drag_ops = item->GetDelegate()->GetDragOperations(item);
1051   drag_in_progress_ = true;
1052   // TODO(varunjain): Properly determine and send DRAG_EVENT_SOURCE below.
1053   item->GetWidget()->RunShellDrag(NULL, data, widget_loc, drag_ops,
1054       ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
1055   drag_in_progress_ = false;
1056
1057   if (GetActiveInstance() == this) {
1058     if (showing_) {
1059       // We're still showing, close all menus.
1060       CloseAllNestedMenus();
1061       Cancel(EXIT_ALL);
1062     }  // else case, drop was on us.
1063   }  // else case, someone canceled us, don't do anything
1064 }
1065
1066 bool MenuController::OnKeyDown(ui::KeyboardCode key_code) {
1067   DCHECK(blocking_run_);
1068
1069   switch (key_code) {
1070     case ui::VKEY_UP:
1071       IncrementSelection(-1);
1072       break;
1073
1074     case ui::VKEY_DOWN:
1075       IncrementSelection(1);
1076       break;
1077
1078     // Handling of VK_RIGHT and VK_LEFT is different depending on the UI
1079     // layout.
1080     case ui::VKEY_RIGHT:
1081       if (base::i18n::IsRTL())
1082         CloseSubmenu();
1083       else
1084         OpenSubmenuChangeSelectionIfCan();
1085       break;
1086
1087     case ui::VKEY_LEFT:
1088       if (base::i18n::IsRTL())
1089         OpenSubmenuChangeSelectionIfCan();
1090       else
1091         CloseSubmenu();
1092       break;
1093
1094     case ui::VKEY_SPACE:
1095       if (SendAcceleratorToHotTrackedView() == ACCELERATOR_PROCESSED_EXIT)
1096         return false;
1097       break;
1098
1099     case ui::VKEY_F4:
1100       if (!is_combobox_)
1101         break;
1102       // Fallthrough to accept on F4, so combobox menus match Windows behavior.
1103     case ui::VKEY_RETURN:
1104       if (pending_state_.item) {
1105         if (pending_state_.item->HasSubmenu()) {
1106           OpenSubmenuChangeSelectionIfCan();
1107         } else {
1108           SendAcceleratorResultType result = SendAcceleratorToHotTrackedView();
1109           if (result == ACCELERATOR_NOT_PROCESSED &&
1110               pending_state_.item->enabled()) {
1111             Accept(pending_state_.item, 0);
1112             return false;
1113           } else if (result == ACCELERATOR_PROCESSED_EXIT) {
1114             return false;
1115           }
1116         }
1117       }
1118       break;
1119
1120     case ui::VKEY_ESCAPE:
1121       if (!state_.item->GetParentMenuItem() ||
1122           (!state_.item->GetParentMenuItem()->GetParentMenuItem() &&
1123            (!state_.item->HasSubmenu() ||
1124             !state_.item->GetSubmenu()->IsShowing()))) {
1125         // User pressed escape and only one menu is shown, cancel it.
1126         Cancel(EXIT_OUTERMOST);
1127         return false;
1128       }
1129       CloseSubmenu();
1130       break;
1131
1132     default:
1133       break;
1134   }
1135   return true;
1136 }
1137
1138 MenuController::MenuController(ui::NativeTheme* theme,
1139                                bool blocking,
1140                                internal::MenuControllerDelegate* delegate)
1141     : blocking_run_(blocking),
1142       showing_(false),
1143       exit_type_(EXIT_NONE),
1144       did_capture_(false),
1145       result_(NULL),
1146       accept_event_flags_(0),
1147       drop_target_(NULL),
1148       drop_position_(MenuDelegate::DROP_UNKNOWN),
1149       owner_(NULL),
1150       possible_drag_(false),
1151       drag_in_progress_(false),
1152       valid_drop_coordinates_(false),
1153       last_drop_operation_(MenuDelegate::DROP_UNKNOWN),
1154       showing_submenu_(false),
1155       menu_button_(NULL),
1156       active_mouse_view_id_(ViewStorage::GetInstance()->CreateStorageID()),
1157       delegate_(delegate),
1158       message_loop_depth_(0),
1159       menu_config_(theme),
1160       closing_event_time_(base::TimeDelta()),
1161       menu_start_time_(base::TimeTicks()),
1162       is_combobox_(false),
1163       item_selected_by_touch_(false) {
1164   active_instance_ = this;
1165 }
1166
1167 MenuController::~MenuController() {
1168   DCHECK(!showing_);
1169   if (owner_)
1170     owner_->RemoveObserver(this);
1171   if (active_instance_ == this)
1172     active_instance_ = NULL;
1173   StopShowTimer();
1174   StopCancelAllTimer();
1175 }
1176
1177 #if defined(OS_WIN)
1178 void MenuController::RunMessageLoop(bool nested_menu) {
1179   internal::MenuMessagePumpDispatcher nested_dispatcher(this);
1180
1181   // |owner_| may be NULL.
1182   aura::Window* root = GetOwnerRootWindow(owner_);
1183   if (root) {
1184     scoped_ptr<ActivationChangeObserverImpl> observer;
1185     if (!nested_menu)
1186       observer.reset(new ActivationChangeObserverImpl(this, root));
1187     aura::client::GetDispatcherClient(root)
1188         ->RunWithDispatcher(&nested_dispatcher);
1189   } else {
1190     base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
1191     base::MessageLoop::ScopedNestableTaskAllower allow(loop);
1192     base::RunLoop run_loop(&nested_dispatcher);
1193     run_loop.Run();
1194   }
1195 }
1196 #else
1197 void MenuController::RunMessageLoop(bool nested_menu) {
1198   internal::MenuEventDispatcher event_dispatcher(this);
1199   scoped_ptr<ui::ScopedEventDispatcher> old_dispatcher =
1200       nested_dispatcher_.Pass();
1201   if (ui::PlatformEventSource::GetInstance()) {
1202     nested_dispatcher_ =
1203         ui::PlatformEventSource::GetInstance()->OverrideDispatcher(
1204             &event_dispatcher);
1205   }
1206   // |owner_| may be NULL.
1207   aura::Window* root = GetOwnerRootWindow(owner_);
1208   if (root) {
1209     scoped_ptr<ActivationChangeObserverImpl> observer;
1210     if (!nested_menu)
1211       observer.reset(new ActivationChangeObserverImpl(this, root));
1212     aura::client::GetDispatcherClient(root)->RunWithDispatcher(NULL);
1213   } else {
1214     base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
1215     base::MessageLoop::ScopedNestableTaskAllower allow(loop);
1216     base::RunLoop run_loop;
1217     run_loop.Run();
1218   }
1219   nested_dispatcher_ = old_dispatcher.Pass();
1220 }
1221 #endif
1222
1223 MenuController::SendAcceleratorResultType
1224     MenuController::SendAcceleratorToHotTrackedView() {
1225   CustomButton* hot_view = GetFirstHotTrackedView(pending_state_.item);
1226   if (!hot_view)
1227     return ACCELERATOR_NOT_PROCESSED;
1228
1229   ui::Accelerator accelerator(ui::VKEY_RETURN, ui::EF_NONE);
1230   hot_view->AcceleratorPressed(accelerator);
1231   CustomButton* button = static_cast<CustomButton*>(hot_view);
1232   button->SetHotTracked(true);
1233   return (exit_type_ == EXIT_NONE) ?
1234       ACCELERATOR_PROCESSED : ACCELERATOR_PROCESSED_EXIT;
1235 }
1236
1237 void MenuController::UpdateInitialLocation(const gfx::Rect& bounds,
1238                                            MenuAnchorPosition position,
1239                                            bool context_menu) {
1240   pending_state_.context_menu = context_menu;
1241   pending_state_.initial_bounds = bounds;
1242   if (bounds.height() > 1) {
1243     // Inset the bounds slightly, otherwise drag coordinates don't line up
1244     // nicely and menus close prematurely.
1245     pending_state_.initial_bounds.Inset(0, 1);
1246   }
1247
1248   // Reverse anchor position for RTL languages.
1249   if (base::i18n::IsRTL() &&
1250       (position == MENU_ANCHOR_TOPRIGHT || position == MENU_ANCHOR_TOPLEFT)) {
1251     pending_state_.anchor = position == MENU_ANCHOR_TOPRIGHT
1252                                 ? MENU_ANCHOR_TOPLEFT
1253                                 : MENU_ANCHOR_TOPRIGHT;
1254   } else {
1255     pending_state_.anchor = position;
1256   }
1257
1258   // Calculate the bounds of the monitor we'll show menus on. Do this once to
1259   // avoid repeated system queries for the info.
1260   pending_state_.monitor_bounds = GetScreen()->GetDisplayNearestPoint(
1261       bounds.origin()).work_area();
1262 #if defined(USE_ASH)
1263   if (!pending_state_.monitor_bounds.Contains(bounds)) {
1264     // Use the monitor area if the work area doesn't contain the bounds. This
1265     // handles showing a menu from the launcher.
1266     gfx::Rect monitor_area = GetScreen()->GetDisplayNearestPoint(
1267         bounds.origin()).bounds();
1268     if (monitor_area.Contains(bounds))
1269       pending_state_.monitor_bounds = monitor_area;
1270   }
1271 #endif
1272 }
1273
1274 void MenuController::Accept(MenuItemView* item, int event_flags) {
1275   DCHECK(IsBlockingRun());
1276   result_ = item;
1277   if (item && !menu_stack_.empty() &&
1278       !item->GetDelegate()->ShouldCloseAllMenusOnExecute(item->GetCommand())) {
1279     SetExitType(EXIT_OUTERMOST);
1280   } else {
1281     SetExitType(EXIT_ALL);
1282   }
1283   accept_event_flags_ = event_flags;
1284 }
1285
1286 bool MenuController::ShowSiblingMenu(SubmenuView* source,
1287                                      const gfx::Point& mouse_location) {
1288   if (!menu_stack_.empty() || !menu_button_)
1289     return false;
1290
1291   View* source_view = source->GetScrollViewContainer();
1292   if (mouse_location.x() >= 0 &&
1293       mouse_location.x() < source_view->width() &&
1294       mouse_location.y() >= 0 &&
1295       mouse_location.y() < source_view->height()) {
1296     // The mouse is over the menu, no need to continue.
1297     return false;
1298   }
1299
1300   gfx::NativeWindow window_under_mouse = GetScreen()->GetWindowUnderCursor();
1301   // TODO(oshima): Replace with views only API.
1302   if (!owner_ || window_under_mouse != owner_->GetNativeWindow())
1303     return false;
1304
1305   // The user moved the mouse outside the menu and over the owning window. See
1306   // if there is a sibling menu we should show.
1307   gfx::Point screen_point(mouse_location);
1308   View::ConvertPointToScreen(source_view, &screen_point);
1309   MenuAnchorPosition anchor;
1310   bool has_mnemonics;
1311   MenuButton* button = NULL;
1312   MenuItemView* alt_menu = source->GetMenuItem()->GetDelegate()->
1313       GetSiblingMenu(source->GetMenuItem()->GetRootMenuItem(),
1314                      screen_point, &anchor, &has_mnemonics, &button);
1315   if (!alt_menu || (state_.item && state_.item->GetRootMenuItem() == alt_menu))
1316     return false;
1317
1318   delegate_->SiblingMenuCreated(alt_menu);
1319
1320   if (!button) {
1321     // If the delegate returns a menu, they must also return a button.
1322     NOTREACHED();
1323     return false;
1324   }
1325
1326   // There is a sibling menu, update the button state, hide the current menu
1327   // and show the new one.
1328   menu_button_->SetState(CustomButton::STATE_NORMAL);
1329   menu_button_->SchedulePaint();
1330   menu_button_ = button;
1331   menu_button_->SetState(CustomButton::STATE_PRESSED);
1332   menu_button_->SchedulePaint();
1333
1334   // Need to reset capture when we show the menu again, otherwise we aren't
1335   // going to get any events.
1336   did_capture_ = false;
1337   gfx::Point screen_menu_loc;
1338   View::ConvertPointToScreen(button, &screen_menu_loc);
1339
1340   // It is currently not possible to show a submenu recursively in a bubble.
1341   DCHECK(!MenuItemView::IsBubble(anchor));
1342   // Subtract 1 from the height to make the popup flush with the button border.
1343   UpdateInitialLocation(gfx::Rect(screen_menu_loc.x(), screen_menu_loc.y(),
1344                                   button->width(), button->height() - 1),
1345                         anchor, state_.context_menu);
1346   alt_menu->PrepareForRun(
1347       false, has_mnemonics,
1348       source->GetMenuItem()->GetRootMenuItem()->show_mnemonics_);
1349   alt_menu->controller_ = this;
1350   SetSelection(alt_menu, SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
1351   return true;
1352 }
1353
1354 bool MenuController::ShowContextMenu(MenuItemView* menu_item,
1355                                      SubmenuView* source,
1356                                      const ui::LocatedEvent& event,
1357                                      ui::MenuSourceType source_type) {
1358   // Set the selection immediately, making sure the submenu is only open
1359   // if it already was.
1360   int selection_types = SELECTION_UPDATE_IMMEDIATELY;
1361   if (state_.item == pending_state_.item && state_.submenu_open)
1362     selection_types |= SELECTION_OPEN_SUBMENU;
1363   SetSelection(pending_state_.item, selection_types);
1364   gfx::Point loc(event.location());
1365   View::ConvertPointToScreen(source->GetScrollViewContainer(), &loc);
1366
1367   if (menu_item->GetDelegate()->ShowContextMenu(
1368           menu_item, menu_item->GetCommand(), loc, source_type)) {
1369     SendMouseCaptureLostToActiveView();
1370     return true;
1371   }
1372   return false;
1373 }
1374
1375 void MenuController::CloseAllNestedMenus() {
1376   for (std::list<State>::iterator i = menu_stack_.begin();
1377        i != menu_stack_.end(); ++i) {
1378     MenuItemView* last_item = i->item;
1379     for (MenuItemView* item = last_item; item;
1380          item = item->GetParentMenuItem()) {
1381       CloseMenu(item);
1382       last_item = item;
1383     }
1384     i->submenu_open = false;
1385     i->item = last_item;
1386   }
1387 }
1388
1389 MenuItemView* MenuController::GetMenuItemAt(View* source, int x, int y) {
1390   // Walk the view hierarchy until we find a menu item (or the root).
1391   View* child_under_mouse = source->GetEventHandlerForPoint(gfx::Point(x, y));
1392   while (child_under_mouse &&
1393          child_under_mouse->id() != MenuItemView::kMenuItemViewID) {
1394     child_under_mouse = child_under_mouse->parent();
1395   }
1396   if (child_under_mouse && child_under_mouse->enabled() &&
1397       child_under_mouse->id() == MenuItemView::kMenuItemViewID) {
1398     return static_cast<MenuItemView*>(child_under_mouse);
1399   }
1400   return NULL;
1401 }
1402
1403 MenuItemView* MenuController::GetEmptyMenuItemAt(View* source, int x, int y) {
1404   View* child_under_mouse = source->GetEventHandlerForPoint(gfx::Point(x, y));
1405   if (child_under_mouse &&
1406       child_under_mouse->id() == MenuItemView::kEmptyMenuItemViewID) {
1407     return static_cast<MenuItemView*>(child_under_mouse);
1408   }
1409   return NULL;
1410 }
1411
1412 bool MenuController::IsScrollButtonAt(SubmenuView* source,
1413                                       int x,
1414                                       int y,
1415                                       MenuPart::Type* part) {
1416   MenuScrollViewContainer* scroll_view = source->GetScrollViewContainer();
1417   View* child_under_mouse =
1418       scroll_view->GetEventHandlerForPoint(gfx::Point(x, y));
1419   if (child_under_mouse && child_under_mouse->enabled()) {
1420     if (child_under_mouse == scroll_view->scroll_up_button()) {
1421       *part = MenuPart::SCROLL_UP;
1422       return true;
1423     }
1424     if (child_under_mouse == scroll_view->scroll_down_button()) {
1425       *part = MenuPart::SCROLL_DOWN;
1426       return true;
1427     }
1428   }
1429   return false;
1430 }
1431
1432 MenuController::MenuPart MenuController::GetMenuPart(
1433     SubmenuView* source,
1434     const gfx::Point& source_loc) {
1435   gfx::Point screen_loc(source_loc);
1436   View::ConvertPointToScreen(source->GetScrollViewContainer(), &screen_loc);
1437   return GetMenuPartByScreenCoordinateUsingMenu(state_.item, screen_loc);
1438 }
1439
1440 MenuController::MenuPart MenuController::GetMenuPartByScreenCoordinateUsingMenu(
1441     MenuItemView* item,
1442     const gfx::Point& screen_loc) {
1443   MenuPart part;
1444   for (; item; item = item->GetParentMenuItem()) {
1445     if (item->HasSubmenu() && item->GetSubmenu()->IsShowing() &&
1446         GetMenuPartByScreenCoordinateImpl(item->GetSubmenu(), screen_loc,
1447                                           &part)) {
1448       return part;
1449     }
1450   }
1451   return part;
1452 }
1453
1454 bool MenuController::GetMenuPartByScreenCoordinateImpl(
1455     SubmenuView* menu,
1456     const gfx::Point& screen_loc,
1457     MenuPart* part) {
1458   // Is the mouse over the scroll buttons?
1459   gfx::Point scroll_view_loc = screen_loc;
1460   View* scroll_view_container = menu->GetScrollViewContainer();
1461   View::ConvertPointFromScreen(scroll_view_container, &scroll_view_loc);
1462   if (scroll_view_loc.x() < 0 ||
1463       scroll_view_loc.x() >= scroll_view_container->width() ||
1464       scroll_view_loc.y() < 0 ||
1465       scroll_view_loc.y() >= scroll_view_container->height()) {
1466     // Point isn't contained in menu.
1467     return false;
1468   }
1469   if (IsScrollButtonAt(menu, scroll_view_loc.x(), scroll_view_loc.y(),
1470                        &(part->type))) {
1471     part->submenu = menu;
1472     return true;
1473   }
1474
1475   // Not over the scroll button. Check the actual menu.
1476   if (DoesSubmenuContainLocation(menu, screen_loc)) {
1477     gfx::Point menu_loc = screen_loc;
1478     View::ConvertPointFromScreen(menu, &menu_loc);
1479     part->menu = GetMenuItemAt(menu, menu_loc.x(), menu_loc.y());
1480     part->type = MenuPart::MENU_ITEM;
1481     part->submenu = menu;
1482     if (!part->menu)
1483       part->parent = menu->GetMenuItem();
1484     return true;
1485   }
1486
1487   // While the mouse isn't over a menu item or the scroll buttons of menu, it
1488   // is contained by menu and so we return true. If we didn't return true other
1489   // menus would be searched, even though they are likely obscured by us.
1490   return true;
1491 }
1492
1493 bool MenuController::DoesSubmenuContainLocation(SubmenuView* submenu,
1494                                                 const gfx::Point& screen_loc) {
1495   gfx::Point view_loc = screen_loc;
1496   View::ConvertPointFromScreen(submenu, &view_loc);
1497   gfx::Rect vis_rect = submenu->GetVisibleBounds();
1498   return vis_rect.Contains(view_loc.x(), view_loc.y());
1499 }
1500
1501 void MenuController::CommitPendingSelection() {
1502   StopShowTimer();
1503
1504   size_t paths_differ_at = 0;
1505   std::vector<MenuItemView*> current_path;
1506   std::vector<MenuItemView*> new_path;
1507   BuildPathsAndCalculateDiff(state_.item, pending_state_.item, &current_path,
1508                              &new_path, &paths_differ_at);
1509
1510   // Hide the old menu.
1511   for (size_t i = paths_differ_at; i < current_path.size(); ++i) {
1512     if (current_path[i]->HasSubmenu()) {
1513       current_path[i]->GetSubmenu()->Hide();
1514     }
1515   }
1516
1517   // Copy pending to state_, making sure to preserve the direction menus were
1518   // opened.
1519   std::list<bool> pending_open_direction;
1520   state_.open_leading.swap(pending_open_direction);
1521   state_ = pending_state_;
1522   state_.open_leading.swap(pending_open_direction);
1523
1524   int menu_depth = MenuDepth(state_.item);
1525   if (menu_depth == 0) {
1526     state_.open_leading.clear();
1527   } else {
1528     int cached_size = static_cast<int>(state_.open_leading.size());
1529     DCHECK_GE(menu_depth, 0);
1530     while (cached_size-- >= menu_depth)
1531       state_.open_leading.pop_back();
1532   }
1533
1534   if (!state_.item) {
1535     // Nothing to select.
1536     StopScrolling();
1537     return;
1538   }
1539
1540   // Open all the submenus preceeding the last menu item (last menu item is
1541   // handled next).
1542   if (new_path.size() > 1) {
1543     for (std::vector<MenuItemView*>::iterator i = new_path.begin();
1544          i != new_path.end() - 1; ++i) {
1545       OpenMenu(*i);
1546     }
1547   }
1548
1549   if (state_.submenu_open) {
1550     // The submenu should be open, open the submenu if the item has a submenu.
1551     if (state_.item->HasSubmenu()) {
1552       OpenMenu(state_.item);
1553     } else {
1554       state_.submenu_open = false;
1555     }
1556   } else if (state_.item->HasSubmenu() &&
1557              state_.item->GetSubmenu()->IsShowing()) {
1558     state_.item->GetSubmenu()->Hide();
1559   }
1560
1561   if (scroll_task_.get() && scroll_task_->submenu()) {
1562     // Stop the scrolling if none of the elements of the selection contain
1563     // the menu being scrolled.
1564     bool found = false;
1565     for (MenuItemView* item = state_.item; item && !found;
1566          item = item->GetParentMenuItem()) {
1567       found = (item->HasSubmenu() && item->GetSubmenu()->IsShowing() &&
1568                item->GetSubmenu() == scroll_task_->submenu());
1569     }
1570     if (!found)
1571       StopScrolling();
1572   }
1573 }
1574
1575 void MenuController::CloseMenu(MenuItemView* item) {
1576   DCHECK(item);
1577   if (!item->HasSubmenu())
1578     return;
1579   item->GetSubmenu()->Hide();
1580 }
1581
1582 void MenuController::OpenMenu(MenuItemView* item) {
1583   DCHECK(item);
1584   if (item->GetSubmenu()->IsShowing()) {
1585     return;
1586   }
1587
1588   OpenMenuImpl(item, true);
1589   did_capture_ = true;
1590 }
1591
1592 void MenuController::OpenMenuImpl(MenuItemView* item, bool show) {
1593   // TODO(oshima|sky): Don't show the menu if drag is in progress and
1594   // this menu doesn't support drag drop. See crbug.com/110495.
1595   if (show) {
1596     int old_count = item->GetSubmenu()->child_count();
1597     item->GetDelegate()->WillShowMenu(item);
1598     if (old_count != item->GetSubmenu()->child_count()) {
1599       // If the number of children changed then we may need to add empty items.
1600       item->AddEmptyMenus();
1601     }
1602   }
1603   bool prefer_leading =
1604       state_.open_leading.empty() ? true : state_.open_leading.back();
1605   bool resulting_direction;
1606   gfx::Rect bounds = MenuItemView::IsBubble(state_.anchor) ?
1607       CalculateBubbleMenuBounds(item, prefer_leading, &resulting_direction) :
1608       CalculateMenuBounds(item, prefer_leading, &resulting_direction);
1609   state_.open_leading.push_back(resulting_direction);
1610   bool do_capture = (!did_capture_ && blocking_run_);
1611   showing_submenu_ = true;
1612   if (show) {
1613     // Menus are the only place using kGroupingPropertyKey, so any value (other
1614     // than 0) is fine.
1615     const int kGroupingId = 1001;
1616     item->GetSubmenu()->ShowAt(owner_, bounds, do_capture);
1617     item->GetSubmenu()->GetWidget()->SetNativeWindowProperty(
1618         TooltipManager::kGroupingPropertyKey,
1619         reinterpret_cast<void*>(kGroupingId));
1620   } else {
1621     item->GetSubmenu()->Reposition(bounds);
1622   }
1623   showing_submenu_ = false;
1624 }
1625
1626 void MenuController::MenuChildrenChanged(MenuItemView* item) {
1627   DCHECK(item);
1628   // Menu shouldn't be updated during drag operation.
1629   DCHECK(!GetActiveMouseView());
1630
1631   // If the current item or pending item is a descendant of the item
1632   // that changed, move the selection back to the changed item.
1633   const MenuItemView* ancestor = state_.item;
1634   while (ancestor && ancestor != item)
1635     ancestor = ancestor->GetParentMenuItem();
1636   if (!ancestor) {
1637     ancestor = pending_state_.item;
1638     while (ancestor && ancestor != item)
1639       ancestor = ancestor->GetParentMenuItem();
1640     if (!ancestor)
1641       return;
1642   }
1643   SetSelection(item, SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
1644   if (item->HasSubmenu())
1645     OpenMenuImpl(item, false);
1646 }
1647
1648 void MenuController::BuildPathsAndCalculateDiff(
1649     MenuItemView* old_item,
1650     MenuItemView* new_item,
1651     std::vector<MenuItemView*>* old_path,
1652     std::vector<MenuItemView*>* new_path,
1653     size_t* first_diff_at) {
1654   DCHECK(old_path && new_path && first_diff_at);
1655   BuildMenuItemPath(old_item, old_path);
1656   BuildMenuItemPath(new_item, new_path);
1657
1658   size_t common_size = std::min(old_path->size(), new_path->size());
1659
1660   // Find the first difference between the two paths, when the loop
1661   // returns, diff_i is the first index where the two paths differ.
1662   for (size_t i = 0; i < common_size; ++i) {
1663     if ((*old_path)[i] != (*new_path)[i]) {
1664       *first_diff_at = i;
1665       return;
1666     }
1667   }
1668
1669   *first_diff_at = common_size;
1670 }
1671
1672 void MenuController::BuildMenuItemPath(MenuItemView* item,
1673                                        std::vector<MenuItemView*>* path) {
1674   if (!item)
1675     return;
1676   BuildMenuItemPath(item->GetParentMenuItem(), path);
1677   path->push_back(item);
1678 }
1679
1680 void MenuController::StartShowTimer() {
1681   show_timer_.Start(FROM_HERE,
1682                     TimeDelta::FromMilliseconds(menu_config_.show_delay),
1683                     this, &MenuController::CommitPendingSelection);
1684 }
1685
1686 void MenuController::StopShowTimer() {
1687   show_timer_.Stop();
1688 }
1689
1690 void MenuController::StartCancelAllTimer() {
1691   cancel_all_timer_.Start(FROM_HERE,
1692                           TimeDelta::FromMilliseconds(kCloseOnExitTime),
1693                           this, &MenuController::CancelAll);
1694 }
1695
1696 void MenuController::StopCancelAllTimer() {
1697   cancel_all_timer_.Stop();
1698 }
1699
1700 gfx::Rect MenuController::CalculateMenuBounds(MenuItemView* item,
1701                                               bool prefer_leading,
1702                                               bool* is_leading) {
1703   DCHECK(item);
1704
1705   SubmenuView* submenu = item->GetSubmenu();
1706   DCHECK(submenu);
1707
1708   gfx::Size pref = submenu->GetScrollViewContainer()->GetPreferredSize();
1709
1710   // Don't let the menu go too wide.
1711   pref.set_width(std::min(pref.width(),
1712                             item->GetDelegate()->GetMaxWidthForMenu(item)));
1713   if (!state_.monitor_bounds.IsEmpty())
1714     pref.set_width(std::min(pref.width(), state_.monitor_bounds.width()));
1715
1716   // Assume we can honor prefer_leading.
1717   *is_leading = prefer_leading;
1718
1719   int x, y;
1720
1721   const MenuConfig& menu_config = item->GetMenuConfig();
1722
1723   if (!item->GetParentMenuItem()) {
1724     // First item, position relative to initial location.
1725     x = state_.initial_bounds.x();
1726
1727     // Offsets for context menu prevent menu items being selected by
1728     // simply opening the menu (bug 142992).
1729     if (menu_config.offset_context_menus && state_.context_menu)
1730       x += 1;
1731
1732     y = state_.initial_bounds.bottom();
1733     if (state_.anchor == MENU_ANCHOR_TOPRIGHT) {
1734       x = x + state_.initial_bounds.width() - pref.width();
1735       if (menu_config.offset_context_menus && state_.context_menu)
1736         x -= 1;
1737     } else if (state_.anchor == MENU_ANCHOR_BOTTOMCENTER) {
1738       x = x - (pref.width() - state_.initial_bounds.width()) / 2;
1739       if (pref.height() >
1740           state_.initial_bounds.y() + kCenteredContextMenuYOffset) {
1741         // Menu does not fit above the anchor. We move it to below.
1742         y = state_.initial_bounds.y() - kCenteredContextMenuYOffset;
1743       } else {
1744         y = std::max(0, state_.initial_bounds.y() - pref.height()) +
1745             kCenteredContextMenuYOffset;
1746       }
1747     }
1748
1749     if (!state_.monitor_bounds.IsEmpty() &&
1750         y + pref.height() > state_.monitor_bounds.bottom()) {
1751       // The menu doesn't fit fully below the button on the screen. The menu
1752       // position with respect to the bounds will be preserved if it has
1753       // already been drawn. When the requested positioning is below the bounds
1754       // it will shrink the menu to make it fit below.
1755       // If the requested positioning is best fit, it will first try to fit the
1756       // menu below. If that does not fit it will try to place it above. If
1757       // that will not fit it will place it at the bottom of the work area and
1758       // moving it off the initial_bounds region to avoid overlap.
1759       // In all other requested position styles it will be flipped above and
1760       // the height will be shrunken to the usable height.
1761       if (item->actual_menu_position() == MenuItemView::POSITION_BELOW_BOUNDS) {
1762         pref.set_height(std::min(pref.height(),
1763                                  state_.monitor_bounds.bottom() - y));
1764       } else if (item->actual_menu_position() ==
1765                  MenuItemView::POSITION_BEST_FIT) {
1766         MenuItemView::MenuPosition orientation =
1767             MenuItemView::POSITION_BELOW_BOUNDS;
1768         if (state_.monitor_bounds.height() < pref.height()) {
1769           // Handle very tall menus.
1770           pref.set_height(state_.monitor_bounds.height());
1771           y = state_.monitor_bounds.y();
1772         } else if (state_.monitor_bounds.y() + pref.height() <
1773             state_.initial_bounds.y()) {
1774           // Flipping upwards if there is enough space.
1775           y = state_.initial_bounds.y() - pref.height();
1776           orientation = MenuItemView::POSITION_ABOVE_BOUNDS;
1777         } else {
1778           // It is allowed to move the menu a bit around in order to get the
1779           // best fit and to avoid showing scroll elements.
1780           y = state_.monitor_bounds.bottom() - pref.height();
1781         }
1782         if (orientation == MenuItemView::POSITION_BELOW_BOUNDS) {
1783           // The menu should never overlap the owning button. So move it.
1784           // We use the anchor view style to determine the preferred position
1785           // relative to the owning button.
1786           if (state_.anchor == MENU_ANCHOR_TOPLEFT) {
1787             // The menu starts with the same x coordinate as the owning button.
1788             if (x + state_.initial_bounds.width() + pref.width() >
1789                 state_.monitor_bounds.right())
1790               x -= pref.width();  // Move the menu to the left of the button.
1791             else
1792               x += state_.initial_bounds.width(); // Move the menu right.
1793           } else {
1794             // The menu should end with the same x coordinate as the owning
1795             // button.
1796             if (state_.monitor_bounds.x() >
1797                 state_.initial_bounds.x() - pref.width())
1798               x = state_.initial_bounds.right();  // Move right of the button.
1799             else
1800               x = state_.initial_bounds.x() - pref.width(); // Move left.
1801           }
1802         }
1803         item->set_actual_menu_position(orientation);
1804       } else {
1805         pref.set_height(std::min(pref.height(),
1806             state_.initial_bounds.y() - state_.monitor_bounds.y()));
1807         y = state_.initial_bounds.y() - pref.height();
1808         item->set_actual_menu_position(MenuItemView::POSITION_ABOVE_BOUNDS);
1809       }
1810     } else if (item->actual_menu_position() ==
1811                MenuItemView::POSITION_ABOVE_BOUNDS) {
1812       pref.set_height(std::min(pref.height(),
1813           state_.initial_bounds.y() - state_.monitor_bounds.y()));
1814       y = state_.initial_bounds.y() - pref.height();
1815     } else {
1816       item->set_actual_menu_position(MenuItemView::POSITION_BELOW_BOUNDS);
1817     }
1818     if (state_.monitor_bounds.width() != 0 &&
1819         menu_config.offset_context_menus && state_.context_menu) {
1820       if (x + pref.width() > state_.monitor_bounds.right())
1821         x = state_.initial_bounds.x() - pref.width() - 1;
1822       if (x < state_.monitor_bounds.x())
1823         x = state_.monitor_bounds.x();
1824     }
1825   } else {
1826     // Not the first menu; position it relative to the bounds of the menu
1827     // item.
1828     gfx::Point item_loc;
1829     View::ConvertPointToScreen(item, &item_loc);
1830
1831     // We must make sure we take into account the UI layout. If the layout is
1832     // RTL, then a 'leading' menu is positioned to the left of the parent menu
1833     // item and not to the right.
1834     bool layout_is_rtl = base::i18n::IsRTL();
1835     bool create_on_the_right = (prefer_leading && !layout_is_rtl) ||
1836                                (!prefer_leading && layout_is_rtl);
1837     int submenu_horizontal_inset = menu_config.submenu_horizontal_inset;
1838
1839     if (create_on_the_right) {
1840       x = item_loc.x() + item->width() - submenu_horizontal_inset;
1841       if (state_.monitor_bounds.width() != 0 &&
1842           x + pref.width() > state_.monitor_bounds.right()) {
1843         if (layout_is_rtl)
1844           *is_leading = true;
1845         else
1846           *is_leading = false;
1847         x = item_loc.x() - pref.width() + submenu_horizontal_inset;
1848       }
1849     } else {
1850       x = item_loc.x() - pref.width() + submenu_horizontal_inset;
1851       if (state_.monitor_bounds.width() != 0 && x < state_.monitor_bounds.x()) {
1852         if (layout_is_rtl)
1853           *is_leading = false;
1854         else
1855           *is_leading = true;
1856         x = item_loc.x() + item->width() - submenu_horizontal_inset;
1857       }
1858     }
1859     y = item_loc.y() - menu_config.menu_vertical_border_size;
1860     if (state_.monitor_bounds.width() != 0) {
1861       pref.set_height(std::min(pref.height(), state_.monitor_bounds.height()));
1862       if (y + pref.height() > state_.monitor_bounds.bottom())
1863         y = state_.monitor_bounds.bottom() - pref.height();
1864       if (y < state_.monitor_bounds.y())
1865         y = state_.monitor_bounds.y();
1866     }
1867   }
1868
1869   if (state_.monitor_bounds.width() != 0) {
1870     if (x + pref.width() > state_.monitor_bounds.right())
1871       x = state_.monitor_bounds.right() - pref.width();
1872     if (x < state_.monitor_bounds.x())
1873       x = state_.monitor_bounds.x();
1874   }
1875   return gfx::Rect(x, y, pref.width(), pref.height());
1876 }
1877
1878 gfx::Rect MenuController::CalculateBubbleMenuBounds(MenuItemView* item,
1879                                                     bool prefer_leading,
1880                                                     bool* is_leading) {
1881   DCHECK(item);
1882   DCHECK(!item->GetParentMenuItem());
1883
1884   // Assume we can honor prefer_leading.
1885   *is_leading = prefer_leading;
1886
1887   SubmenuView* submenu = item->GetSubmenu();
1888   DCHECK(submenu);
1889
1890   gfx::Size pref = submenu->GetScrollViewContainer()->GetPreferredSize();
1891   const gfx::Rect& owner_bounds = pending_state_.initial_bounds;
1892
1893   // First the size gets reduced to the possible space.
1894   if (!state_.monitor_bounds.IsEmpty()) {
1895     int max_width = state_.monitor_bounds.width();
1896     int max_height = state_.monitor_bounds.height();
1897     // In case of bubbles, the maximum width is limited by the space
1898     // between the display corner and the target area + the tip size.
1899     if (state_.anchor == MENU_ANCHOR_BUBBLE_LEFT) {
1900       max_width = owner_bounds.x() - state_.monitor_bounds.x() +
1901                   kBubbleTipSizeLeftRight;
1902     } else if (state_.anchor == MENU_ANCHOR_BUBBLE_RIGHT) {
1903       max_width = state_.monitor_bounds.right() - owner_bounds.right() +
1904                   kBubbleTipSizeLeftRight;
1905     } else if (state_.anchor == MENU_ANCHOR_BUBBLE_ABOVE) {
1906       max_height = owner_bounds.y() - state_.monitor_bounds.y() +
1907                    kBubbleTipSizeTopBottom;
1908     } else if (state_.anchor == MENU_ANCHOR_BUBBLE_BELOW) {
1909       max_height = state_.monitor_bounds.bottom() - owner_bounds.bottom() +
1910                    kBubbleTipSizeTopBottom;
1911     }
1912     // The space for the menu to cover should never get empty.
1913     DCHECK_GE(max_width, kBubbleTipSizeLeftRight);
1914     DCHECK_GE(max_height, kBubbleTipSizeTopBottom);
1915     pref.set_width(std::min(pref.width(), max_width));
1916     pref.set_height(std::min(pref.height(), max_height));
1917   }
1918   // Also make sure that the menu does not go too wide.
1919   pref.set_width(std::min(pref.width(),
1920                           item->GetDelegate()->GetMaxWidthForMenu(item)));
1921
1922   int x, y;
1923   if (state_.anchor == MENU_ANCHOR_BUBBLE_ABOVE ||
1924       state_.anchor == MENU_ANCHOR_BUBBLE_BELOW) {
1925     if (state_.anchor == MENU_ANCHOR_BUBBLE_ABOVE)
1926       y = owner_bounds.y() - pref.height() + kBubbleTipSizeTopBottom;
1927     else
1928       y = owner_bounds.bottom() - kBubbleTipSizeTopBottom;
1929
1930     x = owner_bounds.CenterPoint().x() - pref.width() / 2;
1931     int x_old = x;
1932     if (x < state_.monitor_bounds.x()) {
1933       x = state_.monitor_bounds.x();
1934     } else if (x + pref.width() > state_.monitor_bounds.right()) {
1935       x = state_.monitor_bounds.right() - pref.width();
1936     }
1937     submenu->GetScrollViewContainer()->SetBubbleArrowOffset(
1938         pref.width() / 2 - x + x_old);
1939   } else {
1940     if (state_.anchor == MENU_ANCHOR_BUBBLE_RIGHT)
1941       x = owner_bounds.right() - kBubbleTipSizeLeftRight;
1942     else
1943       x = owner_bounds.x() - pref.width() + kBubbleTipSizeLeftRight;
1944
1945     y = owner_bounds.CenterPoint().y() - pref.height() / 2;
1946     int y_old = y;
1947     if (y < state_.monitor_bounds.y()) {
1948       y = state_.monitor_bounds.y();
1949     } else if (y + pref.height() > state_.monitor_bounds.bottom()) {
1950       y = state_.monitor_bounds.bottom() - pref.height();
1951     }
1952     submenu->GetScrollViewContainer()->SetBubbleArrowOffset(
1953         pref.height() / 2 - y + y_old);
1954   }
1955   return gfx::Rect(x, y, pref.width(), pref.height());
1956 }
1957
1958 // static
1959 int MenuController::MenuDepth(MenuItemView* item) {
1960   return item ? (MenuDepth(item->GetParentMenuItem()) + 1) : 0;
1961 }
1962
1963 void MenuController::IncrementSelection(int delta) {
1964   MenuItemView* item = pending_state_.item;
1965   DCHECK(item);
1966   if (pending_state_.submenu_open && item->HasSubmenu() &&
1967       item->GetSubmenu()->IsShowing()) {
1968     // A menu is selected and open, but none of its children are selected,
1969     // select the first menu item.
1970     if (item->GetSubmenu()->GetMenuItemCount()) {
1971       SetSelection(item->GetSubmenu()->GetMenuItemAt(0), SELECTION_DEFAULT);
1972       return;
1973     }
1974   }
1975
1976   if (item->has_children()) {
1977     CustomButton* button = GetFirstHotTrackedView(item);
1978     if (button) {
1979       button->SetHotTracked(false);
1980       View* to_make_hot = GetNextFocusableView(item, button, delta == 1);
1981       CustomButton* button_hot = CustomButton::AsCustomButton(to_make_hot);
1982       if (button_hot) {
1983         button_hot->SetHotTracked(true);
1984         return;
1985       }
1986     } else {
1987       View* to_make_hot = GetInitialFocusableView(item, delta == 1);
1988       CustomButton* button_hot = CustomButton::AsCustomButton(to_make_hot);
1989       if (button_hot) {
1990         button_hot->SetHotTracked(true);
1991         return;
1992       }
1993     }
1994   }
1995
1996   MenuItemView* parent = item->GetParentMenuItem();
1997   if (parent) {
1998     int parent_count = parent->GetSubmenu()->GetMenuItemCount();
1999     if (parent_count > 1) {
2000       for (int i = 0; i < parent_count; ++i) {
2001         if (parent->GetSubmenu()->GetMenuItemAt(i) == item) {
2002           MenuItemView* to_select =
2003               FindNextSelectableMenuItem(parent, i, delta);
2004           if (!to_select)
2005             break;
2006           SetSelection(to_select, SELECTION_DEFAULT);
2007           View* to_make_hot = GetInitialFocusableView(to_select, delta == 1);
2008           CustomButton* button_hot = CustomButton::AsCustomButton(to_make_hot);
2009           if (button_hot)
2010             button_hot->SetHotTracked(true);
2011           break;
2012         }
2013       }
2014     }
2015   }
2016 }
2017
2018 MenuItemView* MenuController::FindNextSelectableMenuItem(MenuItemView* parent,
2019                                                          int index,
2020                                                          int delta) {
2021   int start_index = index;
2022   int parent_count = parent->GetSubmenu()->GetMenuItemCount();
2023   // Loop through the menu items skipping any invisible menus. The loop stops
2024   // when we wrap or find a visible child.
2025   do {
2026     index = (index + delta + parent_count) % parent_count;
2027     if (index == start_index)
2028       return NULL;
2029     MenuItemView* child = parent->GetSubmenu()->GetMenuItemAt(index);
2030     if (child->visible())
2031       return child;
2032   } while (index != start_index);
2033   return NULL;
2034 }
2035
2036 void MenuController::OpenSubmenuChangeSelectionIfCan() {
2037   MenuItemView* item = pending_state_.item;
2038   if (item->HasSubmenu() && item->enabled()) {
2039     if (item->GetSubmenu()->GetMenuItemCount() > 0) {
2040       SetSelection(item->GetSubmenu()->GetMenuItemAt(0),
2041                    SELECTION_UPDATE_IMMEDIATELY);
2042     } else {
2043       // No menu items, just show the sub-menu.
2044       SetSelection(item, SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
2045     }
2046   }
2047 }
2048
2049 void MenuController::CloseSubmenu() {
2050   MenuItemView* item = state_.item;
2051   DCHECK(item);
2052   if (!item->GetParentMenuItem())
2053     return;
2054   if (item->HasSubmenu() && item->GetSubmenu()->IsShowing())
2055     SetSelection(item, SELECTION_UPDATE_IMMEDIATELY);
2056   else if (item->GetParentMenuItem()->GetParentMenuItem())
2057     SetSelection(item->GetParentMenuItem(), SELECTION_UPDATE_IMMEDIATELY);
2058 }
2059
2060 MenuController::SelectByCharDetails MenuController::FindChildForMnemonic(
2061     MenuItemView* parent,
2062     base::char16 key,
2063     bool (*match_function)(MenuItemView* menu, base::char16 mnemonic)) {
2064   SubmenuView* submenu = parent->GetSubmenu();
2065   DCHECK(submenu);
2066   SelectByCharDetails details;
2067
2068   for (int i = 0, menu_item_count = submenu->GetMenuItemCount();
2069        i < menu_item_count; ++i) {
2070     MenuItemView* child = submenu->GetMenuItemAt(i);
2071     if (child->enabled() && child->visible()) {
2072       if (child == pending_state_.item)
2073         details.index_of_item = i;
2074       if (match_function(child, key)) {
2075         if (details.first_match == -1)
2076           details.first_match = i;
2077         else
2078           details.has_multiple = true;
2079         if (details.next_match == -1 && details.index_of_item != -1 &&
2080             i > details.index_of_item)
2081           details.next_match = i;
2082       }
2083     }
2084   }
2085   return details;
2086 }
2087
2088 bool MenuController::AcceptOrSelect(MenuItemView* parent,
2089                                     const SelectByCharDetails& details) {
2090   // This should only be invoked if there is a match.
2091   DCHECK(details.first_match != -1);
2092   DCHECK(parent->HasSubmenu());
2093   SubmenuView* submenu = parent->GetSubmenu();
2094   DCHECK(submenu);
2095   if (!details.has_multiple) {
2096     // There's only one match, activate it (or open if it has a submenu).
2097     if (submenu->GetMenuItemAt(details.first_match)->HasSubmenu()) {
2098       SetSelection(submenu->GetMenuItemAt(details.first_match),
2099                    SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
2100     } else {
2101       Accept(submenu->GetMenuItemAt(details.first_match), 0);
2102       return true;
2103     }
2104   } else if (details.index_of_item == -1 || details.next_match == -1) {
2105     SetSelection(submenu->GetMenuItemAt(details.first_match),
2106                  SELECTION_DEFAULT);
2107   } else {
2108     SetSelection(submenu->GetMenuItemAt(details.next_match),
2109                  SELECTION_DEFAULT);
2110   }
2111   return false;
2112 }
2113
2114 bool MenuController::SelectByChar(base::char16 character) {
2115   base::char16 char_array[] = { character, 0 };
2116   base::char16 key = base::i18n::ToLower(char_array)[0];
2117   MenuItemView* item = pending_state_.item;
2118   if (!item->HasSubmenu() || !item->GetSubmenu()->IsShowing())
2119     item = item->GetParentMenuItem();
2120   DCHECK(item);
2121   DCHECK(item->HasSubmenu());
2122   DCHECK(item->GetSubmenu());
2123   if (item->GetSubmenu()->GetMenuItemCount() == 0)
2124     return false;
2125
2126   // Look for matches based on mnemonic first.
2127   SelectByCharDetails details =
2128       FindChildForMnemonic(item, key, &MatchesMnemonic);
2129   if (details.first_match != -1)
2130     return AcceptOrSelect(item, details);
2131
2132   if (is_combobox_) {
2133     item->GetSubmenu()->GetTextInputClient()->InsertChar(character, 0);
2134   } else {
2135     // If no mnemonics found, look at first character of titles.
2136     details = FindChildForMnemonic(item, key, &TitleMatchesMnemonic);
2137     if (details.first_match != -1)
2138       return AcceptOrSelect(item, details);
2139   }
2140
2141   return false;
2142 }
2143
2144 void MenuController::RepostEvent(SubmenuView* source,
2145                                  const ui::LocatedEvent& event) {
2146   if (!event.IsMouseEvent()) {
2147     // TODO(rbyers): Gesture event repost is tricky to get right
2148     // crbug.com/170987.
2149     DCHECK(event.IsGestureEvent());
2150     return;
2151   }
2152
2153 #if defined(OS_WIN)
2154   if (!state_.item) {
2155     // We some times get an event after closing all the menus. Ignore it. Make
2156     // sure the menu is in fact not visible. If the menu is visible, then
2157     // we're in a bad state where we think the menu isn't visibile but it is.
2158     DCHECK(!source->GetWidget()->IsVisible());
2159     return;
2160   }
2161
2162   state_.item->GetRootMenuItem()->GetSubmenu()->ReleaseCapture();
2163 #endif
2164
2165   gfx::Point screen_loc(event.location());
2166   View::ConvertPointToScreen(source->GetScrollViewContainer(), &screen_loc);
2167   gfx::NativeView native_view = source->GetWidget()->GetNativeView();
2168   if (!native_view)
2169     return;
2170
2171   gfx::Screen* screen = gfx::Screen::GetScreenFor(native_view);
2172   gfx::NativeWindow window = screen->GetWindowAtScreenPoint(screen_loc);
2173
2174 #if defined(OS_WIN)
2175   // PostMessage() to metro windows isn't allowed (access will be denied). Don't
2176   // try to repost with Win32 if the window under the mouse press is in metro.
2177   if (!ViewsDelegate::views_delegate ||
2178       !ViewsDelegate::views_delegate->IsWindowInMetro(window)) {
2179     HWND target_window = window ? HWNDForNativeWindow(window) :
2180                                   WindowFromPoint(screen_loc.ToPOINT());
2181     HWND source_window = HWNDForNativeView(native_view);
2182     if (!target_window || !source_window ||
2183         GetWindowThreadProcessId(source_window, NULL) !=
2184         GetWindowThreadProcessId(target_window, NULL)) {
2185       // Even though we have mouse capture, windows generates a mouse event if
2186       // the other window is in a separate thread. Only repost an event if
2187       // |target_window| and |source_window| were created on the same thread,
2188       // else double events can occur and lead to bad behavior.
2189       return;
2190     }
2191
2192     // Determine whether the click was in the client area or not.
2193     // NOTE: WM_NCHITTEST coordinates are relative to the screen.
2194     LPARAM coords = MAKELPARAM(screen_loc.x(), screen_loc.y());
2195     LRESULT nc_hit_result = SendMessage(target_window, WM_NCHITTEST, 0, coords);
2196     const bool client_area = nc_hit_result == HTCLIENT;
2197
2198     // TODO(sky): this isn't right. The event to generate should correspond with
2199     // the event we just got. MouseEvent only tells us what is down, which may
2200     // differ. Need to add ability to get changed button from MouseEvent.
2201     int event_type;
2202     int flags = event.flags();
2203     if (flags & ui::EF_LEFT_MOUSE_BUTTON) {
2204       event_type = client_area ? WM_LBUTTONDOWN : WM_NCLBUTTONDOWN;
2205     } else if (flags & ui::EF_MIDDLE_MOUSE_BUTTON) {
2206       event_type = client_area ? WM_MBUTTONDOWN : WM_NCMBUTTONDOWN;
2207     } else if (flags & ui::EF_RIGHT_MOUSE_BUTTON) {
2208       event_type = client_area ? WM_RBUTTONDOWN : WM_NCRBUTTONDOWN;
2209     } else {
2210       NOTREACHED();
2211       return;
2212     }
2213
2214     int window_x = screen_loc.x();
2215     int window_y = screen_loc.y();
2216     if (client_area) {
2217       POINT pt = { window_x, window_y };
2218       ScreenToClient(target_window, &pt);
2219       window_x = pt.x;
2220       window_y = pt.y;
2221     }
2222
2223     WPARAM target = client_area ? event.native_event().wParam : nc_hit_result;
2224     LPARAM window_coords = MAKELPARAM(window_x, window_y);
2225     PostMessage(target_window, event_type, target, window_coords);
2226     return;
2227   }
2228 #endif
2229   // Non-Windows Aura or |window| is in metro mode.
2230   if (!window)
2231     return;
2232
2233   aura::Window* root = window->GetRootWindow();
2234   ScreenPositionClient* spc = aura::client::GetScreenPositionClient(root);
2235   if (!spc)
2236     return;
2237
2238   gfx::Point root_loc(screen_loc);
2239   spc->ConvertPointFromScreen(root, &root_loc);
2240
2241   ui::MouseEvent clone(static_cast<const ui::MouseEvent&>(event));
2242   clone.set_location(root_loc);
2243   clone.set_root_location(root_loc);
2244   root->GetHost()->dispatcher()->RepostEvent(clone);
2245 }
2246
2247 void MenuController::SetDropMenuItem(
2248     MenuItemView* new_target,
2249     MenuDelegate::DropPosition new_position) {
2250   if (new_target == drop_target_ && new_position == drop_position_)
2251     return;
2252
2253   if (drop_target_) {
2254     drop_target_->GetParentMenuItem()->GetSubmenu()->SetDropMenuItem(
2255         NULL, MenuDelegate::DROP_NONE);
2256   }
2257   drop_target_ = new_target;
2258   drop_position_ = new_position;
2259   if (drop_target_) {
2260     drop_target_->GetParentMenuItem()->GetSubmenu()->SetDropMenuItem(
2261         drop_target_, drop_position_);
2262   }
2263 }
2264
2265 void MenuController::UpdateScrolling(const MenuPart& part) {
2266   if (!part.is_scroll() && !scroll_task_.get())
2267     return;
2268
2269   if (!scroll_task_.get())
2270     scroll_task_.reset(new MenuScrollTask());
2271   scroll_task_->Update(part);
2272 }
2273
2274 void MenuController::StopScrolling() {
2275   scroll_task_.reset(NULL);
2276 }
2277
2278 void MenuController::UpdateActiveMouseView(SubmenuView* event_source,
2279                                            const ui::MouseEvent& event,
2280                                            View* target_menu) {
2281   View* target = NULL;
2282   gfx::Point target_menu_loc(event.location());
2283   if (target_menu && target_menu->has_children()) {
2284     // Locate the deepest child view to send events to.  This code assumes we
2285     // don't have to walk up the tree to find a view interested in events. This
2286     // is currently true for the cases we are embedding views, but if we embed
2287     // more complex hierarchies it'll need to change.
2288     View::ConvertPointToScreen(event_source->GetScrollViewContainer(),
2289                                &target_menu_loc);
2290     View::ConvertPointFromScreen(target_menu, &target_menu_loc);
2291     target = target_menu->GetEventHandlerForPoint(target_menu_loc);
2292     if (target == target_menu || !target->enabled())
2293       target = NULL;
2294   }
2295   View* active_mouse_view = GetActiveMouseView();
2296   if (target != active_mouse_view) {
2297     SendMouseCaptureLostToActiveView();
2298     active_mouse_view = target;
2299     SetActiveMouseView(active_mouse_view);
2300     if (active_mouse_view) {
2301       gfx::Point target_point(target_menu_loc);
2302       View::ConvertPointToTarget(
2303           target_menu, active_mouse_view, &target_point);
2304       ui::MouseEvent mouse_entered_event(ui::ET_MOUSE_ENTERED,
2305                                          target_point, target_point,
2306                                          0, 0);
2307       active_mouse_view->OnMouseEntered(mouse_entered_event);
2308
2309       ui::MouseEvent mouse_pressed_event(ui::ET_MOUSE_PRESSED,
2310                                          target_point, target_point,
2311                                          event.flags(),
2312                                          event.changed_button_flags());
2313       active_mouse_view->OnMousePressed(mouse_pressed_event);
2314     }
2315   }
2316
2317   if (active_mouse_view) {
2318     gfx::Point target_point(target_menu_loc);
2319     View::ConvertPointToTarget(target_menu, active_mouse_view, &target_point);
2320     ui::MouseEvent mouse_dragged_event(ui::ET_MOUSE_DRAGGED,
2321                                        target_point, target_point,
2322                                        event.flags(),
2323                                        event.changed_button_flags());
2324     active_mouse_view->OnMouseDragged(mouse_dragged_event);
2325   }
2326 }
2327
2328 void MenuController::SendMouseReleaseToActiveView(SubmenuView* event_source,
2329                                                   const ui::MouseEvent& event) {
2330   View* active_mouse_view = GetActiveMouseView();
2331   if (!active_mouse_view)
2332     return;
2333
2334   gfx::Point target_loc(event.location());
2335   View::ConvertPointToScreen(event_source->GetScrollViewContainer(),
2336                              &target_loc);
2337   View::ConvertPointFromScreen(active_mouse_view, &target_loc);
2338   ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, target_loc, target_loc,
2339                                event.flags(), event.changed_button_flags());
2340   // Reset active mouse view before sending mouse released. That way if it calls
2341   // back to us, we aren't in a weird state.
2342   SetActiveMouseView(NULL);
2343   active_mouse_view->OnMouseReleased(release_event);
2344 }
2345
2346 void MenuController::SendMouseCaptureLostToActiveView() {
2347   View* active_mouse_view = GetActiveMouseView();
2348   if (!active_mouse_view)
2349     return;
2350
2351   // Reset the active_mouse_view_ before sending mouse capture lost. That way if
2352   // it calls back to us, we aren't in a weird state.
2353   SetActiveMouseView(NULL);
2354   active_mouse_view->OnMouseCaptureLost();
2355 }
2356
2357 void MenuController::SetActiveMouseView(View* view) {
2358   if (view)
2359     ViewStorage::GetInstance()->StoreView(active_mouse_view_id_, view);
2360   else
2361     ViewStorage::GetInstance()->RemoveView(active_mouse_view_id_);
2362 }
2363
2364 View* MenuController::GetActiveMouseView() {
2365   return ViewStorage::GetInstance()->RetrieveView(active_mouse_view_id_);
2366 }
2367
2368 void MenuController::SetExitType(ExitType type) {
2369   exit_type_ = type;
2370   // Exit nested message loops as soon as possible. We do this as
2371   // MessagePumpDispatcher is only invoked before native events, which means
2372   // its entirely possible for a Widget::CloseNow() task to be processed before
2373   // the next native message. We quite the nested message loop as soon as
2374   // possible to avoid having deleted views classes (such as widgets and
2375   // rootviews) on the stack when the nested message loop stops.
2376   //
2377   // It's safe to invoke QuitNestedMessageLoop() multiple times, it only effects
2378   // the current loop.
2379   bool quit_now = ShouldQuitNow() && exit_type_ != EXIT_NONE &&
2380       message_loop_depth_;
2381
2382   if (quit_now) {
2383     if (owner_) {
2384       aura::Window* root = owner_->GetNativeWindow()->GetRootWindow();
2385       aura::client::GetDispatcherClient(root)->QuitNestedMessageLoop();
2386     } else {
2387       base::MessageLoop::current()->QuitNow();
2388     }
2389     // Restore the previous dispatcher.
2390     nested_dispatcher_.reset();
2391   }
2392 }
2393
2394 bool MenuController::ShouldQuitNow() const {
2395   aura::Window* root = GetOwnerRootWindow(owner_);
2396   return !aura::client::GetDragDropClient(root) ||
2397          !aura::client::GetDragDropClient(root)->IsDragDropInProgress();
2398 }
2399
2400 void MenuController::HandleMouseLocation(SubmenuView* source,
2401                                          const gfx::Point& mouse_location) {
2402   if (showing_submenu_)
2403     return;
2404
2405   // Ignore mouse events if we're closing the menu.
2406   if (exit_type_ != EXIT_NONE)
2407     return;
2408
2409   MenuPart part = GetMenuPart(source, mouse_location);
2410
2411   UpdateScrolling(part);
2412
2413   if (!blocking_run_)
2414     return;
2415
2416   if (part.type == MenuPart::NONE && ShowSiblingMenu(source, mouse_location))
2417     return;
2418
2419   if (part.type == MenuPart::MENU_ITEM && part.menu) {
2420     SetSelection(part.menu, SELECTION_OPEN_SUBMENU);
2421   } else if (!part.is_scroll() && pending_state_.item &&
2422              pending_state_.item->GetParentMenuItem() &&
2423              (!pending_state_.item->HasSubmenu() ||
2424               !pending_state_.item->GetSubmenu()->IsShowing())) {
2425     // On exit if the user hasn't selected an item with a submenu, move the
2426     // selection back to the parent menu item.
2427     SetSelection(pending_state_.item->GetParentMenuItem(),
2428                  SELECTION_OPEN_SUBMENU);
2429   }
2430 }
2431
2432 gfx::Screen* MenuController::GetScreen() {
2433   aura::Window* root = GetOwnerRootWindow(owner_);
2434   return root ? gfx::Screen::GetScreenFor(root)
2435               : gfx::Screen::GetNativeScreen();
2436 }
2437
2438 }  // namespace views