diff options
Diffstat (limited to 'chrome/browser/automation/automation_provider_win.cc')
-rw-r--r-- | chrome/browser/automation/automation_provider_win.cc | 605 |
1 files changed, 605 insertions, 0 deletions
diff --git a/chrome/browser/automation/automation_provider_win.cc b/chrome/browser/automation/automation_provider_win.cc new file mode 100644 index 0000000..51959bd --- /dev/null +++ b/chrome/browser/automation/automation_provider_win.cc @@ -0,0 +1,605 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/automation/automation_provider.h" + +#include "base/json/json_reader.h" +#include "base/keyboard_codes.h" +#include "chrome/browser/automation/automation_browser_tracker.h" +#include "chrome/browser/automation/automation_extension_function.h" +#include "chrome/browser/automation/automation_tab_tracker.h" +#include "chrome/browser/automation/automation_window_tracker.h" +#include "chrome/browser/automation/extension_automation_constants.h" +#include "chrome/browser/automation/extension_port_container.h" +#include "chrome/browser/automation/ui_controls.h" +#include "chrome/browser/browser_window.h" +#include "chrome/browser/extensions/extension_message_service.h" +#include "chrome/browser/external_tab_container_win.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/views/bookmark_bar_view.h" +#include "chrome/test/automation/automation_messages.h" +#include "views/widget/root_view.h" +#include "views/widget/widget_win.h" +#include "views/window/window.h" + +// This task just adds another task to the event queue. This is useful if +// you want to ensure that any tasks added to the event queue after this one +// have already been processed by the time |task| is run. +class InvokeTaskLaterTask : public Task { + public: + explicit InvokeTaskLaterTask(Task* task) : task_(task) {} + virtual ~InvokeTaskLaterTask() {} + + virtual void Run() { + MessageLoop::current()->PostTask(FROM_HERE, task_); + } + + private: + Task* task_; + + DISALLOW_COPY_AND_ASSIGN(InvokeTaskLaterTask); +}; + +static void MoveMouse(const POINT& point) { + SetCursorPos(point.x, point.y); + + // Now, make sure that GetMessagePos returns the values we just set by + // simulating a mouse move. The value returned by GetMessagePos is updated + // when a mouse move event is removed from the event queue. + PostMessage(NULL, WM_MOUSEMOVE, 0, MAKELPARAM(point.x, point.y)); + MSG msg; + while (PeekMessage(&msg, NULL, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE)) { + } + + // Verify +#ifndef NDEBUG + DWORD pos = GetMessagePos(); + gfx::Point cursor_point(pos); + DCHECK_EQ(point.x, cursor_point.x()); + DCHECK_EQ(point.y, cursor_point.y()); +#endif +} + +BOOL CALLBACK EnumThreadWndProc(HWND hwnd, LPARAM l_param) { + if (hwnd == reinterpret_cast<HWND>(l_param)) { + return FALSE; + } + return TRUE; +} + +void AutomationProvider::GetActiveWindow(int* handle) { + HWND window = GetForegroundWindow(); + + // Let's make sure this window belongs to our process. + if (EnumThreadWindows(::GetCurrentThreadId(), + EnumThreadWndProc, + reinterpret_cast<LPARAM>(window))) { + // We enumerated all the windows and did not find the foreground window, + // it is not our window, ignore it. + *handle = 0; + return; + } + + *handle = window_tracker_->Add(window); +} + +// This task enqueues a mouse event on the event loop, so that the view +// that it's being sent to can do the requisite post-processing. +class MouseEventTask : public Task { + public: + MouseEventTask(views::View* view, + views::Event::EventType type, + const gfx::Point& point, + int flags) + : view_(view), type_(type), point_(point), flags_(flags) {} + virtual ~MouseEventTask() {} + + virtual void Run() { + views::MouseEvent event(type_, point_.x(), point_.y(), flags_); + // We need to set the cursor position before we process the event because + // some code (tab dragging, for instance) queries the actual cursor location + // rather than the location of the mouse event. Note that the reason why + // the drag code moved away from using mouse event locations was because + // our conversion to screen location doesn't work well with multiple + // monitors, so this only works reliably in a single monitor setup. + gfx::Point screen_location(point_.x(), point_.y()); + view_->ConvertPointToScreen(view_, &screen_location); + MoveMouse(screen_location.ToPOINT()); + switch (type_) { + case views::Event::ET_MOUSE_PRESSED: + view_->OnMousePressed(event); + break; + + case views::Event::ET_MOUSE_DRAGGED: + view_->OnMouseDragged(event); + break; + + case views::Event::ET_MOUSE_RELEASED: + view_->OnMouseReleased(event, false); + break; + + default: + NOTREACHED(); + } + } + + private: + views::View* view_; + views::Event::EventType type_; + gfx::Point point_; + int flags_; + + DISALLOW_COPY_AND_ASSIGN(MouseEventTask); +}; + +// This task sends a WindowDragResponse message with the appropriate +// routing ID to the automation proxy. This is implemented as a task so that +// we know that the mouse events (and any tasks that they spawn on the message +// loop) have been processed by the time this is sent. +class WindowDragResponseTask : public Task { + public: + WindowDragResponseTask(AutomationProvider* provider, + IPC::Message* reply_message) + : provider_(provider), reply_message_(reply_message) {} + virtual ~WindowDragResponseTask() {} + + virtual void Run() { + DCHECK(reply_message_ != NULL); + AutomationMsg_WindowDrag::WriteReplyParams(reply_message_, true); + provider_->Send(reply_message_); + } + + private: + AutomationProvider* provider_; + IPC::Message* reply_message_; + + DISALLOW_COPY_AND_ASSIGN(WindowDragResponseTask); +}; + +void AutomationProvider::WindowSimulateDrag(int handle, + std::vector<gfx::Point> drag_path, + int flags, + bool press_escape_en_route, + IPC::Message* reply_message) { + if (browser_tracker_->ContainsHandle(handle) && (drag_path.size() > 1)) { + gfx::NativeWindow window = + browser_tracker_->GetResource(handle)->window()->GetNativeHandle(); + + UINT down_message = 0; + UINT up_message = 0; + WPARAM wparam_flags = 0; + if (flags & views::Event::EF_SHIFT_DOWN) + wparam_flags |= MK_SHIFT; + if (flags & views::Event::EF_CONTROL_DOWN) + wparam_flags |= MK_CONTROL; + if (flags & views::Event::EF_LEFT_BUTTON_DOWN) { + wparam_flags |= MK_LBUTTON; + down_message = WM_LBUTTONDOWN; + up_message = WM_LBUTTONUP; + } + if (flags & views::Event::EF_MIDDLE_BUTTON_DOWN) { + wparam_flags |= MK_MBUTTON; + down_message = WM_MBUTTONDOWN; + up_message = WM_MBUTTONUP; + } + if (flags & views::Event::EF_RIGHT_BUTTON_DOWN) { + wparam_flags |= MK_RBUTTON; + down_message = WM_LBUTTONDOWN; + up_message = WM_LBUTTONUP; + } + + Browser* browser = browser_tracker_->GetResource(handle); + DCHECK(browser); + HWND top_level_hwnd = + reinterpret_cast<HWND>(browser->window()->GetNativeHandle()); + POINT temp = drag_path[0].ToPOINT(); + MapWindowPoints(top_level_hwnd, HWND_DESKTOP, &temp, 1); + MoveMouse(temp); + SendMessage(top_level_hwnd, down_message, wparam_flags, + MAKELPARAM(drag_path[0].x(), drag_path[0].y())); + for (int i = 1; i < static_cast<int>(drag_path.size()); ++i) { + temp = drag_path[i].ToPOINT(); + MapWindowPoints(top_level_hwnd, HWND_DESKTOP, &temp, 1); + MoveMouse(temp); + SendMessage(top_level_hwnd, WM_MOUSEMOVE, wparam_flags, + MAKELPARAM(drag_path[i].x(), drag_path[i].y())); + } + POINT end = drag_path[drag_path.size() - 1].ToPOINT(); + MapWindowPoints(top_level_hwnd, HWND_DESKTOP, &end, 1); + MoveMouse(end); + + if (press_escape_en_route) { + // Press Escape. + ui_controls::SendKeyPress(window, base::VKEY_ESCAPE, + ((flags & views::Event::EF_CONTROL_DOWN) + == views::Event::EF_CONTROL_DOWN), + ((flags & views::Event::EF_SHIFT_DOWN) == + views::Event::EF_SHIFT_DOWN), + ((flags & views::Event::EF_ALT_DOWN) == + views::Event::EF_ALT_DOWN), + false); + } + SendMessage(top_level_hwnd, up_message, wparam_flags, + MAKELPARAM(end.x, end.y)); + + MessageLoop::current()->PostTask(FROM_HERE, new InvokeTaskLaterTask( + new WindowDragResponseTask(this, reply_message))); + } else { + AutomationMsg_WindowDrag::WriteReplyParams(reply_message, false); + Send(reply_message); + } +} + +void AutomationProvider::GetWindowBounds(int handle, gfx::Rect* bounds, + bool* success) { + *success = false; + HWND hwnd = window_tracker_->GetResource(handle); + if (hwnd) { + *success = true; + WINDOWPLACEMENT window_placement; + GetWindowPlacement(hwnd, &window_placement); + *bounds = window_placement.rcNormalPosition; + } +} + +void AutomationProvider::SetWindowBounds(int handle, const gfx::Rect& bounds, + bool* success) { + *success = false; + if (window_tracker_->ContainsHandle(handle)) { + HWND hwnd = window_tracker_->GetResource(handle); + if (::MoveWindow(hwnd, bounds.x(), bounds.y(), bounds.width(), + bounds.height(), true)) { + *success = true; + } + } +} + +void AutomationProvider::SetWindowVisible(int handle, bool visible, + bool* result) { + if (window_tracker_->ContainsHandle(handle)) { + HWND hwnd = window_tracker_->GetResource(handle); + ::ShowWindow(hwnd, visible ? SW_SHOW : SW_HIDE); + *result = true; + } else { + *result = false; + } +} + +void AutomationProvider::ActivateWindow(int handle) { + if (window_tracker_->ContainsHandle(handle)) { + ::SetActiveWindow(window_tracker_->GetResource(handle)); + } +} + +void AutomationProvider::IsWindowMaximized(int handle, bool* is_maximized, + bool* success) { + *success = false; + + HWND hwnd = window_tracker_->GetResource(handle); + if (hwnd) { + *success = true; + WINDOWPLACEMENT window_placement; + GetWindowPlacement(hwnd, &window_placement); + *is_maximized = (window_placement.showCmd == SW_MAXIMIZE); + } +} + +void AutomationProvider::GetTabHWND(int handle, HWND* tab_hwnd) { + *tab_hwnd = NULL; + + if (tab_tracker_->ContainsHandle(handle)) { + NavigationController* tab = tab_tracker_->GetResource(handle); + *tab_hwnd = tab->tab_contents()->GetNativeView(); + } +} + +void AutomationProvider::CreateExternalTab( + const IPC::ExternalTabSettings& settings, + gfx::NativeWindow* tab_container_window, gfx::NativeWindow* tab_window, + int* tab_handle) { + *tab_handle = 0; + *tab_container_window = NULL; + *tab_window = NULL; + scoped_refptr<ExternalTabContainer> external_tab_container = + new ExternalTabContainer(this, automation_resource_message_filter_); + + Profile* profile = settings.is_off_the_record ? + profile_->GetOffTheRecordProfile() : profile_; + + // When the ExternalTabContainer window is created we grab a reference on it + // which is released when the window is destroyed. + external_tab_container->Init(profile, settings.parent, settings.dimensions, + settings.style, settings.load_requests_via_automation, + settings.handle_top_level_requests, NULL, settings.initial_url, + settings.referrer, settings.infobars_enabled); + + if (AddExternalTab(external_tab_container)) { + TabContents* tab_contents = external_tab_container->tab_contents(); + *tab_handle = external_tab_container->tab_handle(); + *tab_container_window = external_tab_container->GetNativeView(); + *tab_window = tab_contents->GetNativeView(); + } else { + external_tab_container->Uninitialize(); + } +} + +bool AutomationProvider::AddExternalTab(ExternalTabContainer* external_tab) { + DCHECK(external_tab != NULL); + + TabContents* tab_contents = external_tab->tab_contents(); + if (tab_contents) { + int tab_handle = tab_tracker_->Add(&tab_contents->controller()); + external_tab->SetTabHandle(tab_handle); + return true; + } + + return false; +} + +void AutomationProvider::ProcessUnhandledAccelerator( + const IPC::Message& message, int handle, const MSG& msg) { + ExternalTabContainer* external_tab = GetExternalTabForHandle(handle); + if (external_tab) { + external_tab->ProcessUnhandledAccelerator(msg); + } + // This message expects no response. +} + +void AutomationProvider::SetInitialFocus(const IPC::Message& message, + int handle, bool reverse, + bool restore_focus_to_view) { + ExternalTabContainer* external_tab = GetExternalTabForHandle(handle); + if (external_tab) { + external_tab->FocusThroughTabTraversal(reverse, restore_focus_to_view); + } + // This message expects no response. +} + +void AutomationProvider::PrintAsync(int tab_handle) { + NavigationController* tab = NULL; + TabContents* tab_contents = GetTabContentsForHandle(tab_handle, &tab); + if (tab_contents) { + if (tab_contents->PrintNow()) + return; + } +} + +ExternalTabContainer* AutomationProvider::GetExternalTabForHandle(int handle) { + if (tab_tracker_->ContainsHandle(handle)) { + NavigationController* tab = tab_tracker_->GetResource(handle); + return ExternalTabContainer::GetContainerForTab( + tab->tab_contents()->GetNativeView()); + } + + return NULL; +} + +void AutomationProvider::OnTabReposition( + int tab_handle, const IPC::Reposition_Params& params) { + if (!tab_tracker_->ContainsHandle(tab_handle)) + return; + + if (!IsWindow(params.window)) + return; + + unsigned long process_id = 0; + unsigned long thread_id = 0; + + thread_id = GetWindowThreadProcessId(params.window, &process_id); + + if (thread_id != GetCurrentThreadId()) { + DCHECK_EQ(thread_id, GetCurrentThreadId()); + return; + } + + SetWindowPos(params.window, params.window_insert_after, params.left, + params.top, params.width, params.height, params.flags); + + if (params.set_parent) { + if (IsWindow(params.parent_window)) { + if (!SetParent(params.window, params.parent_window)) + DLOG(WARNING) << "SetParent failed. Error 0x%x" << GetLastError(); + } + } +} + +void AutomationProvider::OnForwardContextMenuCommandToChrome(int tab_handle, + int command) { + if (tab_tracker_->ContainsHandle(tab_handle)) { + NavigationController* tab = tab_tracker_->GetResource(tab_handle); + if (!tab) { + NOTREACHED(); + return; + } + + TabContents* tab_contents = tab->tab_contents(); + if (!tab_contents || !tab_contents->delegate()) { + NOTREACHED(); + return; + } + + tab_contents->delegate()->ExecuteContextMenuCommand(command); + } +} + +void AutomationProvider::ConnectExternalTab( + uint64 cookie, + bool allow, + gfx::NativeWindow parent_window, + gfx::NativeWindow* tab_container_window, + gfx::NativeWindow* tab_window, + int* tab_handle) { + *tab_handle = 0; + *tab_container_window = NULL; + *tab_window = NULL; + + scoped_refptr<ExternalTabContainer> external_tab_container = + ExternalTabContainer::RemovePendingTab(static_cast<uintptr_t>(cookie)); + if (!external_tab_container.get()) { + NOTREACHED(); + return; + } + + if (allow && AddExternalTab(external_tab_container)) { + external_tab_container->Reinitialize(this, + automation_resource_message_filter_, + parent_window); + TabContents* tab_contents = external_tab_container->tab_contents(); + *tab_handle = external_tab_container->tab_handle(); + *tab_container_window = external_tab_container->GetNativeView(); + *tab_window = tab_contents->GetNativeView(); + } else { + external_tab_container->Uninitialize(); + } +} + +void AutomationProvider::TerminateSession(int handle, bool* success) { + *success = false; + + if (browser_tracker_->ContainsHandle(handle)) { + Browser* browser = browser_tracker_->GetResource(handle); + HWND window = browser->window()->GetNativeHandle(); + *success = (::PostMessageW(window, WM_ENDSESSION, 0, 0) == TRUE); + } +} + +void AutomationProvider::SetEnableExtensionAutomation( + int tab_handle, + const std::vector<std::string>& functions_enabled) { + ExternalTabContainer* external_tab = GetExternalTabForHandle(tab_handle); + if (external_tab) { + external_tab->SetEnableExtensionAutomation(functions_enabled); + } else { + // Tab must exist, and must be an external tab so that its + // delegate has an on-empty + // implementation of ForwardMessageToExternalHost. + DLOG(WARNING) << + "SetEnableExtensionAutomation called with invalid tab handle."; + } +} + +void AutomationProvider::OnBrowserMoved(int tab_handle) { + ExternalTabContainer* external_tab = GetExternalTabForHandle(tab_handle); + if (external_tab) { + external_tab->WindowMoved(); + } else { + DLOG(WARNING) << + "AutomationProvider::OnBrowserMoved called with invalid tab handle."; + } +} + +void AutomationProvider::GetWindowTitle(int handle, string16* text) { + gfx::NativeWindow window = window_tracker_->GetResource(handle); + std::wstring result; + int length = ::GetWindowTextLength(window) + 1; + ::GetWindowText(window, WriteInto(&result, length), length); + text->assign(WideToUTF16(result)); +} + +void AutomationProvider::OnMessageFromExternalHost(int handle, + const std::string& message, + const std::string& origin, + const std::string& target) { + RenderViewHost* view_host = GetViewForTab(handle); + if (!view_host) + return; + + if (AutomationExtensionFunction::InterceptMessageFromExternalHost( + view_host, message, origin, target)) { + // Message was diverted. + return; + } + + if (ExtensionPortContainer::InterceptMessageFromExternalHost( + message, origin, target, this, view_host, handle)) { + // Message was diverted. + return; + } + + if (InterceptBrowserEventMessageFromExternalHost(message, origin, target)) { + // Message was diverted. + return; + } + + view_host->ForwardMessageFromExternalHost(message, origin, target); +} + +bool AutomationProvider::InterceptBrowserEventMessageFromExternalHost( + const std::string& message, const std::string& origin, + const std::string& target) { + if (target != + extension_automation_constants::kAutomationBrowserEventRequestTarget) + return false; + + if (origin != extension_automation_constants::kAutomationOrigin) { + LOG(WARNING) << "Wrong origin on automation browser event " << origin; + return false; + } + + // The message is a JSON-encoded array with two elements, both strings. The + // first is the name of the event to dispatch. The second is a JSON-encoding + // of the arguments specific to that event. + scoped_ptr<Value> message_value(base::JSONReader::Read(message, false)); + if (!message_value.get() || !message_value->IsType(Value::TYPE_LIST)) { + LOG(WARNING) << "Invalid browser event specified through automation"; + return false; + } + + const ListValue* args = static_cast<const ListValue*>(message_value.get()); + + std::string event_name; + if (!args->GetString(0, &event_name)) { + LOG(WARNING) << "No browser event name specified through automation"; + return false; + } + + std::string json_args; + if (!args->GetString(1, &json_args)) { + LOG(WARNING) << "No browser event args specified through automation"; + return false; + } + + if (profile()->GetExtensionMessageService()) { + profile()->GetExtensionMessageService()->DispatchEventToRenderers( + event_name, json_args, profile()->IsOffTheRecord(), GURL()); + } + + return true; +} + +void AutomationProvider::NavigateInExternalTab( + int handle, const GURL& url, const GURL& referrer, + AutomationMsg_NavigationResponseValues* status) { + *status = AUTOMATION_MSG_NAVIGATION_ERROR; + + if (tab_tracker_->ContainsHandle(handle)) { + NavigationController* tab = tab_tracker_->GetResource(handle); + tab->LoadURL(url, referrer, PageTransition::TYPED); + *status = AUTOMATION_MSG_NAVIGATION_SUCCESS; + } +} + +void AutomationProvider::NavigateExternalTabAtIndex( + int handle, int navigation_index, + AutomationMsg_NavigationResponseValues* status) { + *status = AUTOMATION_MSG_NAVIGATION_ERROR; + + if (tab_tracker_->ContainsHandle(handle)) { + NavigationController* tab = tab_tracker_->GetResource(handle); + tab->GoToIndex(navigation_index); + *status = AUTOMATION_MSG_NAVIGATION_SUCCESS; + } +} + +void AutomationProvider::OnRunUnloadHandlers( + int handle, gfx::NativeWindow notification_window, + int notification_message) { + ExternalTabContainer* external_tab = GetExternalTabForHandle(handle); + if (external_tab) { + external_tab->RunUnloadHandlers(notification_window, notification_message); + } +} + |