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
20 namespace {
21
22 // Helper class that waits until the focus has changed to a view other
23 // than the one with the provided view id.
24 class ViewFocusChangeWaiter : public views::FocusChangeListener {
25 public:
ViewFocusChangeWaiter(views::FocusManager * focus_manager,int previous_view_id,AutomationProvider * automation,IPC::Message * reply_message)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
~ViewFocusChangeWaiter()41 ~ViewFocusChangeWaiter() {
42 focus_manager_->RemoveFocusChangeListener(this);
43 }
44
45 // Inherited from FocusChangeListener
FocusWillChange(views::View * focused_before,views::View * focused_now)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:
FocusChanged(views::View * focused_before,views::View * focused_now)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
81 class TestingAutomationProvider::PopupMenuWaiter : public views::MenuListener {
82 public:
PopupMenuWaiter(ToolbarView * toolbar_view,TestingAutomationProvider * automation)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
OnMenuOpened()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
set_reply_message(IPC::Message * reply_message)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
WindowGetViewBounds(int handle,int view_id,bool screen_coordinates,bool * success,gfx::Rect * bounds)116 void 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
GetFocusedViewID(int handle,int * view_id)144 void 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
WaitForFocusedViewIDToChange(int handle,int previous_view_id,IPC::Message * reply_message)157 void 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
StartTrackingPopupMenus(int browser_handle,bool * success)172 void 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
WaitForPopupMenuToOpen(IPC::Message * reply_message)185 void 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