summaryrefslogtreecommitdiffstats
path: root/chrome/browser/automation/automation_provider_win.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/automation/automation_provider_win.cc')
-rw-r--r--chrome/browser/automation/automation_provider_win.cc605
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);
+ }
+}
+