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