Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / ui / views / controls / menu / menu_controller.cc
index 5d58c69..f5d28ef 100644 (file)
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
+#include "ui/aura/client/screen_position_client.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
+#include "ui/aura/window_tree_host.h"
 #include "ui/base/dragdrop/drag_utils.h"
 #include "ui/base/dragdrop/os_exchange_data.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/events/event_constants.h"
+#include "ui/events/event.h"
 #include "ui/events/event_utils.h"
-#include "ui/events/keycodes/keyboard_code_conversion.h"
-#include "ui/events/keycodes/keyboard_codes.h"
+#include "ui/events/platform/platform_event_source.h"
+#include "ui/events/platform/scoped_event_dispatcher.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/point.h"
 #include "ui/gfx/screen.h"
 #include "ui/gfx/vector2d.h"
 #include "ui/native_theme/native_theme.h"
 #include "ui/views/controls/menu/menu_config.h"
 #include "ui/views/controls/menu/menu_controller_delegate.h"
 #include "ui/views/controls/menu/menu_host_root_view.h"
+#include "ui/views/controls/menu/menu_item_view.h"
 #include "ui/views/controls/menu/menu_scroll_view_container.h"
 #include "ui/views/controls/menu/submenu_view.h"
 #include "ui/views/drag_utils.h"
-#include "ui/views/event_utils.h"
 #include "ui/views/focus/view_storage.h"
 #include "ui/views/mouse_constants.h"
+#include "ui/views/view.h"
 #include "ui/views/view_constants.h"
 #include "ui/views/views_delegate.h"
 #include "ui/views/widget/root_view.h"
 #include "ui/views/widget/tooltip_manager.h"
 #include "ui/views/widget/widget.h"
+#include "ui/wm/public/activation_change_observer.h"
+#include "ui/wm/public/activation_client.h"
 #include "ui/wm/public/dispatcher_client.h"
+#include "ui/wm/public/drag_drop_client.h"
 
 #if defined(OS_WIN)
 #include "ui/base/win/internal_constants.h"
+#include "ui/views/controls/menu/menu_message_pump_dispatcher_win.h"
 #include "ui/views/win/hwnd_util.h"
+#else
+#include "ui/views/controls/menu/menu_event_dispatcher_linux.h"
 #endif
 
-#if defined(USE_X11)
-#include <X11/Xlib.h>
-#endif
-
+using aura::client::ScreenPositionClient;
 using base::Time;
 using base::TimeDelta;
 using ui::OSExchangeData;
@@ -101,6 +108,67 @@ bool TitleMatchesMnemonic(MenuItemView* menu, base::char16 key) {
   return !lower_title.empty() && lower_title[0] == key;
 }
 
+aura::Window* GetOwnerRootWindow(views::Widget* owner) {
+  return owner ? owner->GetNativeWindow()->GetRootWindow() : NULL;
+}
+
+// ActivationChangeObserverImpl is used to observe activation changes and close
+// the menu. Additionally it listens for the root window to be destroyed and
+// cancel the menu as well.
+class ActivationChangeObserverImpl
+    : public aura::client::ActivationChangeObserver,
+      public aura::WindowObserver,
+      public ui::EventHandler {
+ public:
+  ActivationChangeObserverImpl(MenuController* controller, aura::Window* root)
+      : controller_(controller),
+        root_(root) {
+    aura::client::GetActivationClient(root_)->AddObserver(this);
+    root_->AddObserver(this);
+    root_->AddPreTargetHandler(this);
+  }
+
+  virtual ~ActivationChangeObserverImpl() {
+    Cleanup();
+  }
+
+  // aura::client::ActivationChangeObserver:
+  virtual void OnWindowActivated(aura::Window* gained_active,
+                                 aura::Window* lost_active) OVERRIDE {
+    if (!controller_->drag_in_progress())
+      controller_->CancelAll();
+  }
+
+  // aura::WindowObserver:
+  virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
+    Cleanup();
+  }
+
+  // ui::EventHandler:
+  virtual void OnCancelMode(ui::CancelModeEvent* event) OVERRIDE {
+    controller_->CancelAll();
+  }
+
+ private:
+  void Cleanup() {
+    if (!root_)
+      return;
+    // The ActivationClient may have been destroyed by the time we get here.
+    aura::client::ActivationClient* client =
+        aura::client::GetActivationClient(root_);
+    if (client)
+      client->RemoveObserver(this);
+    root_->RemovePreTargetHandler(this);
+    root_->RemoveObserver(this);
+    root_ = NULL;
+  }
+
+  MenuController* controller_;
+  aura::Window* root_;
+
+  DISALLOW_COPY_AND_ASSIGN(ActivationChangeObserverImpl);
+};
+
 }  // namespace
 
 // Returns the first descendant of |view| that is hot tracked.
