1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "ui/views/controls/menu/menu_message_loop_aura.h" 6 7 #if defined(OS_WIN) 8 #include <windowsx.h> 9 #endif 10 11 #include "base/run_loop.h" 12 #include "ui/aura/client/screen_position_client.h" 13 #include "ui/aura/env.h" 14 #include "ui/aura/window.h" 15 #include "ui/aura/window_event_dispatcher.h" 16 #include "ui/aura/window_tree_host.h" 17 #include "ui/events/event.h" 18 #include "ui/events/platform/platform_event_source.h" 19 #include "ui/events/platform/scoped_event_dispatcher.h" 20 #include "ui/views/controls/menu/menu_controller.h" 21 #include "ui/views/widget/widget.h" 22 #include "ui/wm/public/activation_change_observer.h" 23 #include "ui/wm/public/activation_client.h" 24 #include "ui/wm/public/dispatcher_client.h" 25 #include "ui/wm/public/drag_drop_client.h" 26 27 #if defined(OS_WIN) 28 #include "ui/base/win/internal_constants.h" 29 #include "ui/views/controls/menu/menu_message_pump_dispatcher_win.h" 30 #include "ui/views/win/hwnd_util.h" 31 #else 32 #include "ui/views/controls/menu/menu_event_dispatcher_linux.h" 33 #endif 34 35 using aura::client::ScreenPositionClient; 36 37 namespace views { 38 39 namespace { 40 41 aura::Window* GetOwnerRootWindow(views::Widget* owner) { 42 return owner ? owner->GetNativeWindow()->GetRootWindow() : NULL; 43 } 44 45 // ActivationChangeObserverImpl is used to observe activation changes and close 46 // the menu. Additionally it listens for the root window to be destroyed and 47 // cancel the menu as well. 48 class ActivationChangeObserverImpl 49 : public aura::client::ActivationChangeObserver, 50 public aura::WindowObserver, 51 public ui::EventHandler { 52 public: 53 ActivationChangeObserverImpl(MenuController* controller, aura::Window* root) 54 : controller_(controller), root_(root) { 55 aura::client::GetActivationClient(root_)->AddObserver(this); 56 root_->AddObserver(this); 57 root_->AddPreTargetHandler(this); 58 } 59 60 virtual ~ActivationChangeObserverImpl() { Cleanup(); } 61 62 // aura::client::ActivationChangeObserver: 63 virtual void OnWindowActivated(aura::Window* gained_active, 64 aura::Window* lost_active) OVERRIDE { 65 if (!controller_->drag_in_progress()) 66 controller_->CancelAll(); 67 } 68 69 // aura::WindowObserver: 70 virtual void OnWindowDestroying(aura::Window* window) OVERRIDE { Cleanup(); } 71 72 // ui::EventHandler: 73 virtual void OnCancelMode(ui::CancelModeEvent* event) OVERRIDE { 74 controller_->CancelAll(); 75 } 76 77 private: 78 void Cleanup() { 79 if (!root_) 80 return; 81 // The ActivationClient may have been destroyed by the time we get here. 82 aura::client::ActivationClient* client = 83 aura::client::GetActivationClient(root_); 84 if (client) 85 client->RemoveObserver(this); 86 root_->RemovePreTargetHandler(this); 87 root_->RemoveObserver(this); 88 root_ = NULL; 89 } 90 91 MenuController* controller_; 92 aura::Window* root_; 93 94 DISALLOW_COPY_AND_ASSIGN(ActivationChangeObserverImpl); 95 }; 96 97 } // namespace 98 99 // static 100 MenuMessageLoop* MenuMessageLoop::Create() { 101 return new MenuMessageLoopAura; 102 } 103 104 MenuMessageLoopAura::MenuMessageLoopAura() : owner_(NULL) { 105 } 106 107 MenuMessageLoopAura::~MenuMessageLoopAura() { 108 } 109 110 void MenuMessageLoopAura::RepostEventToWindow(const ui::LocatedEvent& event, 111 gfx::NativeWindow window, 112 const gfx::Point& screen_loc) { 113 aura::Window* root = window->GetRootWindow(); 114 ScreenPositionClient* spc = aura::client::GetScreenPositionClient(root); 115 if (!spc) 116 return; 117 118 gfx::Point root_loc(screen_loc); 119 spc->ConvertPointFromScreen(root, &root_loc); 120 121 ui::MouseEvent clone(static_cast<const ui::MouseEvent&>(event)); 122 clone.set_location(root_loc); 123 clone.set_root_location(root_loc); 124 root->GetHost()->dispatcher()->RepostEvent(clone); 125 } 126 127 void MenuMessageLoopAura::Run(MenuController* controller, 128 Widget* owner, 129 bool nested_menu) { 130 // |owner_| may be NULL. 131 owner_ = owner; 132 aura::Window* root = GetOwnerRootWindow(owner_); 133 // It is possible for the same MenuMessageLoopAura to start a nested 134 // message-loop while it is already running a nested loop. So make sure the 135 // quit-closure gets reset to the outer loop's quit-closure once the innermost 136 // loop terminates. 137 base::AutoReset<base::Closure> reset_quit_closure(&message_loop_quit_, 138 base::Closure()); 139 140 #if defined(OS_WIN) 141 internal::MenuMessagePumpDispatcher nested_dispatcher(controller); 142 if (root) { 143 scoped_ptr<ActivationChangeObserverImpl> observer; 144 if (!nested_menu) 145 observer.reset(new ActivationChangeObserverImpl(controller, root)); 146 aura::client::DispatcherRunLoop run_loop( 147 aura::client::GetDispatcherClient(root), &nested_dispatcher); 148 message_loop_quit_ = run_loop.QuitClosure(); 149 run_loop.Run(); 150 } else { 151 base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); 152 base::MessageLoop::ScopedNestableTaskAllower allow(loop); 153 base::RunLoop run_loop(&nested_dispatcher); 154 message_loop_quit_ = run_loop.QuitClosure(); 155 run_loop.Run(); 156 } 157 #else 158 internal::MenuEventDispatcher event_dispatcher(controller); 159 scoped_ptr<ui::ScopedEventDispatcher> old_dispatcher = 160 nested_dispatcher_.Pass(); 161 if (ui::PlatformEventSource::GetInstance()) { 162 nested_dispatcher_ = 163 ui::PlatformEventSource::GetInstance()->OverrideDispatcher( 164 &event_dispatcher); 165 } 166 if (root) { 167 scoped_ptr<ActivationChangeObserverImpl> observer; 168 if (!nested_menu) 169 observer.reset(new ActivationChangeObserverImpl(controller, root)); 170 aura::client::DispatcherRunLoop run_loop( 171 aura::client::GetDispatcherClient(root), NULL); 172 message_loop_quit_ = run_loop.QuitClosure(); 173 run_loop.Run(); 174 } else { 175 base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); 176 base::MessageLoop::ScopedNestableTaskAllower allow(loop); 177 base::RunLoop run_loop; 178 message_loop_quit_ = run_loop.QuitClosure(); 179 run_loop.Run(); 180 } 181 nested_dispatcher_ = old_dispatcher.Pass(); 182 #endif 183 } 184 185 bool MenuMessageLoopAura::ShouldQuitNow() const { 186 aura::Window* root = GetOwnerRootWindow(owner_); 187 return !aura::client::GetDragDropClient(root) || 188 !aura::client::GetDragDropClient(root)->IsDragDropInProgress(); 189 } 190 191 void MenuMessageLoopAura::QuitNow() { 192 CHECK(!message_loop_quit_.is_null()); 193 message_loop_quit_.Run(); 194 // Restore the previous dispatcher. 195 nested_dispatcher_.reset(); 196 } 197 198 void MenuMessageLoopAura::ClearOwner() { 199 owner_ = NULL; 200 } 201 202 } // namespace views 203