• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/automation_provider.h"
6 
7 #include "base/debug/trace_event.h"
8 #include "base/json/json_reader.h"
9 #include "base/utf_string_conversions.h"
10 #include "chrome/browser/automation/automation_browser_tracker.h"
11 #include "chrome/browser/automation/automation_tab_tracker.h"
12 #include "chrome/browser/automation/automation_window_tracker.h"
13 #include "chrome/browser/automation/ui_controls.h"
14 #include "chrome/browser/external_tab_container_win.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/ui/browser.h"
17 #include "chrome/browser/ui/browser_window.h"
18 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
19 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
20 #include "chrome/common/automation_messages.h"
21 #include "content/browser/renderer_host/render_view_host.h"
22 #include "content/browser/tab_contents/tab_contents.h"
23 #include "content/common/page_zoom.h"
24 #include "ui/base/keycodes/keyboard_codes.h"
25 #include "views/focus/accelerator_handler.h"
26 #include "views/widget/root_view.h"
27 #include "views/widget/widget_win.h"
28 #include "views/window/window.h"
29 
30 // This task just adds another task to the event queue.  This is useful if
31 // you want to ensure that any tasks added to the event queue after this one
32 // have already been processed by the time |task| is run.
33 class InvokeTaskLaterTask : public Task {
34  public:
InvokeTaskLaterTask(Task * task)35   explicit InvokeTaskLaterTask(Task* task) : task_(task) {}
~InvokeTaskLaterTask()36   virtual ~InvokeTaskLaterTask() {}
37 
Run()38   virtual void Run() {
39     MessageLoop::current()->PostTask(FROM_HERE, task_);
40   }
41 
42  private:
43   Task* task_;
44 
45   DISALLOW_COPY_AND_ASSIGN(InvokeTaskLaterTask);
46 };
47 
MoveMouse(const POINT & point)48 static void MoveMouse(const POINT& point) {
49   SetCursorPos(point.x, point.y);
50 
51   // Now, make sure that GetMessagePos returns the values we just set by
52   // simulating a mouse move.  The value returned by GetMessagePos is updated
53   // when a mouse move event is removed from the event queue.
54   PostMessage(NULL, WM_MOUSEMOVE, 0, MAKELPARAM(point.x, point.y));
55   MSG msg;
56   while (PeekMessage(&msg, NULL, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE)) {
57   }
58 
59   // Verify
60 #ifndef NDEBUG
61   DWORD pos = GetMessagePos();
62   gfx::Point cursor_point(pos);
63   DCHECK_EQ(point.x, cursor_point.x());
64   DCHECK_EQ(point.y, cursor_point.y());
65 #endif
66 }
67 
EnumThreadWndProc(HWND hwnd,LPARAM l_param)68 BOOL CALLBACK EnumThreadWndProc(HWND hwnd, LPARAM l_param) {
69   if (hwnd == reinterpret_cast<HWND>(l_param)) {
70     return FALSE;
71   }
72   return TRUE;
73 }
74 
75 // This task enqueues a mouse event on the event loop, so that the view
76 // that it's being sent to can do the requisite post-processing.
77 class MouseEventTask : public Task {
78  public:
MouseEventTask(views::View * view,ui::EventType type,const gfx::Point & point,int flags)79   MouseEventTask(views::View* view,
80                  ui::EventType type,
81                  const gfx::Point& point,
82                  int flags)
83       : view_(view), type_(type), point_(point), flags_(flags) {}
~MouseEventTask()84   virtual ~MouseEventTask() {}
85 
Run()86   virtual void Run() {
87     views::MouseEvent event(type_, point_.x(), point_.y(), flags_);
88     // We need to set the cursor position before we process the event because
89     // some code (tab dragging, for instance) queries the actual cursor location
90     // rather than the location of the mouse event. Note that the reason why
91     // the drag code moved away from using mouse event locations was because
92     // our conversion to screen location doesn't work well with multiple
93     // monitors, so this only works reliably in a single monitor setup.
94     gfx::Point screen_location(point_.x(), point_.y());
95     view_->ConvertPointToScreen(view_, &screen_location);
96     MoveMouse(screen_location.ToPOINT());
97     switch (type_) {
98       case ui::ET_MOUSE_PRESSED:
99         view_->OnMousePressed(event);
100         break;
101 
102       case ui::ET_MOUSE_DRAGGED:
103         view_->OnMouseDragged(event);
104         break;
105 
106       case ui::ET_MOUSE_RELEASED:
107         view_->OnMouseReleased(event);
108         break;
109 
110       default:
111         NOTREACHED();
112     }
113   }
114 
115  private:
116   views::View* view_;
117   ui::EventType type_;
118   gfx::Point point_;
119   int flags_;
120 
121   DISALLOW_COPY_AND_ASSIGN(MouseEventTask);
122 };
123 
124 // This task sends a WindowDragResponse message with the appropriate
125 // routing ID to the automation proxy.  This is implemented as a task so that
126 // we know that the mouse events (and any tasks that they spawn on the message
127 // loop) have been processed by the time this is sent.
128 class WindowDragResponseTask : public Task {
129  public:
WindowDragResponseTask(AutomationProvider * provider,IPC::Message * reply_message)130   WindowDragResponseTask(AutomationProvider* provider,
131                          IPC::Message* reply_message)
132       : provider_(provider), reply_message_(reply_message) {}
~WindowDragResponseTask()133   virtual ~WindowDragResponseTask() {}
134 
Run()135   virtual void Run() {
136     DCHECK(reply_message_ != NULL);
137     AutomationMsg_WindowDrag::WriteReplyParams(reply_message_, true);
138     provider_->Send(reply_message_);
139   }
140 
141  private:
142   AutomationProvider* provider_;
143   IPC::Message* reply_message_;
144 
145   DISALLOW_COPY_AND_ASSIGN(WindowDragResponseTask);
146 };
147 
WindowSimulateDrag(int handle,const std::vector<gfx::Point> & drag_path,int flags,bool press_escape_en_route,IPC::Message * reply_message)148 void AutomationProvider::WindowSimulateDrag(
149     int handle,
150     const std::vector<gfx::Point>& drag_path,
151     int flags,
152     bool press_escape_en_route,
153     IPC::Message* reply_message) {
154   if (browser_tracker_->ContainsHandle(handle) && (drag_path.size() > 1)) {
155     gfx::NativeWindow window =
156         browser_tracker_->GetResource(handle)->window()->GetNativeHandle();
157 
158     UINT down_message = 0;
159     UINT up_message = 0;
160     WPARAM wparam_flags = 0;
161     if (flags & ui::EF_SHIFT_DOWN)
162       wparam_flags |= MK_SHIFT;
163     if (flags & ui::EF_CONTROL_DOWN)
164       wparam_flags |= MK_CONTROL;
165     if (flags & ui::EF_LEFT_BUTTON_DOWN) {
166       wparam_flags |= MK_LBUTTON;
167       down_message = WM_LBUTTONDOWN;
168       up_message = WM_LBUTTONUP;
169     }
170     if (flags & ui::EF_MIDDLE_BUTTON_DOWN) {
171       wparam_flags |= MK_MBUTTON;
172       down_message = WM_MBUTTONDOWN;
173       up_message = WM_MBUTTONUP;
174     }
175     if (flags & ui::EF_RIGHT_BUTTON_DOWN) {
176       wparam_flags |= MK_RBUTTON;
177       down_message = WM_LBUTTONDOWN;
178       up_message = WM_LBUTTONUP;
179     }
180 
181     Browser* browser = browser_tracker_->GetResource(handle);
182     DCHECK(browser);
183     HWND top_level_hwnd =
184         reinterpret_cast<HWND>(browser->window()->GetNativeHandle());
185     POINT temp = drag_path[0].ToPOINT();
186     MapWindowPoints(top_level_hwnd, HWND_DESKTOP, &temp, 1);
187     MoveMouse(temp);
188     SendMessage(top_level_hwnd, down_message, wparam_flags,
189                 MAKELPARAM(drag_path[0].x(), drag_path[0].y()));
190     for (int i = 1; i < static_cast<int>(drag_path.size()); ++i) {
191       temp = drag_path[i].ToPOINT();
192       MapWindowPoints(top_level_hwnd, HWND_DESKTOP, &temp, 1);
193       MoveMouse(temp);
194       SendMessage(top_level_hwnd, WM_MOUSEMOVE, wparam_flags,
195                   MAKELPARAM(drag_path[i].x(), drag_path[i].y()));
196     }
197     POINT end = drag_path[drag_path.size() - 1].ToPOINT();
198     MapWindowPoints(top_level_hwnd, HWND_DESKTOP, &end, 1);
199     MoveMouse(end);
200 
201     if (press_escape_en_route) {
202       // Press Escape, making sure we wait until chrome processes the escape.
203       // TODO(phajdan.jr): make this use ui_test_utils::SendKeyPressSync.
204       ui_controls::SendKeyPressNotifyWhenDone(
205           window, ui::VKEY_ESCAPE,
206           ((flags & ui::EF_CONTROL_DOWN) ==
207            ui::EF_CONTROL_DOWN),
208           ((flags & ui::EF_SHIFT_DOWN) ==
209            ui::EF_SHIFT_DOWN),
210           ((flags & ui::EF_ALT_DOWN) == ui::EF_ALT_DOWN),
211           false,
212           new MessageLoop::QuitTask());
213       MessageLoopForUI* loop = MessageLoopForUI::current();
214       bool did_allow_task_nesting = loop->NestableTasksAllowed();
215       loop->SetNestableTasksAllowed(true);
216       views::AcceleratorHandler handler;
217       loop->Run(&handler);
218       loop->SetNestableTasksAllowed(did_allow_task_nesting);
219     }
220     SendMessage(top_level_hwnd, up_message, wparam_flags,
221                 MAKELPARAM(end.x, end.y));
222 
223     MessageLoop::current()->PostTask(FROM_HERE, new InvokeTaskLaterTask(
224         new WindowDragResponseTask(this, reply_message)));
225   } else {
226     AutomationMsg_WindowDrag::WriteReplyParams(reply_message, false);
227     Send(reply_message);
228   }
229 }
230 
CreateExternalTab(const ExternalTabSettings & settings,gfx::NativeWindow * tab_container_window,gfx::NativeWindow * tab_window,int * tab_handle,int * session_id)231 void AutomationProvider::CreateExternalTab(
232     const ExternalTabSettings& settings,
233     gfx::NativeWindow* tab_container_window, gfx::NativeWindow* tab_window,
234     int* tab_handle, int* session_id) {
235   TRACE_EVENT_BEGIN("AutomationProvider::CreateExternalTab", 0, "");
236 
237   *tab_handle = 0;
238   *tab_container_window = NULL;
239   *tab_window = NULL;
240   *session_id = -1;
241   scoped_refptr<ExternalTabContainer> external_tab_container =
242       new ExternalTabContainer(this, automation_resource_message_filter_);
243 
244   Profile* profile = settings.is_incognito ?
245       profile_->GetOffTheRecordProfile() : profile_;
246 
247   // When the ExternalTabContainer window is created we grab a reference on it
248   // which is released when the window is destroyed.
249   external_tab_container->Init(profile, settings.parent, settings.dimensions,
250       settings.style, settings.load_requests_via_automation,
251       settings.handle_top_level_requests, NULL, settings.initial_url,
252       settings.referrer, settings.infobars_enabled,
253       settings.route_all_top_level_navigations);
254 
255   if (AddExternalTab(external_tab_container)) {
256     TabContents* tab_contents = external_tab_container->tab_contents();
257     *tab_handle = external_tab_container->tab_handle();
258     *tab_container_window = external_tab_container->GetNativeView();
259     *tab_window = tab_contents->GetNativeView();
260     *session_id = tab_contents->controller().session_id().id();
261   } else {
262     external_tab_container->Uninitialize();
263   }
264 
265   TRACE_EVENT_END("AutomationProvider::CreateExternalTab", 0, "");
266 }
267 
AddExternalTab(ExternalTabContainer * external_tab)268 bool AutomationProvider::AddExternalTab(ExternalTabContainer* external_tab) {
269   DCHECK(external_tab != NULL);
270 
271   TabContents* tab_contents = external_tab->tab_contents();
272   if (tab_contents) {
273     int tab_handle = tab_tracker_->Add(&tab_contents->controller());
274     external_tab->SetTabHandle(tab_handle);
275     return true;
276   }
277 
278   return false;
279 }
280 
ProcessUnhandledAccelerator(const IPC::Message & message,int handle,const MSG & msg)281 void AutomationProvider::ProcessUnhandledAccelerator(
282     const IPC::Message& message, int handle, const MSG& msg) {
283   ExternalTabContainer* external_tab = GetExternalTabForHandle(handle);
284   if (external_tab) {
285     external_tab->ProcessUnhandledAccelerator(msg);
286   }
287   // This message expects no response.
288 }
289 
SetInitialFocus(const IPC::Message & message,int handle,bool reverse,bool restore_focus_to_view)290 void AutomationProvider::SetInitialFocus(const IPC::Message& message,
291                                          int handle, bool reverse,
292                                          bool restore_focus_to_view) {
293   ExternalTabContainer* external_tab = GetExternalTabForHandle(handle);
294   if (external_tab) {
295     external_tab->FocusThroughTabTraversal(reverse, restore_focus_to_view);
296   }
297   // This message expects no response.
298 }
299 
PrintAsync(int tab_handle)300 void AutomationProvider::PrintAsync(int tab_handle) {
301   TabContents* tab_contents = GetTabContentsForHandle(tab_handle, NULL);
302   if (!tab_contents)
303     return;
304 
305   TabContentsWrapper* wrapper =
306       TabContentsWrapper::GetCurrentWrapperForContents(tab_contents);
307   wrapper->print_view_manager()->PrintNow();
308 }
309 
GetExternalTabForHandle(int handle)310 ExternalTabContainer* AutomationProvider::GetExternalTabForHandle(int handle) {
311   if (tab_tracker_->ContainsHandle(handle)) {
312     NavigationController* tab = tab_tracker_->GetResource(handle);
313     return ExternalTabContainer::GetContainerForTab(
314         tab->tab_contents()->GetNativeView());
315   }
316 
317   return NULL;
318 }
319 
OnTabReposition(int tab_handle,const Reposition_Params & params)320 void AutomationProvider::OnTabReposition(
321     int tab_handle, const Reposition_Params& params) {
322   if (!tab_tracker_->ContainsHandle(tab_handle))
323     return;
324 
325   if (!IsWindow(params.window))
326     return;
327 
328   unsigned long process_id = 0;
329   unsigned long thread_id = 0;
330 
331   thread_id = GetWindowThreadProcessId(params.window, &process_id);
332 
333   if (thread_id != GetCurrentThreadId()) {
334     DCHECK_EQ(thread_id, GetCurrentThreadId());
335     return;
336   }
337 
338   SetWindowPos(params.window, params.window_insert_after, params.left,
339                params.top, params.width, params.height, params.flags);
340 
341   if (params.set_parent) {
342     if (IsWindow(params.parent_window)) {
343       if (!SetParent(params.window, params.parent_window))
344         DLOG(WARNING) << "SetParent failed. Error 0x%x" << GetLastError();
345     }
346   }
347 }
348 
OnForwardContextMenuCommandToChrome(int tab_handle,int command)349 void AutomationProvider::OnForwardContextMenuCommandToChrome(int tab_handle,
350                                                              int command) {
351   if (tab_tracker_->ContainsHandle(tab_handle)) {
352     NavigationController* tab = tab_tracker_->GetResource(tab_handle);
353     if (!tab) {
354       NOTREACHED();
355       return;
356     }
357 
358     TabContents* tab_contents = tab->tab_contents();
359     if (!tab_contents || !tab_contents->delegate()) {
360       NOTREACHED();
361       return;
362     }
363 
364     tab_contents->delegate()->ExecuteContextMenuCommand(command);
365   }
366 }
367 
ConnectExternalTab(uint64 cookie,bool allow,gfx::NativeWindow parent_window,gfx::NativeWindow * tab_container_window,gfx::NativeWindow * tab_window,int * tab_handle,int * session_id)368 void AutomationProvider::ConnectExternalTab(
369     uint64 cookie,
370     bool allow,
371     gfx::NativeWindow parent_window,
372     gfx::NativeWindow* tab_container_window,
373     gfx::NativeWindow* tab_window,
374     int* tab_handle,
375     int* session_id) {
376   TRACE_EVENT_BEGIN("AutomationProvider::ConnectExternalTab", 0, "");
377 
378   *tab_handle = 0;
379   *tab_container_window = NULL;
380   *tab_window = NULL;
381   *session_id = -1;
382 
383   scoped_refptr<ExternalTabContainer> external_tab_container =
384       ExternalTabContainer::RemovePendingTab(static_cast<uintptr_t>(cookie));
385   if (!external_tab_container.get()) {
386     NOTREACHED();
387     return;
388   }
389 
390   if (allow && AddExternalTab(external_tab_container)) {
391     external_tab_container->Reinitialize(this,
392                                          automation_resource_message_filter_,
393                                          parent_window);
394     TabContents* tab_contents = external_tab_container->tab_contents();
395     *tab_handle = external_tab_container->tab_handle();
396     *tab_container_window = external_tab_container->GetNativeView();
397     *tab_window = tab_contents->GetNativeView();
398     *session_id = tab_contents->controller().session_id().id();
399   } else {
400     external_tab_container->Uninitialize();
401   }
402 
403   TRACE_EVENT_END("AutomationProvider::ConnectExternalTab", 0, "");
404 }
405 
OnBrowserMoved(int tab_handle)406 void AutomationProvider::OnBrowserMoved(int tab_handle) {
407   ExternalTabContainer* external_tab = GetExternalTabForHandle(tab_handle);
408   if (external_tab) {
409     external_tab->WindowMoved();
410   } else {
411     DLOG(WARNING) <<
412       "AutomationProvider::OnBrowserMoved called with invalid tab handle.";
413   }
414 }
415 
OnMessageFromExternalHost(int handle,const std::string & message,const std::string & origin,const std::string & target)416 void AutomationProvider::OnMessageFromExternalHost(int handle,
417                                                    const std::string& message,
418                                                    const std::string& origin,
419                                                    const std::string& target) {
420   RenderViewHost* view_host = GetViewForTab(handle);
421   if (!view_host)
422     return;
423 
424   view_host->ForwardMessageFromExternalHost(message, origin, target);
425 }
426 
NavigateInExternalTab(int handle,const GURL & url,const GURL & referrer,AutomationMsg_NavigationResponseValues * status)427 void AutomationProvider::NavigateInExternalTab(
428     int handle, const GURL& url, const GURL& referrer,
429     AutomationMsg_NavigationResponseValues* status) {
430   *status = AUTOMATION_MSG_NAVIGATION_ERROR;
431 
432   if (tab_tracker_->ContainsHandle(handle)) {
433     NavigationController* tab = tab_tracker_->GetResource(handle);
434     tab->LoadURL(url, referrer, PageTransition::TYPED);
435     *status = AUTOMATION_MSG_NAVIGATION_SUCCESS;
436   }
437 }
438 
NavigateExternalTabAtIndex(int handle,int navigation_index,AutomationMsg_NavigationResponseValues * status)439 void AutomationProvider::NavigateExternalTabAtIndex(
440     int handle, int navigation_index,
441     AutomationMsg_NavigationResponseValues* status) {
442   *status = AUTOMATION_MSG_NAVIGATION_ERROR;
443 
444   if (tab_tracker_->ContainsHandle(handle)) {
445     NavigationController* tab = tab_tracker_->GetResource(handle);
446     tab->GoToIndex(navigation_index);
447     *status = AUTOMATION_MSG_NAVIGATION_SUCCESS;
448   }
449 }
450 
OnRunUnloadHandlers(int handle,IPC::Message * reply_message)451 void AutomationProvider::OnRunUnloadHandlers(
452     int handle, IPC::Message* reply_message) {
453   ExternalTabContainer* external_tab = GetExternalTabForHandle(handle);
454   if (external_tab) {
455     external_tab->RunUnloadHandlers(reply_message);
456   }
457 }
458 
OnSetZoomLevel(int handle,int zoom_level)459 void AutomationProvider::OnSetZoomLevel(int handle, int zoom_level) {
460   if (tab_tracker_->ContainsHandle(handle)) {
461     NavigationController* tab = tab_tracker_->GetResource(handle);
462     if (tab->tab_contents() && tab->tab_contents()->render_view_host()) {
463       tab->tab_contents()->render_view_host()->Zoom(
464           static_cast<PageZoom::Function>(zoom_level));
465     }
466   }
467 }
468