@@ -279,8 +347,9 @@ struct MenuController::SelectByCharDetails {
 MenuController::State::State()
     : item(NULL),
       submenu_open(false),
-      anchor(MenuItemView::TOPLEFT),
-      context_menu(false) {}
+      anchor(MENU_ANCHOR_TOPLEFT),
+      context_menu(false) {
+}
 
 MenuController::State::~State() {}
 
@@ -298,7 +367,7 @@ MenuItemView* MenuController::Run(Widget* parent,
                                   MenuButton* button,
                                   MenuItemView* root,
                                   const gfx::Rect& bounds,
-                                  MenuItemView::AnchorPosition position,
+                                  MenuAnchorPosition position,
                                   bool context_menu,
                                   int* result_event_flags) {
   exit_type_ = EXIT_NONE;
@@ -910,14 +979,7 @@ void MenuController::SetSelectionOnPointerDown(SubmenuView* source,
 #if defined(OS_WIN)
     // We're going to close and we own the mouse capture. We need to repost the
     // mouse down, otherwise the window the user clicked on won't get the event.
-    if (!state_.item) {
-      // We some times get an event after closing all the menus. Ignore it. Make
-      // sure the menu is in fact not visible. If the menu is visible, then
-      // we're in a bad state where we think the menu isn't visibile but it is.
-      DCHECK(!source->GetWidget()->IsVisible());
-    } else {
-      RepostEvent(source, event);
-    }
+    RepostEvent(source, event);
 #endif
 
     // And close.
@@ -1001,94 +1063,6 @@ void MenuController::StartDrag(SubmenuView* source,
   }  // else case, someone canceled us, don't do anything
 }
 
-#if defined(OS_WIN)
-uint32_t MenuController::Dispatch(const MSG& msg) {
-  DCHECK(blocking_run_);
-
-  if (exit_type_ == EXIT_ALL || exit_type_ == EXIT_DESTROYED)
-    return (POST_DISPATCH_QUIT_LOOP | POST_DISPATCH_PERFORM_DEFAULT);
-
-  // NOTE: we don't get WM_ACTIVATE or anything else interesting in here.
-  switch (msg.message) {
-    case WM_CONTEXTMENU: {
-      MenuItemView* item = pending_state_.item;
-      if (item && item->GetRootMenuItem() != item) {
-        gfx::Point screen_loc(0, item->height());
-        View::ConvertPointToScreen(item, &screen_loc);
-        ui::MenuSourceType source_type = ui::MENU_SOURCE_MOUSE;
-        if (GET_X_LPARAM(msg.lParam) == -1 && GET_Y_LPARAM(msg.lParam) == -1)
-          source_type = ui::MENU_SOURCE_KEYBOARD;
-        item->GetDelegate()->ShowContextMenu(item, item->GetCommand(),
-                                             screen_loc, source_type);
-      }
-      return POST_DISPATCH_NONE;
-    }
-
-    // NOTE: focus wasn't changed when the menu was shown. As such, don't
-    // dispatch key events otherwise the focused window will get the events.
-    case WM_KEYDOWN: {
-      bool result = OnKeyDown(ui::KeyboardCodeFromNative(msg));
-      TranslateMessage(&msg);
-      return result ? POST_DISPATCH_NONE : POST_DISPATCH_QUIT_LOOP;
-    }
-    case WM_CHAR: {
-      bool should_exit = SelectByChar(static_cast<base::char16>(msg.wParam));
-      return should_exit ? POST_DISPATCH_QUIT_LOOP : POST_DISPATCH_NONE;
-    }
-    case WM_KEYUP:
-      return POST_DISPATCH_NONE;
-
-    case WM_SYSKEYUP:
-      // We may have been shown on a system key, as such don't do anything
-      // here. If another system key is pushed we'll get a WM_SYSKEYDOWN and
-      // close the menu.
-      return POST_DISPATCH_NONE;
-
-    case WM_CANCELMODE:
-    case WM_SYSKEYDOWN:
-      // Exit immediately on system keys.
-      Cancel(EXIT_ALL);
-      return POST_DISPATCH_QUIT_LOOP;
-
-    default:
-      break;
-  }
-  return POST_DISPATCH_PERFORM_DEFAULT |
-         (exit_type_ == EXIT_NONE ? POST_DISPATCH_NONE
-                                  : POST_DISPATCH_QUIT_LOOP);
-}
-#else
-uint32_t MenuController::Dispatch(const base::NativeEvent& event) {
-  if (exit_type_ == EXIT_ALL || exit_type_ == EXIT_DESTROYED)
-    return (POST_DISPATCH_QUIT_LOOP | POST_DISPATCH_PERFORM_DEFAULT);
-
-  switch (ui::EventTypeFromNative(event)) {
-    case ui::ET_KEY_PRESSED: {
-      if (!OnKeyDown(ui::KeyboardCodeFromNative(event)))
-        return POST_DISPATCH_QUIT_LOOP;
-
-      // Do not check mnemonics if the Alt or Ctrl modifiers are pressed.
-      int flags = ui::EventFlagsFromNative(event);
-      if ((flags & (ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)) == 0) {
-        char c = ui::GetCharacterFromKeyCode(
-            ui::KeyboardCodeFromNative(event), flags);
-        if (SelectByChar(c))
-          return POST_DISPATCH_QUIT_LOOP;
-      }
-      return POST_DISPATCH_NONE;
-    }
-    case ui::ET_KEY_RELEASED:
-      return POST_DISPATCH_NONE;
-    default:
-      break;
-  }
-
-  return POST_DISPATCH_PERFORM_DEFAULT |
-         (exit_type_ == EXIT_NONE ? POST_DISPATCH_NONE
-                                  : POST_DISPATCH_QUIT_LOOP);
-}
-#endif
-
 bool MenuController::OnKeyDown(ui::KeyboardCode key_code) {
   DCHECK(blocking_run_);
 
@@ -1155,11 +1129,6 @@ bool MenuController::OnKeyDown(ui::KeyboardCode key_code) {
       CloseSubmenu();
       break;
 
-#if defined(OS_WIN)
-    case VK_APPS:
-      break;
-#endif
-
     default:
       break;
   }
@@ -1205,6 +1174,52 @@ MenuController::~MenuController() {
   StopCancelAllTimer();
 }
 
+#if defined(OS_WIN)
+void MenuController::RunMessageLoop(bool nested_menu) {
+  internal::MenuMessagePumpDispatcher nested_dispatcher(this);
+
+  // |owner_| may be NULL.
+  aura::Window* root = GetOwnerRootWindow(owner_);
+  if (root) {
+    scoped_ptr<ActivationChangeObserverImpl> observer;
+    if (!nested_menu)
+      observer.reset(new ActivationChangeObserverImpl(this, root));
+    aura::client::GetDispatcherClient(root)
+        ->RunWithDispatcher(&nested_dispatcher);
+  } else {
+    base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
+    base::MessageLoop::ScopedNestableTaskAllower allow(loop);
+    base::RunLoop run_loop(&nested_dispatcher);
+    run_loop.Run();
+  }
+}
+#else
+void MenuController::RunMessageLoop(bool nested_menu) {
+  internal::MenuEventDispatcher event_dispatcher(this);
+  scoped_ptr<ui::ScopedEventDispatcher> old_dispatcher =
+      nested_dispatcher_.Pass();
+  if (ui::PlatformEventSource::GetInstance()) {
+    nested_dispatcher_ =
+        ui::PlatformEventSource::GetInstance()->OverrideDispatcher(
+            &event_dispatcher);
+  }
+  // |owner_| may be NULL.
+  aura::Window* root = GetOwnerRootWindow(owner_);
+  if (root) {
+    scoped_ptr<ActivationChangeObserverImpl> observer;
+    if (!nested_menu)
+      observer.reset(new ActivationChangeObserverImpl(this, root));
+    aura::client::GetDispatcherClient(root)->RunWithDispatcher(NULL);
+  } else {
+    base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
+    base::MessageLoop::ScopedNestableTaskAllower allow(loop);
+    base::RunLoop run_loop;
+    run_loop.Run();
+  }
+  nested_dispatcher_ = old_dispatcher.Pass();
+}
+#endif
+
 MenuController::SendAcceleratorResultType
     MenuController::SendAcceleratorToHotTrackedView() {
   CustomButton* hot_view = GetFirstHotTrackedView(pending_state_.item);
@@ -1219,10 +1234,9 @@ MenuController::SendAcceleratorResultType
       ACCELERATOR_PROCESSED : ACCELERATOR_PROCESSED_EXIT;
 }
 
-void MenuController::UpdateInitialLocation(
-    const gfx::Rect& bounds,
-    MenuItemView::AnchorPosition position,
-    bool context_menu) {
+void MenuController::UpdateInitialLocation(const gfx::Rect& bounds,
+                                           MenuAnchorPosition position,
+                                           bool context_menu) {
   pending_state_.context_menu = context_menu;
   pending_state_.initial_bounds = bounds;
   if (bounds.height() > 1) {
@@ -1233,10 +1247,10 @@ void MenuController::UpdateInitialLocation(
 
   // Reverse anchor position for RTL languages.
   if (base::i18n::IsRTL() &&
-      (position == MenuItemView::TOPRIGHT ||
-       position == MenuItemView::TOPLEFT)) {
-    pending_state_.anchor = position == MenuItemView::TOPRIGHT ?
-        MenuItemView::TOPLEFT : MenuItemView::TOPRIGHT;
+      (position == MENU_ANCHOR_TOPRIGHT || position == MENU_ANCHOR_TOPLEFT)) {
+    pending_state_.anchor = position == MENU_ANCHOR_TOPRIGHT
+                                ? MENU_ANCHOR_TOPLEFT
+                                : MENU_ANCHOR_TOPRIGHT;
   } else {
     pending_state_.anchor = position;
   }
@@ -1292,7 +1306,7 @@ bool MenuController::ShowSiblingMenu(SubmenuView* source,
   // if there is a sibling menu we should show.
   gfx::Point screen_point(mouse_location);
   View::ConvertPointToScreen(source_view, &screen_point);
-  MenuItemView::AnchorPosition anchor;
+  MenuAnchorPosition anchor;
   bool has_mnemonics;
   MenuButton* button = NULL;
   MenuItemView* alt_menu = source->GetMenuItem()->GetDelegate()->
@@ -1716,11 +1730,11 @@ gfx::Rect MenuController::CalculateMenuBounds(MenuItemView* item,
       x += 1;
 
     y = state_.initial_bounds.bottom();
-    if (state_.anchor == MenuItemView::TOPRIGHT) {
+    if (state_.anchor == MENU_ANCHOR_TOPRIGHT) {
       x = x + state_.initial_bounds.width() - pref.width();
       if (menu_config.offset_context_menus && state_.context_menu)
         x -= 1;
-    } else if (state_.anchor == MenuItemView::BOTTOMCENTER) {
+    } else if (state_.anchor == MENU_ANCHOR_BOTTOMCENTER) {
       x = x - (pref.width() - state_.initial_bounds.width()) / 2;
       if (pref.height() >
           state_.initial_bounds.y() + kCenteredContextMenuYOffset) {
@@ -1769,7 +1783,7 @@ gfx::Rect MenuController::CalculateMenuBounds(MenuItemView* item,
           // The menu should never overlap the owning button. So move it.
           // We use the anchor view style to determine the preferred position
           // relative to the owning button.
-          if (state_.anchor == MenuItemView::TOPLEFT) {
+          if (state_.anchor == MENU_ANCHOR_TOPLEFT) {
             // The menu starts with the same x coordinate as the owning button.
             if (x + state_.initial_bounds.width() + pref.width() >
                 state_.monitor_bounds.right())
@@ -1882,16 +1896,16 @@ gfx::Rect MenuController::CalculateBubbleMenuBounds(MenuItemView* item,
     int max_height = state_.monitor_bounds.height();
     // In case of bubbles, the maximum width is limited by the space
     // between the display corner and the target area + the tip size.
-    if (state_.anchor == MenuItemView::BUBBLE_LEFT) {
+    if (state_.anchor == MENU_ANCHOR_BUBBLE_LEFT) {
       max_width = owner_bounds.x() - state_.monitor_bounds.x() +
                   kBubbleTipSizeLeftRight;
-    } else if (state_.anchor == MenuItemView::BUBBLE_RIGHT) {
+    } else if (state_.anchor == MENU_ANCHOR_BUBBLE_RIGHT) {
       max_width = state_.monitor_bounds.right() - owner_bounds.right() +
                   kBubbleTipSizeLeftRight;
-    } else if (state_.anchor == MenuItemView::BUBBLE_ABOVE) {
+    } else if (state_.anchor == MENU_ANCHOR_BUBBLE_ABOVE) {
       max_height = owner_bounds.y() - state_.monitor_bounds.y() +
                    kBubbleTipSizeTopBottom;
-    } else if (state_.anchor == MenuItemView::BUBBLE_BELOW) {
+    } else if (state_.anchor == MENU_ANCHOR_BUBBLE_BELOW) {
       max_height = state_.monitor_bounds.bottom() - owner_bounds.bottom() +
                    kBubbleTipSizeTopBottom;
     }
@@ -1906,9 +1920,9 @@ gfx::Rect MenuController::CalculateBubbleMenuBounds(MenuItemView* item,
                           item->GetDelegate()->GetMaxWidthForMenu(item)));
 
   int x, y;
-  if (state_.anchor == MenuItemView::BUBBLE_ABOVE ||
-      state_.anchor == MenuItemView::BUBBLE_BELOW) {
-    if (state_.anchor == MenuItemView::BUBBLE_ABOVE)
+  if (state_.anchor == MENU_ANCHOR_BUBBLE_ABOVE ||
+      state_.anchor == MENU_ANCHOR_BUBBLE_BELOW) {
+    if (state_.anchor == MENU_ANCHOR_BUBBLE_ABOVE)
       y = owner_bounds.y() - pref.height() + kBubbleTipSizeTopBottom;
     else
       y = owner_bounds.bottom() - kBubbleTipSizeTopBottom;
@@ -1923,7 +1937,7 @@ gfx::Rect MenuController::CalculateBubbleMenuBounds(MenuItemView* item,
     submenu->GetScrollViewContainer()->SetBubbleArrowOffset(
         pref.width() / 2 - x + x_old);
   } else {
-    if (state_.anchor == MenuItemView::BUBBLE_RIGHT)
+    if (state_.anchor == MENU_ANCHOR_BUBBLE_RIGHT)
       x = owner_bounds.right() - kBubbleTipSizeLeftRight;
     else
       x = owner_bounds.x() - pref.width() + kBubbleTipSizeLeftRight;
@@ -2129,52 +2143,106 @@ bool MenuController::SelectByChar(base::char16 character) {
 
 void MenuController::RepostEvent(SubmenuView* source,
                                  const ui::LocatedEvent& event) {
+  if (!event.IsMouseEvent()) {
+    // TODO(rbyers): Gesture event repost is tricky to get right
+    // crbug.com/170987.
+    DCHECK(event.IsGestureEvent());
+    return;
+  }
+
+#if defined(OS_WIN)
+  if (!state_.item) {
+    // We some times get an event after closing all the menus. Ignore it. Make
+    // sure the menu is in fact not visible. If the menu is visible, then
+    // we're in a bad state where we think the menu isn't visibile but it is.
+    DCHECK(!source->GetWidget()->IsVisible());
+    return;
+  }
+
+  state_.item->GetRootMenuItem()->GetSubmenu()->ReleaseCapture();
+#endif
+
   gfx::Point screen_loc(event.location());
   View::ConvertPointToScreen(source->GetScrollViewContainer(), &screen_loc);
-
   gfx::NativeView native_view = source->GetWidget()->GetNativeView();
+  if (!native_view)
+    return;
+
   gfx::Screen* screen = gfx::Screen::GetScreenFor(native_view);
   gfx::NativeWindow window = screen->GetWindowAtScreenPoint(screen_loc);
 
-  // On Windows, it is ok for window to be NULL. Please refer to the
-  // RepostLocatedEvent function for more information.
 #if defined(OS_WIN)
-  // Release the capture.
-  SubmenuView* submenu = state_.item->GetRootMenuItem()->GetSubmenu();
-  submenu->ReleaseCapture();
-
-  gfx::NativeView view = submenu->GetWidget()->GetNativeView();
-  if (view && window) {
-    DWORD view_tid = GetWindowThreadProcessId(HWNDForNativeView(view), NULL);
-    if (view_tid != GetWindowThreadProcessId(HWNDForNativeView(window), NULL)) {
+  // PostMessage() to metro windows isn't allowed (access will be denied). Don't
+  // try to repost with Win32 if the window under the mouse press is in metro.
+  if (!ViewsDelegate::views_delegate ||
+      !ViewsDelegate::views_delegate->IsWindowInMetro(window)) {
+    HWND target_window = window ? HWNDForNativeWindow(window) :
+                                  WindowFromPoint(screen_loc.ToPOINT());
+    HWND source_window = HWNDForNativeView(native_view);
+    if (!target_window || !source_window ||
+        GetWindowThreadProcessId(source_window, NULL) !=
+        GetWindowThreadProcessId(target_window, NULL)) {
       // Even though we have mouse capture, windows generates a mouse event if
       // the other window is in a separate thread. Only repost an event if
-      // |view| was created on the same thread, else the target window can get
-      // double events leading to bad behavior.
+      // |target_window| and |source_window| were created on the same thread,
+      // else double events can occur and lead to bad behavior.
       return;
     }
+
+    // Determine whether the click was in the client area or not.
+    // NOTE: WM_NCHITTEST coordinates are relative to the screen.
+    LPARAM coords = MAKELPARAM(screen_loc.x(), screen_loc.y());
+    LRESULT nc_hit_result = SendMessage(target_window, WM_NCHITTEST, 0, coords);
+    const bool client_area = nc_hit_result == HTCLIENT;
+
+    // TODO(sky): this isn't right. The event to generate should correspond with
+    // the event we just got. MouseEvent only tells us what is down, which may
+    // differ. Need to add ability to get changed button from MouseEvent.
+    int event_type;
+    int flags = event.flags();
+    if (flags & ui::EF_LEFT_MOUSE_BUTTON) {
+      event_type = client_area ? WM_LBUTTONDOWN : WM_NCLBUTTONDOWN;
+    } else if (flags & ui::EF_MIDDLE_MOUSE_BUTTON) {
+      event_type = client_area ? WM_MBUTTONDOWN : WM_NCMBUTTONDOWN;
+    } else if (flags & ui::EF_RIGHT_MOUSE_BUTTON) {
+      event_type = client_area ? WM_RBUTTONDOWN : WM_NCRBUTTONDOWN;
+    } else {
+      NOTREACHED();
+      return;
+    }
+
+    int window_x = screen_loc.x();
+    int window_y = screen_loc.y();
+    if (client_area) {
+      POINT pt = { window_x, window_y };
+      ScreenToClient(target_window, &pt);
+      window_x = pt.x;
+      window_y = pt.y;
+    }
+
+    WPARAM target = client_area ? event.native_event().wParam : nc_hit_result;
+    LPARAM window_coords = MAKELPARAM(window_x, window_y);
+    PostMessage(target_window, event_type, target, window_coords);
+    return;
   }
-#else
+#endif
+  // Non-Windows Aura or |window| is in metro mode.
   if (!window)
     return;
-#endif
 
-  scoped_ptr<ui::LocatedEvent> clone;
-  if (event.IsMouseEvent()) {
-    clone.reset(new ui::MouseEvent(static_cast<const ui::MouseEvent&>(event)));
-  } else if (event.IsGestureEvent()) {
-    // TODO(rbyers): Gesture event repost is tricky to get right
-    // crbug.com/170987.
-    return;
-  } else {
-    NOTREACHED();
+  aura::Window* root = window->GetRootWindow();
+  ScreenPositionClient* spc = aura::client::GetScreenPositionClient(root);
+  if (!spc)
     return;
-  }
-  clone->set_location(screen_loc);
 
-  RepostLocatedEvent(window, *clone);
-}
+  gfx::Point root_loc(screen_loc);
+  spc->ConvertPointFromScreen(root, &root_loc);
 
+  ui::MouseEvent clone(static_cast<const ui::MouseEvent&>(event));
+  clone.set_location(root_loc);
+  clone.set_root_location(root_loc);
+  root->GetHost()->dispatcher()->RepostEvent(clone);
+}
 
 void MenuController::SetDropMenuItem(
     MenuItemView* new_target,
@@ -2318,9 +2386,17 @@ void MenuController::SetExitType(ExitType type) {
     } else {
       base::MessageLoop::current()->QuitNow();
     }
+    // Restore the previous dispatcher.
+    nested_dispatcher_.reset();
   }
 }
 
+bool MenuController::ShouldQuitNow() const {
+  aura::Window* root = GetOwnerRootWindow(owner_);
+  return !aura::client::GetDragDropClient(root) ||
+         !aura::client::GetDragDropClient(root)->IsDragDropInProgress();
+}
+
 void MenuController::HandleMouseLocation(SubmenuView* source,
                                          const gfx::Point& mouse_location) {
   if (showing_submenu_)
@@ -2353,4 +2429,10 @@ void MenuController::HandleMouseLocation(SubmenuView* source,
   }
 }
 
+gfx::Screen* MenuController::GetScreen() {
+  aura::Window* root = GetOwnerRootWindow(owner_);
+  return root ? gfx::Screen::GetScreenFor(root)
+              : gfx::Screen::GetNativeScreen();
+}
+
 }  // namespace views