1// Copyright (c) 2011 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 "chrome/browser/automation/testing_automation_provider.h" 6 7#include "chrome/browser/automation/automation_browser_tracker.h" 8#include "chrome/browser/automation/automation_window_tracker.h" 9#include "chrome/browser/ui/browser_window.h" 10#include "chrome/browser/ui/views/frame/browser_view.h" 11#include "chrome/browser/ui/views/toolbar_view.h" 12#include "chrome/common/automation_messages.h" 13#include "ui/gfx/point.h" 14#include "views/controls/menu/menu_wrapper.h" 15#include "views/view.h" 16#include "views/widget/native_widget.h" 17#include "views/widget/root_view.h" 18#include "views/widget/widget.h" 19 20namespace { 21 22// Helper class that waits until the focus has changed to a view other 23// than the one with the provided view id. 24class ViewFocusChangeWaiter : public views::FocusChangeListener { 25 public: 26 ViewFocusChangeWaiter(views::FocusManager* focus_manager, 27 int previous_view_id, 28 AutomationProvider* automation, 29 IPC::Message* reply_message) 30 : focus_manager_(focus_manager), 31 previous_view_id_(previous_view_id), 32 automation_(automation), 33 reply_message_(reply_message), 34 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { 35 focus_manager_->AddFocusChangeListener(this); 36 // Call the focus change notification once in case the focus has 37 // already changed. 38 FocusWillChange(NULL, focus_manager_->GetFocusedView()); 39 } 40 41 ~ViewFocusChangeWaiter() { 42 focus_manager_->RemoveFocusChangeListener(this); 43 } 44 45 // Inherited from FocusChangeListener 46 virtual void FocusWillChange(views::View* focused_before, 47 views::View* focused_now) { 48 // This listener is called before focus actually changes. Post a task 49 // that will get run after focus changes. 50 MessageLoop::current()->PostTask( 51 FROM_HERE, 52 method_factory_.NewRunnableMethod( 53 &ViewFocusChangeWaiter::FocusChanged, 54 focused_before, 55 focused_now)); 56 } 57 58 private: 59 void FocusChanged(views::View* focused_before, 60 views::View* focused_now) { 61 if (focused_now && focused_now->GetID() != previous_view_id_) { 62 AutomationMsg_WaitForFocusedViewIDToChange::WriteReplyParams( 63 reply_message_, true, focused_now->GetID()); 64 65 automation_->Send(reply_message_); 66 delete this; 67 } 68 } 69 70 views::FocusManager* focus_manager_; 71 int previous_view_id_; 72 AutomationProvider* automation_; 73 IPC::Message* reply_message_; 74 ScopedRunnableMethodFactory<ViewFocusChangeWaiter> method_factory_; 75 76 DISALLOW_COPY_AND_ASSIGN(ViewFocusChangeWaiter); 77}; 78 79} // namespace 80 81class TestingAutomationProvider::PopupMenuWaiter : public views::MenuListener { 82 public: 83 PopupMenuWaiter(ToolbarView* toolbar_view, 84 TestingAutomationProvider* automation) 85 : toolbar_view_(toolbar_view), 86 automation_(automation), 87 reply_message_(NULL) { 88 toolbar_view_->AddMenuListener(this); 89 } 90 91 // Implementation of views::MenuListener 92 virtual void OnMenuOpened() { 93 toolbar_view_->RemoveMenuListener(this); 94 automation_->popup_menu_opened_ = true; 95 automation_->popup_menu_waiter_ = NULL; 96 if (reply_message_) { 97 AutomationMsg_WaitForPopupMenuToOpen::WriteReplyParams( 98 reply_message_, true); 99 automation_->Send(reply_message_); 100 } 101 delete this; 102 } 103 104 void set_reply_message(IPC::Message* reply_message) { 105 reply_message_ = reply_message; 106 } 107 108 private: 109 ToolbarView* toolbar_view_; 110 TestingAutomationProvider* automation_; 111 IPC::Message* reply_message_; 112 113 DISALLOW_COPY_AND_ASSIGN(PopupMenuWaiter); 114}; 115 116void TestingAutomationProvider::WindowGetViewBounds(int handle, 117 int view_id, 118 bool screen_coordinates, 119 bool* success, 120 gfx::Rect* bounds) { 121 *success = false; 122 123 if (window_tracker_->ContainsHandle(handle)) { 124 gfx::NativeWindow window = window_tracker_->GetResource(handle); 125 views::NativeWidget* native_widget = 126 views::NativeWidget::GetNativeWidgetForNativeWindow(window); 127 if (native_widget) { 128 views::View* root_view = native_widget->GetWidget()->GetRootView(); 129 views::View* view = root_view->GetViewByID(view_id); 130 if (view) { 131 *success = true; 132 gfx::Point point; 133 if (screen_coordinates) 134 views::View::ConvertPointToScreen(view, &point); 135 else 136 views::View::ConvertPointToView(view, root_view, &point); 137 *bounds = view->GetContentsBounds(); 138 bounds->set_origin(point); 139 } 140 } 141 } 142} 143 144void TestingAutomationProvider::GetFocusedViewID(int handle, int* view_id) { 145 *view_id = -1; 146 if (window_tracker_->ContainsHandle(handle)) { 147 gfx::NativeWindow window = window_tracker_->GetResource(handle); 148 views::FocusManager* focus_manager = 149 views::FocusManager::GetFocusManagerForNativeWindow(window); 150 DCHECK(focus_manager); 151 views::View* focused_view = focus_manager->GetFocusedView(); 152 if (focused_view) 153 *view_id = focused_view->GetID(); 154 } 155} 156 157void TestingAutomationProvider::WaitForFocusedViewIDToChange( 158 int handle, int previous_view_id, IPC::Message* reply_message) { 159 if (!window_tracker_->ContainsHandle(handle)) 160 return; 161 gfx::NativeWindow window = window_tracker_->GetResource(handle); 162 views::FocusManager* focus_manager = 163 views::FocusManager::GetFocusManagerForNativeWindow(window); 164 165 // The waiter will respond to the IPC and delete itself when done. 166 new ViewFocusChangeWaiter(focus_manager, 167 previous_view_id, 168 this, 169 reply_message); 170} 171 172void TestingAutomationProvider::StartTrackingPopupMenus( 173 int browser_handle, bool* success) { 174 if (browser_tracker_->ContainsHandle(browser_handle)) { 175 Browser* browser = browser_tracker_->GetResource(browser_handle); 176 BrowserView* browser_view = reinterpret_cast<BrowserView*>( 177 browser->window()); 178 ToolbarView* toolbar_view = browser_view->GetToolbarView(); 179 popup_menu_opened_ = false; 180 popup_menu_waiter_ = new PopupMenuWaiter(toolbar_view, this); 181 *success = true; 182 } 183} 184 185void TestingAutomationProvider::WaitForPopupMenuToOpen( 186 IPC::Message* reply_message) { 187 // See if the menu already opened and return true if so. 188 if (popup_menu_opened_) { 189 AutomationMsg_WaitForPopupMenuToOpen::WriteReplyParams( 190 reply_message, true); 191 Send(reply_message); 192 return; 193 } 194 195 // Otherwise, register this reply message with the waiter, 196 // which will handle responding to this IPC when the popup 197 // menu opens. 198 popup_menu_waiter_->set_reply_message(reply_message); 199} 200