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