diff options
Diffstat (limited to 'chrome/browser/automation/automation_provider.cc')
-rw-r--r-- | chrome/browser/automation/automation_provider.cc | 2172 |
1 files changed, 2172 insertions, 0 deletions
diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc new file mode 100644 index 0000000..03abe8b --- /dev/null +++ b/chrome/browser/automation/automation_provider.cc @@ -0,0 +1,2172 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/browser/automation/automation_provider.h" + +#include "base/path_service.h" +#include "chrome/browser/automation/automation_provider_list.h" +#include "chrome/browser/automation/ui_controls.h" +#include "chrome/browser/automation/url_request_failed_dns_job.h" +#include "chrome/browser/automation/url_request_mock_http_job.h" +#include "chrome/browser/automation/url_request_slow_download_job.h" +#include "chrome/browser/chrome_frame.h" +#include "chrome/browser/dom_operation_notification_details.h" +#include "chrome/browser/download_manager.h" +#include "chrome/browser/external_tab_container.h" +#include "chrome/browser/find_notification_details.h" +#include "chrome/browser/login_prompt.h" +#include "chrome/browser/navigation_entry.h" +#include "chrome/browser/printing/print_job.h" +#include "chrome/browser/save_package.h" +#include "chrome/browser/ssl_blocking_page.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/test/automation/automation_messages.h" +#include "net/base/cookie_monster.h" +#include "net/url_request/url_request_filter.h" + +class InitialLoadObserver : public NotificationObserver { + public: + InitialLoadObserver(size_t tab_count, AutomationProvider* automation) + : outstanding_tab_count_(tab_count), + automation_(automation) { + if (outstanding_tab_count_ > 0) { + NotificationService* service = NotificationService::current(); + service->AddObserver(this, NOTIFY_LOAD_START, + NotificationService::AllSources()); + service->AddObserver(this, NOTIFY_LOAD_STOP, + NotificationService::AllSources()); + } + } + + ~InitialLoadObserver() { + Unregister(); + } + + void ConditionMet() { + Unregister(); + automation_->Send(new AutomationMsg_InitialLoadsComplete(0)); + } + + void Unregister() { + NotificationService* service = NotificationService::current(); + service->RemoveObserver(this, NOTIFY_LOAD_START, + NotificationService::AllSources()); + service->RemoveObserver(this, NOTIFY_LOAD_STOP, + NotificationService::AllSources()); + } + + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type == NOTIFY_LOAD_START) { + if (outstanding_tab_count_ > loading_tabs_.size()) + loading_tabs_.insert(source.map_key()); + } else if (type == NOTIFY_LOAD_STOP) { + if (outstanding_tab_count_ > finished_tabs_.size()) { + if (loading_tabs_.find(source.map_key()) != loading_tabs_.end()) + finished_tabs_.insert(source.map_key()); + if (outstanding_tab_count_ == finished_tabs_.size()) + ConditionMet(); + } + } else { + NOTREACHED(); + } + } + + private: + typedef std::set<uintptr_t> TabSet; + + AutomationProvider* automation_; + size_t outstanding_tab_count_; + TabSet loading_tabs_; + TabSet finished_tabs_; +}; + +// Watches for NewTabUI page loads for performance timing purposes. +class NewTabUILoadObserver : public NotificationObserver { + public: + explicit NewTabUILoadObserver(AutomationProvider* automation) + : automation_(automation) { + NotificationService::current()-> + AddObserver(this, NOTIFY_INITIAL_NEW_TAB_UI_LOAD, + NotificationService::AllSources()); + } + + ~NewTabUILoadObserver() { + Unregister(); + } + + void Unregister() { + NotificationService::current()-> + RemoveObserver(this, NOTIFY_INITIAL_NEW_TAB_UI_LOAD, + NotificationService::AllSources()); + } + + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type == NOTIFY_INITIAL_NEW_TAB_UI_LOAD) { + Details<int> load_time(details); + automation_->Send( + new AutomationMsg_InitialNewTabUILoadComplete(0, *load_time.ptr())); + } else { + NOTREACHED(); + } + } + + private: + AutomationProvider* automation_; +}; + +class NavigationControllerRestoredObserver : public NotificationObserver { + public: + NavigationControllerRestoredObserver(AutomationProvider* automation, + NavigationController* controller, + int32 routing_id) + : automation_(automation), + controller_(controller), + routing_id_(routing_id) { + if (FinishedRestoring()) { + registered_ = false; + SendDone(); + } else { + registered_ = true; + NotificationService* service = NotificationService::current(); + service->AddObserver(this, NOTIFY_LOAD_STOP, + NotificationService::AllSources()); + } + } + + ~NavigationControllerRestoredObserver() { + if (registered_) + Unregister(); + } + + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (FinishedRestoring()) { + SendDone(); + Unregister(); + } + } + + private: + void Unregister() { + NotificationService* service = NotificationService::current(); + service->RemoveObserver(this, NOTIFY_LOAD_STOP, + NotificationService::AllSources()); + registered_ = false; + } + + bool FinishedRestoring() { + return (!controller_->needs_reload() && !controller_->GetPendingEntry() && + !controller_->active_contents()->IsLoading()); + } + + void SendDone() { + automation_->Send(new AutomationMsg_TabFinishedRestoring(routing_id_)); + } + + bool registered_; + AutomationProvider* automation_; + NavigationController* controller_; + const int routing_id_; + + DISALLOW_EVIL_CONSTRUCTORS(NavigationControllerRestoredObserver); +}; + + +class NavigationNotificationObserver : public NotificationObserver { + public: + NavigationNotificationObserver(NavigationController* controller, + AutomationProvider* automation, + IPC::Message* completed_response, + IPC::Message* auth_needed_response) + : automation_(automation), + completed_response_(completed_response), + auth_needed_response_(auth_needed_response), + controller_(controller), + navigation_started_(false) { + NotificationService* service = NotificationService::current(); + service->AddObserver(this, NOTIFY_LOAD_START, + Source<NavigationController>(controller_)); + service->AddObserver(this, NOTIFY_LOAD_STOP, + Source<NavigationController>(controller_)); + service->AddObserver(this, NOTIFY_AUTH_NEEDED, + Source<NavigationController>(controller_)); + service->AddObserver(this, NOTIFY_AUTH_SUPPLIED, + Source<NavigationController>(controller_)); + } + + ~NavigationNotificationObserver() { + if (completed_response_) delete completed_response_; + if (auth_needed_response_) delete auth_needed_response_; + Unregister(); + } + + void ConditionMet(IPC::Message** response) { + if (*response) { + automation_->Send(*response); + *response = NULL; // *response is deleted by Send. + } + automation_->RemoveNavigationStatusListener(this); + delete this; + } + + void Unregister() { + NotificationService* service = NotificationService::current(); + service->RemoveObserver(this, NOTIFY_LOAD_START, + Source<NavigationController>(controller_)); + service->RemoveObserver(this, NOTIFY_LOAD_STOP, + Source<NavigationController>(controller_)); + service->RemoveObserver(this, NOTIFY_AUTH_NEEDED, + Source<NavigationController>(controller_)); + service->RemoveObserver(this, NOTIFY_AUTH_SUPPLIED, + Source<NavigationController>(controller_)); + } + + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type == NOTIFY_LOAD_START) { + navigation_started_ = true; + } else if (type == NOTIFY_LOAD_STOP) { + if (navigation_started_) { + navigation_started_ = false; + ConditionMet(&completed_response_); + } + } else if (type == NOTIFY_AUTH_SUPPLIED) { + // The LoginHandler for this tab is no longer valid. + automation_->RemoveLoginHandler(controller_); + + // Treat this as if navigation started again, since load start/stop don't + // occur while authentication is ongoing. + navigation_started_ = true; + } else if (type == NOTIFY_AUTH_NEEDED) { + if (navigation_started_) { + // Remember the login handler that wants authentication. + LoginHandler* handler = + Details<LoginNotificationDetails>(details)->handler(); + automation_->AddLoginHandler(controller_, handler); + + // Respond that authentication is needed. + navigation_started_ = false; + ConditionMet(&auth_needed_response_); + } else { + NOTREACHED(); + } + } else { + NOTREACHED(); + } + } + + private: + AutomationProvider* automation_; + IPC::Message* completed_response_; + IPC::Message* auth_needed_response_; + NavigationController* controller_; + bool navigation_started_; +}; + +class TabStripNotificationObserver : public NotificationObserver { + public: + TabStripNotificationObserver(Browser* parent, NotificationType notification, + AutomationProvider* automation, int32 routing_id) + : automation_(automation), + notification_(notification), + parent_(parent), + routing_id_(routing_id) { + NotificationService::current()-> + AddObserver(this, notification_, NotificationService::AllSources()); + } + + virtual ~TabStripNotificationObserver() { + Unregister(); + } + + void Unregister() { + NotificationService::current()-> + RemoveObserver(this, notification_, NotificationService::AllSources()); + } + + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type == notification_) { + ObserveTab(Source<NavigationController>(source).ptr()); + + // If verified, no need to observe anymore + automation_->RemoveTabStripObserver(this); + delete this; + } else { + NOTREACHED(); + } + } + + virtual void ObserveTab(NavigationController* controller) = 0; + + protected: + AutomationProvider* automation_; + Browser* parent_; + NotificationType notification_; + int32 routing_id_; +}; + +class TabAppendedNotificationObserver : public TabStripNotificationObserver { + public: + TabAppendedNotificationObserver(Browser* parent, + AutomationProvider* automation, int32 routing_id) + : TabStripNotificationObserver(parent, NOTIFY_TAB_APPENDED, automation, + routing_id) { + } + + virtual void ObserveTab(NavigationController* controller) { + int tab_index = + automation_->GetIndexForNavigationController(controller, parent_); + if (tab_index == TabStripModel::kNoTab) { + // This tab notification doesn't belong to the parent_ + return; + } + + // Give the same response even if auth is needed, since it doesn't matter. + automation_->AddNavigationStatusListener(controller, + new AutomationMsg_AppendTabResponse(routing_id_, tab_index), + new AutomationMsg_AppendTabResponse(routing_id_, tab_index)); + } +}; + +class TabClosedNotificationObserver : public TabStripNotificationObserver { + public: + TabClosedNotificationObserver(Browser* parent, + AutomationProvider* automation, + int32 routing_id, + bool wait_until_closed) + : TabStripNotificationObserver(parent, + wait_until_closed ? NOTIFY_TAB_CLOSED : + NOTIFY_TAB_CLOSING, + automation, + routing_id) { + } + + virtual void ObserveTab(NavigationController* controller) { + automation_->Send(new AutomationMsg_CloseTabResponse(routing_id_, true)); + } +}; + +class BrowserClosedNotificationObserver : public NotificationObserver { + public: + BrowserClosedNotificationObserver(Browser* browser, + AutomationProvider* automation, + int32 routing_id) + : automation_(automation), + routing_id_(routing_id) { + NotificationService::current()-> + AddObserver(this, NOTIFY_BROWSER_CLOSED, Source<Browser>(browser)); + } + + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == NOTIFY_BROWSER_CLOSED); + Details<bool> close_app(details); + automation_->Send( + new AutomationMsg_CloseBrowserResponse(routing_id_, + true, + *(close_app.ptr()))); + delete this; + } + + private: + AutomationProvider* automation_; + int32 routing_id_; +}; + +class FindInPageNotificationObserver : public NotificationObserver { + public: + FindInPageNotificationObserver(AutomationProvider* automation, + TabContents* parent_tab, + int32 routing_id) + : automation_(automation), + parent_tab_(parent_tab), + routing_id_(routing_id) { + NotificationService::current()-> + AddObserver(this, NOTIFY_FIND_RESULT_AVAILABLE, + Source<TabContents>(parent_tab_)); + } + + ~FindInPageNotificationObserver() { + Unregister(); + } + + void Unregister() { + NotificationService::current()-> + RemoveObserver(this, NOTIFY_FIND_RESULT_AVAILABLE, + Source<TabContents>(parent_tab_)); + } + + virtual void Observe(NotificationType type, const NotificationSource& source, + const NotificationDetails& details) { + if (type == NOTIFY_FIND_RESULT_AVAILABLE) { + Details<FindNotificationDetails> find_details(details); + if (find_details->request_id() == kFindInPageRequestId) { + if (find_details->final_update()) { + automation_->Send(new AutomationMsg_FindInPageResponse(routing_id_, + find_details->number_of_matches())); + } else { + DLOG(INFO) << "Ignoring, since we only care about the final message"; + } + } + } else { + NOTREACHED(); + } + } + + // The Find mechanism is over asynchronous IPC, so a search is kicked off and + // we wait for notification to find out what the results are. As the user is + // typing, new search requests can be issued and the Request ID helps us make + // sense of whether this is the current request or an old one. The unit tests, + // however, which uses this constant issues only one search at a time, so we + // don't need a rolling id to identify each search. But, we still need to + // specify one, so we just use a fixed one - its value does not matter. + static const int kFindInPageRequestId; + private: + AutomationProvider* automation_; + TabContents* parent_tab_; + int32 routing_id_; +}; + +const int FindInPageNotificationObserver::kFindInPageRequestId = -1; + +class DomOperationNotificationObserver : public NotificationObserver { + public: + explicit DomOperationNotificationObserver(AutomationProvider* automation) + : automation_(automation) { + NotificationService::current()-> + AddObserver(this, NOTIFY_DOM_OPERATION_RESPONSE, + NotificationService::AllSources()); + } + + ~DomOperationNotificationObserver() { + NotificationService::current()-> + RemoveObserver(this, NOTIFY_DOM_OPERATION_RESPONSE, + NotificationService::AllSources()); + } + + virtual void Observe(NotificationType type, const NotificationSource& source, + const NotificationDetails& details) { + if (NOTIFY_DOM_OPERATION_RESPONSE == type) { + Details<DomOperationNotificationDetails> dom_op_details(details); + automation_->Send(new AutomationMsg_DomOperationResponse( + dom_op_details->automation_id(), + dom_op_details->json())); + } + } + private: + AutomationProvider* automation_; +}; + +class DomInspectorNotificationObserver : public NotificationObserver { + public: + explicit DomInspectorNotificationObserver(AutomationProvider* automation) + : automation_(automation) { + NotificationService::current()-> + AddObserver(this, NOTIFY_DOM_INSPECT_ELEMENT_RESPONSE, + NotificationService::AllSources()); + } + + ~DomInspectorNotificationObserver() { + NotificationService::current()-> + RemoveObserver(this, NOTIFY_DOM_INSPECT_ELEMENT_RESPONSE, + NotificationService::AllSources()); + } + + virtual void Observe(NotificationType type, const NotificationSource& source, + const NotificationDetails& details) { + if (NOTIFY_DOM_INSPECT_ELEMENT_RESPONSE == type) { + Details<int> dom_inspect_details(details); + automation_->ReceivedInspectElementResponse(*(dom_inspect_details.ptr())); + } + } + + private: + AutomationProvider* automation_; +}; + +class DocumentPrintedNotificationObserver : public NotificationObserver { + public: + DocumentPrintedNotificationObserver(AutomationProvider* automation, + int32 routing_id) + : automation_(automation), + routing_id_(routing_id), + success_(false) { + NotificationService::current()-> + AddObserver(this, NOTIFY_PRINT_JOB_EVENT, + NotificationService::AllSources()); + } + + ~DocumentPrintedNotificationObserver() { + automation_->Send( + new AutomationMsg_PrintNowResponse(routing_id_, success_)); + automation_->RemoveNavigationStatusListener(this); + NotificationService::current()-> + RemoveObserver(this, NOTIFY_PRINT_JOB_EVENT, + NotificationService::AllSources()); + } + + virtual void Observe(NotificationType type, const NotificationSource& source, + const NotificationDetails& details) { + using namespace printing; + DCHECK(type == NOTIFY_PRINT_JOB_EVENT); + switch (Details<JobEventDetails>(details)->type()) { + case JobEventDetails::JOB_DONE: { + // Succeeded. + success_ = true; + delete this; + break; + } + case JobEventDetails::USER_INIT_CANCELED: + case JobEventDetails::FAILED: { + // Failed. + delete this; + break; + } + case JobEventDetails::NEW_DOC: + case JobEventDetails::USER_INIT_DONE: + case JobEventDetails::DEFAULT_INIT_DONE: + case JobEventDetails::NEW_PAGE: + case JobEventDetails::PAGE_DONE: + case JobEventDetails::DOC_DONE: + case JobEventDetails::ALL_PAGES_REQUESTED: { + // Don't care. + break; + } + default: { + NOTREACHED(); + break; + } + } + } + + private: + scoped_refptr<AutomationProvider> automation_; + int32 routing_id_; + bool success_; +}; + +AutomationProvider::AutomationProvider(Profile* profile) + : connected_(false), + redirect_query_(0), + profile_(profile) { + AutomationProviderList* list = + g_browser_process->InitAutomationProviderList(); + DCHECK(NULL != list); + list->AddProvider(this); + browser_tracker_.reset(new AutomationBrowserTracker(this)); + window_tracker_.reset(new AutomationWindowTracker(this)); + tab_tracker_.reset(new AutomationTabTracker(this)); + autocomplete_edit_tracker_.reset( + new AutomationAutocompleteEditTracker(this)); + cwindow_tracker_.reset(new AutomationConstrainedWindowTracker(this)); + new_tab_ui_load_observer_.reset(new NewTabUILoadObserver(this)); + dom_operation_observer_.reset(new DomOperationNotificationObserver(this)); + dom_inspector_observer_.reset(new DomInspectorNotificationObserver(this)); +} + +AutomationProvider::~AutomationProvider() { + // TODO(vibhor) : Delete the pending observer objects. + AutomationProviderList* list = + g_browser_process->InitAutomationProviderList(); + DCHECK(NULL != list); + list->RemoveProvider(this); +} + +void AutomationProvider::ConnectToChannel(const std::wstring& channel_id) { + scoped_ptr<IPC::Channel> channel( + new IPC::Channel(channel_id, IPC::Channel::MODE_CLIENT, this)); + connected_ = channel->Connect(); + if (connected_) { + channel_.swap(channel); + channel_->Send(new AutomationMsg_Hello(0)); + } +} + +void AutomationProvider::SetExpectedTabCount(size_t expected_tabs) { + if (expected_tabs == 0) { + Send(new AutomationMsg_InitialLoadsComplete(0)); + } else { + initial_load_observer_.reset(new InitialLoadObserver(expected_tabs, this)); + } +} + +NotificationObserver* AutomationProvider::AddNavigationStatusListener( + NavigationController* tab, IPC::Message* completed_response, + IPC::Message* auth_needed_response) { + NotificationObserver* observer = + new NavigationNotificationObserver(tab, this, completed_response, + auth_needed_response); + notification_observer_list_.AddObserver(observer); + + return observer; +} + +void AutomationProvider::RemoveNavigationStatusListener( + NotificationObserver* obs) { + notification_observer_list_.RemoveObserver(obs); +} + +NotificationObserver* AutomationProvider::AddTabStripObserver( + Browser* parent, int32 routing_id) { + NotificationObserver* observer = new + TabAppendedNotificationObserver(parent, this, routing_id); + notification_observer_list_.AddObserver(observer); + + return observer; +} + +void AutomationProvider::RemoveTabStripObserver(NotificationObserver* obs) { + notification_observer_list_.RemoveObserver(obs); +} + +void AutomationProvider::AddLoginHandler(NavigationController* tab, + LoginHandler* handler) { + login_handler_map_[tab] = handler; +} + +void AutomationProvider::RemoveLoginHandler(NavigationController* tab) { + DCHECK(login_handler_map_[tab]); + login_handler_map_.erase(tab); +} + +int AutomationProvider::GetIndexForNavigationController( + const NavigationController* controller, const Browser* parent) const { + DCHECK(parent); + return parent->GetIndexOfController(controller); +} + +void AutomationProvider::OnMessageReceived(const IPC::Message& message) { + IPC_BEGIN_MESSAGE_MAP(AutomationProvider, message) + IPC_MESSAGE_HANDLER(AutomationMsg_CloseBrowserRequest, CloseBrowser) + IPC_MESSAGE_HANDLER(AutomationMsg_ActivateTabRequest, ActivateTab) + IPC_MESSAGE_HANDLER(AutomationMsg_ActiveTabIndexRequest, GetActiveTabIndex) + IPC_MESSAGE_HANDLER(AutomationMsg_AppendTabRequest, AppendTab) + IPC_MESSAGE_HANDLER(AutomationMsg_CloseTabRequest, CloseTab) + IPC_MESSAGE_HANDLER(AutomationMsg_GetCookiesRequest, GetCookies) + IPC_MESSAGE_HANDLER(AutomationMsg_SetCookieRequest, SetCookie) + IPC_MESSAGE_HANDLER(AutomationMsg_NavigateToURLRequest, NavigateToURL) + IPC_MESSAGE_HANDLER(AutomationMsg_NavigationAsyncRequest, NavigationAsync) + IPC_MESSAGE_HANDLER(AutomationMsg_GoBackRequest, GoBack) + IPC_MESSAGE_HANDLER(AutomationMsg_GoForwardRequest, GoForward) + IPC_MESSAGE_HANDLER(AutomationMsg_ReloadRequest, Reload) + IPC_MESSAGE_HANDLER(AutomationMsg_SetAuthRequest, SetAuth) + IPC_MESSAGE_HANDLER(AutomationMsg_CancelAuthRequest, CancelAuth) + IPC_MESSAGE_HANDLER(AutomationMsg_NeedsAuthRequest, NeedsAuth) + IPC_MESSAGE_HANDLER(AutomationMsg_RedirectsFromRequest, GetRedirectsFrom) + IPC_MESSAGE_HANDLER(AutomationMsg_BrowserWindowCountRequest, + GetBrowserWindowCount) + IPC_MESSAGE_HANDLER(AutomationMsg_BrowserWindowRequest, GetBrowserWindow) + IPC_MESSAGE_HANDLER(AutomationMsg_LastActiveBrowserWindowRequest, + GetLastActiveBrowserWindow) + IPC_MESSAGE_HANDLER(AutomationMsg_ActiveWindowRequest, GetActiveWindow) + IPC_MESSAGE_HANDLER(AutomationMsg_IsWindowActiveRequest, IsWindowActive) + IPC_MESSAGE_HANDLER(AutomationMsg_ActivateWindow, ActivateWindow); + IPC_MESSAGE_HANDLER(AutomationMsg_WindowHWNDRequest, GetWindowHWND) + IPC_MESSAGE_HANDLER(AutomationMsg_WindowViewBoundsRequest, + WindowGetViewBounds) + IPC_MESSAGE_HANDLER(AutomationMsg_SetWindowVisibleRequest, SetWindowVisible) + IPC_MESSAGE_HANDLER(AutomationMsg_WindowClickRequest, WindowSimulateClick) + IPC_MESSAGE_HANDLER(AutomationMsg_WindowKeyPressRequest, + WindowSimulateKeyPress) + IPC_MESSAGE_HANDLER(AutomationMsg_WindowDragRequest, WindowSimulateDrag) + IPC_MESSAGE_HANDLER(AutomationMsg_TabCountRequest, GetTabCount) + IPC_MESSAGE_HANDLER(AutomationMsg_TabRequest, GetTab) + IPC_MESSAGE_HANDLER(AutomationMsg_TabHWNDRequest, GetTabHWND) + IPC_MESSAGE_HANDLER(AutomationMsg_TabProcessIDRequest, GetTabProcessID) + IPC_MESSAGE_HANDLER(AutomationMsg_TabTitleRequest, GetTabTitle) + IPC_MESSAGE_HANDLER(AutomationMsg_TabURLRequest, GetTabURL) + IPC_MESSAGE_HANDLER(AutomationMsg_ShelfVisibilityRequest, + GetShelfVisibility) + IPC_MESSAGE_HANDLER(AutomationMsg_HandleUnused, HandleUnused) + IPC_MESSAGE_HANDLER(AutomationMsg_ApplyAcceleratorRequest, ApplyAccelerator) + IPC_MESSAGE_HANDLER(AutomationMsg_DomOperationRequest, ExecuteJavascript) + IPC_MESSAGE_HANDLER(AutomationMsg_ConstrainedWindowCountRequest, + GetConstrainedWindowCount) + IPC_MESSAGE_HANDLER(AutomationMsg_ConstrainedWindowRequest, + GetConstrainedWindow) + IPC_MESSAGE_HANDLER(AutomationMsg_ConstrainedTitleRequest, + GetConstrainedTitle) + IPC_MESSAGE_HANDLER(AutomationMsg_FindInPageRequest, + HandleFindInPageRequest) + IPC_MESSAGE_HANDLER(AutomationMsg_GetFocusedViewIDRequest, GetFocusedViewID) + IPC_MESSAGE_HANDLER(AutomationMsg_InspectElementRequest, + HandleInspectElementRequest) + IPC_MESSAGE_HANDLER(AutomationMsg_SetFilteredInet, + SetFilteredInet); + IPC_MESSAGE_HANDLER(AutomationMsg_DownloadDirectoryRequest, + GetDownloadDirectory); + IPC_MESSAGE_HANDLER(AutomationMsg_OpenNewBrowserWindow, + OpenNewBrowserWindow); + IPC_MESSAGE_HANDLER(AutomationMsg_WindowForBrowserRequest, + GetWindowForBrowser); + IPC_MESSAGE_HANDLER(AutomationMsg_AutocompleteEditForBrowserRequest, + GetAutocompleteEditForBrowser); + IPC_MESSAGE_HANDLER(AutomationMsg_BrowserForWindowRequest, + GetBrowserForWindow); + IPC_MESSAGE_HANDLER(AutomationMsg_CreateExternalTab, CreateExternalTab) + IPC_MESSAGE_HANDLER(AutomationMsg_NavigateInExternalTabRequest, + NavigateInExternalTab) + IPC_MESSAGE_HANDLER(AutomationMsg_ShowInterstitialPageRequest, + ShowInterstitialPage); + IPC_MESSAGE_HANDLER(AutomationMsg_HideInterstitialPageRequest, + HideInterstitialPage); + IPC_MESSAGE_HANDLER(AutomationMsg_SetAcceleratorsForTab, + SetAcceleratorsForTab) + IPC_MESSAGE_HANDLER(AutomationMsg_ProcessUnhandledAccelerator, + ProcessUnhandledAccelerator) + IPC_MESSAGE_HANDLER(AutomationMsg_WaitForTabToBeRestored, + WaitForTabToBeRestored) + IPC_MESSAGE_HANDLER(AutomationMsg_GetSecurityState, + GetSecurityState) + IPC_MESSAGE_HANDLER(AutomationMsg_GetPageType, + GetPageType) + IPC_MESSAGE_HANDLER(AutomationMsg_ActionOnSSLBlockingPage, + ActionOnSSLBlockingPage) + IPC_MESSAGE_HANDLER(AutomationMsg_BringBrowserToFront, BringBrowserToFront) + IPC_MESSAGE_HANDLER(AutomationMsg_IsPageMenuCommandEnabled, + IsPageMenuCommandEnabled) + IPC_MESSAGE_HANDLER(AutomationMsg_PrintNowRequest, PrintNow) + IPC_MESSAGE_HANDLER(AutomationMsg_SavePageRequest, SavePage) + IPC_MESSAGE_HANDLER(AutomationMsg_AutocompleteEditGetTextRequest, + GetAutocompleteEditText) + IPC_MESSAGE_HANDLER(AutomationMsg_AutocompleteEditSetTextRequest, + SetAutocompleteEditText) + IPC_MESSAGE_HANDLER(AutomationMsg_AutocompleteEditIsQueryInProgressRequest, + AutocompleteEditIsQueryInProgress) + IPC_MESSAGE_HANDLER(AutomationMsg_AutocompleteEditGetMatchesRequest, + AutocompleteEditGetMatches) + IPC_MESSAGE_HANDLER(AutomationMsg_ConstrainedWindowBoundsRequest, + GetConstrainedWindowBounds) + IPC_END_MESSAGE_MAP() +} + +void AutomationProvider::ActivateTab(const IPC::Message& message, + int handle, int at_index) { + int status = -1; + if (browser_tracker_->ContainsHandle(handle) && at_index > -1) { + Browser* browser = browser_tracker_->GetResource(handle); + if (at_index >= 0 && at_index < browser->tab_count()) { + browser->SelectTabContentsAt(at_index, true); + status = 0; + } + } + Send(new AutomationMsg_ActivateTabResponse(message.routing_id(), status)); +} + +void AutomationProvider::AppendTab(const IPC::Message& message, + int handle, const GURL& url) { + int append_tab_response = -1; // -1 is the error code + NotificationObserver* observer = NULL; + + if (browser_tracker_->ContainsHandle(handle)) { + Browser* browser = browser_tracker_->GetResource(handle); + observer = AddTabStripObserver(browser, message.routing_id()); + TabContents* tab_contents = + browser->AddTabWithURL(url, PageTransition::TYPED, true, NULL); + if (tab_contents) { + append_tab_response = + GetIndexForNavigationController(tab_contents->controller(), browser); + } + } + + if (append_tab_response < 0) { + // The append tab failed. Remove the TabStripObserver + if (observer) { + RemoveTabStripObserver(observer); + delete observer; + } + + // This will be reached only if the tab could not be appended. In case of a + // successful tab append, a successful navigation notification triggers the + // send. + Send(new AutomationMsg_AppendTabResponse(message.routing_id(), + append_tab_response)); + } +} + +void AutomationProvider::NavigateToURL(const IPC::Message& message, + int handle, const GURL& url) { + int status = AUTOMATION_MSG_NAVIGATION_ERROR; + + if (tab_tracker_->ContainsHandle(handle)) { + NavigationController* tab = tab_tracker_->GetResource(handle); + + // Simulate what a user would do. Activate the tab and then navigate. + // We could allow navigating in a background tab in future. + Browser* browser = FindAndActivateTab(tab); + + if (browser) { + AddNavigationStatusListener(tab, + new AutomationMsg_NavigateToURLResponse( + message.routing_id(), AUTOMATION_MSG_NAVIGATION_SUCCESS), + new AutomationMsg_NavigateToURLResponse( + message.routing_id(), AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED)); + // TODO(darin): avoid conversion to GURL + browser->OpenURL(url, CURRENT_TAB, PageTransition::TYPED); + return; + } + } + Send(new AutomationMsg_NavigateToURLResponse( + message.routing_id(), AUTOMATION_MSG_NAVIGATION_ERROR)); +} + +void AutomationProvider::NavigationAsync(const IPC::Message& message, + int handle, const GURL& url) { + bool status = false; + + if (tab_tracker_->ContainsHandle(handle)) { + NavigationController* tab = tab_tracker_->GetResource(handle); + + // Simulate what a user would do. Activate the tab and then navigate. + // We could allow navigating in a background tab in future. + Browser* browser = FindAndActivateTab(tab); + + if (browser) { + // Don't add any listener unless a callback mechanism is desired. + // TODO(vibhor): Do this if such a requirement arises in future. + browser->OpenURL(url, CURRENT_TAB, PageTransition::TYPED); + status = true; + } + } + + Send(new AutomationMsg_NavigationAsyncResponse(message.routing_id(), status)); +} + +void AutomationProvider::GoBack(const IPC::Message& message, int handle) { + if (tab_tracker_->ContainsHandle(handle)) { + NavigationController* tab = tab_tracker_->GetResource(handle); + Browser* browser = FindAndActivateTab(tab); + if (browser && browser->IsCommandEnabled(IDC_BACK)) { + AddNavigationStatusListener(tab, + new AutomationMsg_GoBackResponse( + message.routing_id(), AUTOMATION_MSG_NAVIGATION_SUCCESS), + new AutomationMsg_GoBackResponse( + message.routing_id(), AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED)); + browser->GoBack(); + return; + } + } + Send(new AutomationMsg_GoBackResponse(message.routing_id(), + AUTOMATION_MSG_NAVIGATION_ERROR)); +} + +void AutomationProvider::GoForward(const IPC::Message& message, int handle) { + if (tab_tracker_->ContainsHandle(handle)) { + NavigationController* tab = tab_tracker_->GetResource(handle); + Browser* browser = FindAndActivateTab(tab); + if (browser && browser->IsCommandEnabled(IDC_FORWARD)) { + AddNavigationStatusListener(tab, + new AutomationMsg_GoForwardResponse( + message.routing_id(), AUTOMATION_MSG_NAVIGATION_SUCCESS), + new AutomationMsg_GoForwardResponse( + message.routing_id(), AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED)); + browser->GoForward(); + return; + } + } + Send(new AutomationMsg_GoForwardResponse(message.routing_id(), + AUTOMATION_MSG_NAVIGATION_ERROR)); +} + +void AutomationProvider::Reload(const IPC::Message& message, int handle) { + if (tab_tracker_->ContainsHandle(handle)) { + NavigationController* tab = tab_tracker_->GetResource(handle); + Browser* browser = FindAndActivateTab(tab); + if (browser && browser->IsCommandEnabled(IDC_RELOAD)) { + AddNavigationStatusListener(tab, + new AutomationMsg_ReloadResponse( + message.routing_id(), AUTOMATION_MSG_NAVIGATION_SUCCESS), + new AutomationMsg_ReloadResponse( + message.routing_id(), AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED)); + browser->Reload(); + return; + } + } + Send(new AutomationMsg_ReloadResponse(message.routing_id(), + AUTOMATION_MSG_NAVIGATION_ERROR)); +} + +void AutomationProvider::SetAuth(const IPC::Message& message, int tab_handle, + const std::wstring& username, + const std::wstring& password) { + int status = -1; + + if (tab_tracker_->ContainsHandle(tab_handle)) { + NavigationController* tab = tab_tracker_->GetResource(tab_handle); + LoginHandlerMap::iterator iter = login_handler_map_.find(tab); + + if (iter != login_handler_map_.end()) { + // If auth is needed again after this, assume login has failed. This is + // not strictly correct, because a navigation can require both proxy and + // server auth, but it should be OK for now. + LoginHandler* handler = iter->second; + AddNavigationStatusListener(tab, + new AutomationMsg_SetAuthResponse(message.routing_id(), 0), + new AutomationMsg_SetAuthResponse(message.routing_id(), -1)); + handler->SetAuth(username, password); + status = 0; + } + } + if (status < 0) { + Send(new AutomationMsg_SetAuthResponse(message.routing_id(), status)); + } +} + +void AutomationProvider::CancelAuth(const IPC::Message& message, + int tab_handle) { + int status = -1; + + if (tab_tracker_->ContainsHandle(tab_handle)) { + NavigationController* tab = tab_tracker_->GetResource(tab_handle); + LoginHandlerMap::iterator iter = login_handler_map_.find(tab); + + if (iter != login_handler_map_.end()) { + // If auth is needed again after this, something is screwy. + LoginHandler* handler = iter->second; + AddNavigationStatusListener(tab, + new AutomationMsg_CancelAuthResponse(message.routing_id(), 0), + new AutomationMsg_CancelAuthResponse(message.routing_id(), -1)); + handler->CancelAuth(); + status = 0; + } + } + if (status < 0) { + Send(new AutomationMsg_CancelAuthResponse(message.routing_id(), status)); + } +} + +void AutomationProvider::NeedsAuth(const IPC::Message& message, + int tab_handle) { + bool needs_auth = false; + + if (tab_tracker_->ContainsHandle(tab_handle)) { + NavigationController* tab = tab_tracker_->GetResource(tab_handle); + LoginHandlerMap::iterator iter = login_handler_map_.find(tab); + + if (iter != login_handler_map_.end()) { + // The LoginHandler will be in our map IFF the tab needs auth. + needs_auth = true; + } + } + + Send(new AutomationMsg_NeedsAuthResponse(message.routing_id(), needs_auth)); +} + +void AutomationProvider::GetRedirectsFrom(const IPC::Message& message, + int tab_handle, + const GURL& source_url) { + DCHECK(!redirect_query_) << "Can only handle one redirect query at once."; + if (tab_tracker_->ContainsHandle(tab_handle)) { + NavigationController* tab = tab_tracker_->GetResource(tab_handle); + HistoryService* history_service = + tab->profile()->GetHistoryService(Profile::EXPLICIT_ACCESS); + + DCHECK(history_service) << "Tab " << tab_handle << "'s profile " << + "has no history service"; + if (history_service) { + // Schedule a history query for redirects. The response will be sent + // asynchronously from the callback the history system uses to notify us + // that it's done: OnRedirectQueryComplete. + redirect_query_routing_id_ = message.routing_id(); + redirect_query_ = history_service->QueryRedirectsFrom( + source_url, &consumer_, + NewCallback(this, &AutomationProvider::OnRedirectQueryComplete)); + return; // Response will be sent when query completes. + } + } + + // Send failure response. + IPC::Message* msg = new IPC::Message( + message.routing_id(), AutomationMsg_RedirectsFromResponse::ID, + IPC::Message::PRIORITY_NORMAL); + msg->WriteInt(-1); // Negative string count indicates an error. + Send(msg); +} + +void AutomationProvider::GetActiveTabIndex(const IPC::Message& message, + int handle) { + int active_tab_index = -1; // -1 is the error code + if (browser_tracker_->ContainsHandle(handle)) { + Browser* browser = browser_tracker_->GetResource(handle); + active_tab_index = browser->selected_index(); + } + Send(new AutomationMsg_ActiveTabIndexResponse(message.routing_id(), + active_tab_index)); +} + +void AutomationProvider::GetBrowserWindowCount(const IPC::Message& message) { + Send(new AutomationMsg_BrowserWindowCountResponse( + message.routing_id(), static_cast<int>(BrowserList::size()))); +} + +void AutomationProvider::GetBrowserWindow(const IPC::Message& message, + int index) { + int handle = 0; + if (index >= 0) { + BrowserList::const_iterator iter = BrowserList::begin(); + + for (; (iter != BrowserList::end()) && (index > 0); ++iter, --index); + if (iter != BrowserList::end()) { + handle = browser_tracker_->Add(*iter); + } + } + + Send(new AutomationMsg_BrowserWindowResponse(message.routing_id(), handle)); +} + +void AutomationProvider::GetLastActiveBrowserWindow( + const IPC::Message& message) { + int handle = 0; + Browser* browser = BrowserList::GetLastActive(); + if (browser) + handle = browser_tracker_->Add(browser); + Send(new AutomationMsg_LastActiveBrowserWindowResponse(message.routing_id(), + handle)); +} + +BOOL CALLBACK EnumThreadWndProc(HWND hwnd, LPARAM l_param) { + if (hwnd == reinterpret_cast<HWND>(l_param)) { + return FALSE; + } + return TRUE; +} + +void AutomationProvider::GetActiveWindow(const IPC::Message& message) { + 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. + Send(new AutomationMsg_ActiveWindowResponse(message.routing_id(), 0)); + return; + } + + int handle = window_tracker_->Add(window); + Send(new AutomationMsg_ActiveWindowResponse(message.routing_id(), handle)); +} + +void AutomationProvider::GetWindowHWND(const IPC::Message& message, + int handle) { + HWND win32_handle = window_tracker_->GetResource(handle); + Send(new AutomationMsg_WindowHWNDResponse(message.routing_id(), + win32_handle)); +} + +void AutomationProvider::WindowGetViewBounds(const IPC::Message& message, + int handle, + int view_id, + bool screen_coordinates) { + bool succeeded = false; + CRect bounds; + bounds.SetRect(0, 0, 0, 0); + + void* iter = NULL; + if (window_tracker_->ContainsHandle(handle)) { + HWND hwnd = window_tracker_->GetResource(handle); + ChromeViews::RootView* root_view = + ChromeViews::HWNDViewContainer::FindRootView(hwnd); + if (root_view) { + ChromeViews::View* view = root_view->GetViewByID(view_id); + if (view) { + succeeded = true; + CPoint point(0, 0); + if (screen_coordinates) + ChromeViews::View::ConvertPointToScreen(view, &point); + else + ChromeViews::View::ConvertPointToView(view, root_view, &point); + view->GetLocalBounds(&bounds, false); + bounds.MoveToXY(point.x, point.y); + } + } + } + + Send(new AutomationMsg_WindowViewBoundsResponse( + message.routing_id(), succeeded, gfx::Rect(bounds))); +} + +// 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(ChromeViews::View* view, + ChromeViews::Event::EventType type, + POINT point, + int flags) + : view_(view), type_(type), point_(point), flags_(flags) {} + virtual ~MouseEventTask() {} + + virtual void Run() { + ChromeViews::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. + CPoint screen_location = CPoint(point_.x, point_.y); + view_->ConvertPointToScreen(view_, &screen_location); + ::SetCursorPos(screen_location.x, screen_location.y); + switch (type_) { + case ChromeViews::Event::ET_MOUSE_PRESSED: + view_->OnMousePressed(event); + break; + + case ChromeViews::Event::ET_MOUSE_DRAGGED: + view_->OnMouseDragged(event); + break; + + case ChromeViews::Event::ET_MOUSE_RELEASED: + view_->OnMouseReleased(event, false); + break; + + default: + NOTREACHED(); + } + } + + private: + ChromeViews::View* view_; + ChromeViews::Event::EventType type_; + POINT point_; + int flags_; + + DISALLOW_EVIL_CONSTRUCTORS(MouseEventTask); +}; + +void AutomationProvider::ScheduleMouseEvent(ChromeViews::View* view, + ChromeViews::Event::EventType type, + POINT point, + int flags) { + MessageLoop::current()->PostTask(FROM_HERE, + new MouseEventTask(view, type, point, flags)); +} + +// 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_EVIL_CONSTRUCTORS(InvokeTaskLaterTask); +}; + +// 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, int routing_id) + : provider_(provider), routing_id_(routing_id) {} + virtual ~WindowDragResponseTask() {} + + virtual void Run() { + provider_->Send(new AutomationMsg_WindowDragResponse(routing_id_, true)); + } + + private: + AutomationProvider* provider_; + int routing_id_; + + DISALLOW_EVIL_CONSTRUCTORS(WindowDragResponseTask); +}; + +void AutomationProvider::WindowSimulateClick(const IPC::Message& message, + int handle, + POINT click, + int flags) { + HWND hwnd = 0; + + if (window_tracker_->ContainsHandle(handle)) { + hwnd = window_tracker_->GetResource(handle); + + BOOL r = ::ClientToScreen(hwnd, &click); + DCHECK(r); + ui_controls::SendMouseMove(click.x, click.y); + + ui_controls::MouseButton button = ui_controls::LEFT; + if ((flags & ChromeViews::Event::EF_LEFT_BUTTON_DOWN) == + ChromeViews::Event::EF_LEFT_BUTTON_DOWN) { + button = ui_controls::LEFT; + } else if ((flags & ChromeViews::Event::EF_RIGHT_BUTTON_DOWN) == + ChromeViews::Event::EF_RIGHT_BUTTON_DOWN) { + button = ui_controls::RIGHT; + } else if ((flags & ChromeViews::Event::EF_MIDDLE_BUTTON_DOWN) == + ChromeViews::Event::EF_MIDDLE_BUTTON_DOWN) { + button = ui_controls::MIDDLE; + } else { + NOTREACHED(); + } + ui_controls::SendMouseClick(button); + } +} + +void AutomationProvider::WindowSimulateDrag(const IPC::Message& message, + int handle, + std::vector<POINT> drag_path, + int flags) { + bool succeeded = false; + if (browser_tracker_->ContainsHandle(handle) && (drag_path.size() > 1)) { + succeeded = true; + + Browser* browser = browser_tracker_->GetResource(handle); + DCHECK(browser); + ChromeViews::RootView* root = browser->frame()->GetRootView(); + DCHECK(root); + ScheduleMouseEvent(root, ChromeViews::Event::ET_MOUSE_PRESSED, + drag_path[0], flags); + for (int i = 1; i < static_cast<int>(drag_path.size()); ++i) { + ScheduleMouseEvent(root, ChromeViews::Event::ET_MOUSE_DRAGGED, + drag_path[i], flags); + } + POINT end = drag_path[drag_path.size() - 1]; + ScheduleMouseEvent(root, ChromeViews::Event::ET_MOUSE_RELEASED, + drag_path[drag_path.size() - 1], flags); + + MessageLoop::current()->PostTask(FROM_HERE, + new InvokeTaskLaterTask( + new WindowDragResponseTask(this, message.routing_id()))); + } else { + Send(new AutomationMsg_WindowDragResponse(message.routing_id(), true)); + } +} + +void AutomationProvider::WindowSimulateKeyPress(const IPC::Message& message, + int handle, + wchar_t key, + int flags) { + if (!window_tracker_->ContainsHandle(handle)) + return; + + // The key event is sent to whatever window is active. + ui_controls::SendKeyPress(key, + ((flags & ChromeViews::Event::EF_CONTROL_DOWN) == + ChromeViews::Event::EF_CONTROL_DOWN), + ((flags & ChromeViews::Event::EF_SHIFT_DOWN) == + ChromeViews::Event::EF_SHIFT_DOWN), + ((flags & ChromeViews::Event::EF_ALT_DOWN) == + ChromeViews::Event::EF_ALT_DOWN)); +} + +void AutomationProvider::GetFocusedViewID(const IPC::Message& message, + int handle) { + int view_id = -1; + if (window_tracker_->ContainsHandle(handle)) { + HWND hwnd = window_tracker_->GetResource(handle); + ChromeViews::FocusManager* focus_manager = + ChromeViews::FocusManager::GetFocusManager(hwnd); + DCHECK(focus_manager); + ChromeViews::View* focused_view = focus_manager->GetFocusedView(); + if (focused_view) + view_id = focused_view->GetID(); + } + Send(new AutomationMsg_GetFocusedViewIDResponse(message.routing_id(), + view_id)); +} + +void AutomationProvider::SetWindowVisible(const IPC::Message& message, + int handle, bool visible) { + if (window_tracker_->ContainsHandle(handle)) { + HWND hwnd = window_tracker_->GetResource(handle); + ::ShowWindow(hwnd, visible ? SW_SHOW : SW_HIDE); + Send(new AutomationMsg_SetWindowVisibleResponse(message.routing_id(), + true)); + } else { + Send(new AutomationMsg_SetWindowVisibleResponse(message.routing_id(), + false)); + } +} + +void AutomationProvider::IsWindowActive(const IPC::Message& message, + int handle) { + if (window_tracker_->ContainsHandle(handle)) { + HWND hwnd = window_tracker_->GetResource(handle); + bool is_active = ::GetForegroundWindow() == hwnd; + Send(new AutomationMsg_IsWindowActiveResponse( + message.routing_id(), true, is_active)); + } else { + Send(new AutomationMsg_IsWindowActiveResponse(message.routing_id(), + false, false)); + } +} + +void AutomationProvider::ActivateWindow(const IPC::Message& message, + int handle) { + if (window_tracker_->ContainsHandle(handle)) { + ::SetActiveWindow(window_tracker_->GetResource(handle)); + } +} + +void AutomationProvider::GetTabCount(const IPC::Message& message, int handle) { + int tab_count = -1; // -1 is the error code + + if (browser_tracker_->ContainsHandle(handle)) { + Browser* browser = browser_tracker_->GetResource(handle); + tab_count = browser->tab_count(); + } + + Send(new AutomationMsg_TabCountResponse(message.routing_id(), tab_count)); +} + +void AutomationProvider::GetTab(const IPC::Message& message, + int win_handle, int tab_index) { + void* iter = NULL; + int tab_handle = 0; + if (browser_tracker_->ContainsHandle(win_handle) && (tab_index >= 0)) { + Browser* browser = browser_tracker_->GetResource(win_handle); + if (tab_index < browser->tab_count()) { + TabContents* tab_contents = + browser->GetTabContentsAt(tab_index); + tab_handle = tab_tracker_->Add(tab_contents->controller()); + } + } + + Send(new AutomationMsg_TabResponse(message.routing_id(), tab_handle)); +} + +void AutomationProvider::GetTabTitle(const IPC::Message& message, int handle) { + int title_string_size = -1; // -1 is the error code + std::wstring title; + if (tab_tracker_->ContainsHandle(handle)) { + NavigationController* tab = tab_tracker_->GetResource(handle); + title = tab->GetActiveEntry()->GetTitle(); + title_string_size = static_cast<int>(title.size()); + } + + Send(new AutomationMsg_TabTitleResponse(message.routing_id(), + title_string_size, title)); +} + +void AutomationProvider::HandleUnused(const IPC::Message& message, int handle) { + if (window_tracker_->ContainsHandle(handle)) { + window_tracker_->Remove(window_tracker_->GetResource(handle)); + } +} + +void AutomationProvider::OnChannelError() { + LOG(ERROR) << "AutomationProxy went away, shutting down app."; + delete this; +} + +// TODO(brettw) change this to accept GURLs when history supports it +void AutomationProvider::OnRedirectQueryComplete( + HistoryService::Handle request_handle, + GURL from_url, + bool success, + HistoryService::RedirectList* redirects) { + DCHECK(request_handle == redirect_query_); + + // Respond to the pending request for the redirect list. + IPC::Message* msg = new IPC::Message(redirect_query_routing_id_, + AutomationMsg_RedirectsFromResponse::ID, + IPC::Message::PRIORITY_NORMAL); + if (success) { + msg->WriteInt(static_cast<int>(redirects->size())); + for (size_t i = 0; i < redirects->size(); i++) + IPC::ParamTraits<GURL>::Write(msg, redirects->at(i)); + } else { + msg->WriteInt(-1); // Negative count indicates failure. + } + + Send(msg); + redirect_query_ = NULL; +} + +bool AutomationProvider::Send(IPC::Message* msg) { + if (connected_) { + DCHECK(channel_.get()); + return channel_->Send(msg); + } + return false; +} + +Browser* AutomationProvider::FindAndActivateTab( + NavigationController* controller) { + int tab_index; + Browser* browser = Browser::GetBrowserForController(controller, &tab_index); + if (browser) + browser->SelectTabContentsAt(tab_index, true); + + return browser; +} + +void AutomationProvider::GetCookies(const IPC::Message& message, + const GURL& url, int handle) { + std::string value; + + if (url.is_valid() && tab_tracker_->ContainsHandle(handle)) { + NavigationController* tab = tab_tracker_->GetResource(handle); + value = + tab->profile()->GetRequestContext()->cookie_store()->GetCookies(url); + } + + Send(new AutomationMsg_GetCookiesResponse(message.routing_id(), + static_cast<int>(value.size()), value)); +} + +void AutomationProvider::SetCookie(const IPC::Message& message, + const GURL& url, + const std::string value, + int handle) { + int response_value = -1; + + if (url.is_valid() && tab_tracker_->ContainsHandle(handle)) { + NavigationController* tab = tab_tracker_->GetResource(handle); + URLRequestContext* context = tab->profile()->GetRequestContext(); + if (context->cookie_store()->SetCookie(url, value)) + response_value = 1; + } + + Send(new AutomationMsg_SetCookieResponse(message.routing_id(), + response_value)); +} + +void AutomationProvider::GetTabURL(const IPC::Message& message, int handle) { + bool success = false; + GURL url; + if (tab_tracker_->ContainsHandle(handle)) { + NavigationController* tab = tab_tracker_->GetResource(handle); + // Return what the user would see in the location bar. + url = tab->GetActiveEntry()->GetDisplayURL(); + success = true; + } + + Send(new AutomationMsg_TabURLResponse(message.routing_id(), success, url)); +} + +void AutomationProvider::GetTabHWND(const IPC::Message& message, int handle) { + HWND tab_hwnd = NULL; + + if (tab_tracker_->ContainsHandle(handle)) { + NavigationController* tab = tab_tracker_->GetResource(handle); + tab_hwnd = tab->active_contents()->GetContainerHWND(); + } + + Send(new AutomationMsg_TabHWNDResponse(message.routing_id(), tab_hwnd)); +} + +void AutomationProvider::GetTabProcessID( + const IPC::Message& message, int handle) { + int process_id = -1; + + if (tab_tracker_->ContainsHandle(handle)) { + process_id = 0; + NavigationController* tab = tab_tracker_->GetResource(handle); + if (tab->active_contents()->AsWebContents()) { + WebContents* web_contents = tab->active_contents()->AsWebContents(); + if (web_contents->process()) { + process_id = + process_util::GetProcId(web_contents->process()->process()); + } + } + } + + Send(new AutomationMsg_TabProcessIDResponse(message.routing_id(), + process_id)); +} + +void AutomationProvider::ApplyAccelerator(int handle, int id) { + if (browser_tracker_->ContainsHandle(handle)) { + Browser* browser = browser_tracker_->GetResource(handle); + browser->controller()->ExecuteCommand(id); + } +} + +void AutomationProvider::ExecuteJavascript(const IPC::Message& message, + int handle, + const std::wstring& frame_xpath, + const std::wstring& script) { + bool succeeded = false; + if (tab_tracker_->ContainsHandle(handle)) { + NavigationController* tab = tab_tracker_->GetResource(handle); + TabContents* tab_contents = tab->active_contents(); + if (tab_contents && tab_contents->type() == TAB_CONTENTS_WEB) { + WebContents* web_contents = tab_contents->AsWebContents(); + + // Set the routing id of this message with the controller. + // This routing id needs to be remembered for the reverse + // communication while sending back the response of + // this javascript execution. + std::wstring url; + SStringPrintf(&url, + L"javascript:void(window.domAutomationController.setAutomationId(%d));", + message.routing_id()); + + web_contents->ExecuteJavascriptInWebFrame(frame_xpath, url); + web_contents->ExecuteJavascriptInWebFrame(frame_xpath, script); + succeeded = true; + } + } + + if (!succeeded) { + Send(new AutomationMsg_DomOperationResponse(message.routing_id(), "")); + } +} + +void AutomationProvider::GetShelfVisibility(const IPC::Message& message, + int handle) { + bool visible = false; + if (tab_tracker_->ContainsHandle(handle)) { + NavigationController* tab = tab_tracker_->GetResource(handle); + TabContents* tab_contents = tab->active_contents(); + if (tab_contents && tab_contents->type() == TAB_CONTENTS_WEB) { + WebContents* web_contents = tab_contents->AsWebContents(); + visible = web_contents->IsDownloadShelfVisible(); + } + } + + Send(new AutomationMsg_ShelfVisibilityResponse(message.routing_id(), + visible)); +} + +void AutomationProvider::GetConstrainedWindowCount(const IPC::Message& message, + int handle) { + int count = -1; // -1 is the error code + if (tab_tracker_->ContainsHandle(handle)) { + NavigationController* nav_controller = tab_tracker_->GetResource(handle); + TabContents* tab_contents = nav_controller->active_contents(); + if (tab_contents) { + count = static_cast<int>(tab_contents->child_windows_.size()); + } + } + + Send(new AutomationMsg_ConstrainedWindowCountResponse(message.routing_id(), + count)); +} + +void AutomationProvider::GetConstrainedWindow(const IPC::Message& message, + int handle, int index) { + int cwindow_handle = 0; + if (tab_tracker_->ContainsHandle(handle) && index >= 0) { + NavigationController* nav_controller = + tab_tracker_->GetResource(handle); + TabContents* tab = nav_controller->active_contents(); + if (tab && index < static_cast<int>(tab->child_windows().size())) { + ConstrainedWindow* window = tab->child_windows()[index]; + cwindow_handle = cwindow_tracker_->Add(window); + } + } + + Send(new AutomationMsg_ConstrainedWindowResponse(message.routing_id(), + cwindow_handle)); +} + +void AutomationProvider::GetConstrainedTitle(const IPC::Message& message, + int handle) { + int title_string_size = -1; // -1 is the error code + std::wstring title; + if (cwindow_tracker_->ContainsHandle(handle)) { + ConstrainedWindow* window = cwindow_tracker_->GetResource(handle); + title = window->GetWindowTitle(); + title_string_size = static_cast<int>(title.size()); + } + + Send(new AutomationMsg_ConstrainedTitleResponse(message.routing_id(), + title_string_size, title)); +} + +void AutomationProvider::GetConstrainedWindowBounds(const IPC::Message& message, + int handle) { + bool exists = false; + gfx::Rect rect(0, 0, 0, 0); + if (cwindow_tracker_->ContainsHandle(handle)) { + ConstrainedWindow* window = cwindow_tracker_->GetResource(handle); + if (window) { + exists = true; + rect = window->GetCurrentBounds(); + } + } + + Send(new AutomationMsg_ConstrainedWindowBoundsResponse(message.routing_id(), + exists, rect)); +} + +void AutomationProvider::HandleFindInPageRequest( + const IPC::Message& message, int handle, const std::wstring& find_request, + int forward, int match_case) { + if (!tab_tracker_->ContainsHandle(handle)) { + Send(new AutomationMsg_FindInPageResponse(message.routing_id(), -1)); + return; + } + + NavigationController* nav = tab_tracker_->GetResource(handle); + TabContents* tab_contents = nav->active_contents(); + + find_in_page_observer_.reset(new + FindInPageNotificationObserver(this, tab_contents, message.routing_id())); + + // The explicit comparison to TRUE avoids a warning (C4800). + tab_contents->StartFinding( + FindInPageNotificationObserver::kFindInPageRequestId, + find_request, forward == TRUE, match_case == TRUE, + false); // Not a FindNext operation. +} + +void AutomationProvider::HandleInspectElementRequest( + const IPC::Message& message, int handle, int x, int y) { + if (!tab_tracker_->ContainsHandle(handle)) { + Send(new AutomationMsg_InspectElementResponse(message.routing_id(), -1)); + return; + } + + NavigationController* nav = tab_tracker_->GetResource(handle); + TabContents* tab_contents = nav->active_contents(); + if (tab_contents->type() == TAB_CONTENTS_WEB) { + WebContents* web_contents = tab_contents->AsWebContents(); + web_contents->InspectElementAt(x, y); + inspect_element_routing_id_ = message.routing_id(); + } else { + Send(new AutomationMsg_InspectElementResponse(message.routing_id(), -1)); + } +} + +void AutomationProvider::ReceivedInspectElementResponse(int num_resources) { + Send(new AutomationMsg_InspectElementResponse(inspect_element_routing_id_, + num_resources)); +} + +// Helper class for making changes to the URLRequest ProtocolFactory on the +// IO thread. +class SetFilteredInetTask : public Task { + public: + explicit SetFilteredInetTask(bool enabled) : enabled_(enabled) { } + virtual void Run() { + if (enabled_) { + URLRequestFilter::GetInstance()->ClearHandlers(); + + URLRequestFailedDnsJob::AddUITestUrls(); + URLRequestSlowDownloadJob::AddUITestUrls(); + + std::wstring root_http; + PathService::Get(chrome::DIR_TEST_DATA, &root_http); + URLRequestMockHTTPJob::AddUITestUrls(root_http); + } else { + // Revert to the default handlers. + URLRequestFilter::GetInstance()->ClearHandlers(); + } + } + private: + bool enabled_; +}; + +void AutomationProvider::SetFilteredInet(const IPC::Message& message, + bool enabled) { + // Since this involves changing the URLRequest ProtocolFactory, we want to + // run on the main thread. + g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE, + new SetFilteredInetTask(enabled)); +} + +void AutomationProvider::GetDownloadDirectory(const IPC::Message& message, + int handle) { + DLOG(INFO) << "Handling download directory request"; + std::wstring download_directory; + if (tab_tracker_->ContainsHandle(handle)) { + NavigationController* tab = tab_tracker_->GetResource(handle); + DownloadManager* dlm = tab->profile()->GetDownloadManager(); + DCHECK(dlm); + download_directory = dlm->download_path(); + } + + Send(new AutomationMsg_DownloadDirectoryResponse(message.routing_id(), + download_directory)); +} + +void AutomationProvider::OpenNewBrowserWindow(int show_command) { + // We may have no current browser windows open so don't rely on + // asking an existing browser to execute the IDC_NEWWINDOW command + Browser::OpenNewBrowserWindow(profile_, show_command); +} + +void AutomationProvider::GetWindowForBrowser(const IPC::Message& message, + int browser_handle) { + bool success = false; + int window_handle = 0; + + if (browser_tracker_->ContainsHandle(browser_handle)) { + Browser* browser = browser_tracker_->GetResource(browser_handle); + HWND hwnd = browser->GetTopLevelHWND(); + // Add() returns the existing handle for the resource if any. + window_handle = window_tracker_->Add(hwnd); + success = true; + } + Send(new AutomationMsg_WindowForBrowserResponse(message.routing_id(), + success, window_handle)); +} + +void AutomationProvider::GetAutocompleteEditForBrowser( + const IPC::Message& message, + int browser_handle) { + bool success = false; + int autocomplete_edit_handle = 0; + + if (browser_tracker_->ContainsHandle(browser_handle)) { + Browser* browser = browser_tracker_->GetResource(browser_handle); + LocationBarView* loc_bar_view = browser->GetLocationBarView(); + AutocompleteEdit* autocomplete_edit = loc_bar_view->location_entry(); + // Add() returns the existing handle for the resource if any. + autocomplete_edit_handle = + autocomplete_edit_tracker_->Add(autocomplete_edit); + success = true; + } + Send(new AutomationMsg_AutocompleteEditForBrowserResponse( + message.routing_id(), success, autocomplete_edit_handle)); +} + +void AutomationProvider::GetBrowserForWindow(const IPC::Message& message, + int window_handle) { + bool success = false; + int browser_handle = 0; + + if (window_tracker_->ContainsHandle(window_handle)) { + HWND window = window_tracker_->GetResource(window_handle); + BrowserList::const_iterator iter = BrowserList::begin(); + Browser* browser = NULL; + for (;iter != BrowserList::end(); ++iter) { + if (window == (*iter)->GetTopLevelHWND()) { + browser = *iter; + break; + } + } + if (browser) { + // Add() returns the existing handle for the resource if any. + browser_handle = browser_tracker_->Add(browser); + success = true; + } + } + Send(new AutomationMsg_BrowserForWindowResponse(message.routing_id(), + success, browser_handle)); +} + +void AutomationProvider::ShowInterstitialPage(const IPC::Message& message, + int tab_handle, + const std::string& html_text) { + if (tab_tracker_->ContainsHandle(tab_handle)) { + NavigationController* controller = tab_tracker_->GetResource(tab_handle); + TabContents* tab_contents = controller->active_contents(); + if (tab_contents->type() == TAB_CONTENTS_WEB) { + AddNavigationStatusListener(controller, + new AutomationMsg_ShowInterstitialPageResponse(message.routing_id(), + true), + NULL); + WebContents* web_contents = tab_contents->AsWebContents(); + web_contents->ShowInterstitialPage(html_text, NULL); + return; + } + } + Send(new AutomationMsg_ShowInterstitialPageResponse(message.routing_id(), + false)); +} + +void AutomationProvider::HideInterstitialPage(const IPC::Message& message, + int tab_handle) { + if (tab_tracker_->ContainsHandle(tab_handle)) { + NavigationController* controller = tab_tracker_->GetResource(tab_handle); + TabContents* tab_contents = controller->active_contents(); + if (tab_contents->type() == TAB_CONTENTS_WEB) { + WebContents* web_contents = tab_contents->AsWebContents(); + web_contents->HideInterstitialPage(false, false); + Send(new AutomationMsg_HideInterstitialPageResponse(message.routing_id(), + true)); + return; + } + } + Send(new AutomationMsg_HideInterstitialPageResponse(message.routing_id(), + false)); +} + +void AutomationProvider::CloseTab(const IPC::Message& message, + int tab_handle, + bool wait_until_closed) { + if (tab_tracker_->ContainsHandle(tab_handle)) { + NavigationController* controller = tab_tracker_->GetResource(tab_handle); + int index; + Browser* browser = Browser::GetBrowserForController(controller, &index); + DCHECK(browser); + TabClosedNotificationObserver* observer = + new TabClosedNotificationObserver(browser, this, message.routing_id(), + wait_until_closed); + browser->CloseContents(controller->active_contents()); + } else { + Send(new AutomationMsg_CloseTabResponse(message.routing_id(), false)); + } +} + +void AutomationProvider::CloseBrowser(const IPC::Message& message, + int browser_handle) { + if (browser_tracker_->ContainsHandle(browser_handle)) { + Browser* browser = browser_tracker_->GetResource(browser_handle); + new BrowserClosedNotificationObserver(browser, this, message.routing_id()); + browser->frame()->Close(); + } else { + NOTREACHED(); + } +} + +void AutomationProvider::CreateExternalTab(const IPC::Message& message) { + int tab_handle = 0; + HWND tab_container_window = NULL; + ExternalTabContainer *external_tab_container = + new ExternalTabContainer(this); + external_tab_container->Init(profile_); + TabContents* tab_contents = external_tab_container->tab_contents(); + if (tab_contents) { + tab_handle = tab_tracker_->Add(tab_contents->controller()); + tab_container_window = *external_tab_container; + } + Send(new AutomationMsg_CreateExternalTabResponse(message.routing_id(), + tab_container_window, + tab_handle)); +} + +void AutomationProvider::NavigateInExternalTab(const IPC::Message& message, + int handle, const GURL& url) { + bool status = false; + + if (tab_tracker_->ContainsHandle(handle)) { + NavigationController* tab = tab_tracker_->GetResource(handle); + tab->LoadURL(url, PageTransition::TYPED); + status = true; + } + + Send(new AutomationMsg_NavigateInExternalTabResponse(message.routing_id(), + status)); +} + +void AutomationProvider::SetAcceleratorsForTab(const IPC::Message& message, + int handle, + HACCEL accel_table, + int accel_entry_count) { + bool status = false; + if (tab_tracker_->ContainsHandle(handle)) { + NavigationController* tab = tab_tracker_->GetResource(handle); + TabContents* tab_contents = tab->GetTabContents(TAB_CONTENTS_WEB); + ExternalTabContainer* external_tab_container = + ExternalTabContainer::GetContainerForTab( + tab_contents->GetContainerHWND()); + // This call is only valid on an externally hosted tab + if (external_tab_container) { + external_tab_container->SetAccelerators(accel_table, + accel_entry_count); + status = true; + } + } + Send(new AutomationMsg_SetAcceleratorsForTabResponse(message.routing_id(), + status)); +} + +void AutomationProvider::ProcessUnhandledAccelerator( + const IPC::Message& message, int handle, const MSG& msg) { + if (tab_tracker_->ContainsHandle(handle)) { + NavigationController* tab = tab_tracker_->GetResource(handle); + TabContents* tab_contents = tab->GetTabContents(TAB_CONTENTS_WEB); + ExternalTabContainer* external_tab_container = + ExternalTabContainer::GetContainerForTab( + tab_contents->GetContainerHWND()); + // This call is only valid on an externally hosted tab + if (external_tab_container) { + external_tab_container->ProcessUnhandledAccelerator(msg); + } + } + // This message expects no response. +} + +void AutomationProvider::WaitForTabToBeRestored( + const IPC::Message& message, + int tab_handle) { + if (tab_tracker_->ContainsHandle(tab_handle)) { + NavigationController* tab = tab_tracker_->GetResource(tab_handle); + restore_tracker_.reset( + new NavigationControllerRestoredObserver(this, tab, + message.routing_id())); + } +} + +void AutomationProvider::GetSecurityState(const IPC::Message& message, + int handle) { + if (tab_tracker_->ContainsHandle(handle)) { + NavigationController* tab = tab_tracker_->GetResource(handle); + NavigationEntry* entry = tab->GetActiveEntry(); + Send(new AutomationMsg_GetSecurityStateResponse(message.routing_id(), true, + entry->GetSecurityStyle(), entry->GetSSLCertStatus(), + entry->GetContentStatus())); + } else { + Send(new AutomationMsg_GetSecurityStateResponse(message.routing_id(), false, + SECURITY_STYLE_UNKNOWN, + 0, 0)); + } +} + +void AutomationProvider::GetPageType(const IPC::Message& message, int handle) { + if (tab_tracker_->ContainsHandle(handle)) { + NavigationController* tab = tab_tracker_->GetResource(handle); + NavigationEntry* entry = tab->GetActiveEntry(); + NavigationEntry::PageType page_type = entry->GetPageType(); + // In order to return the proper result when an interstitial is shown and + // no navigation entry were created for it we need to ask the WebContents. + if (page_type == NavigationEntry::NORMAL_PAGE && + tab->active_contents()->AsWebContents() && + tab->active_contents()->AsWebContents()->IsShowingInterstitialPage()) + page_type = NavigationEntry::INTERSTITIAL_PAGE; + + Send(new AutomationMsg_GetPageTypeResponse(message.routing_id(), true, + page_type)); + } else { + Send(new AutomationMsg_GetPageTypeResponse(message.routing_id(), false, + NavigationEntry::NORMAL_PAGE)); + } +} + +void AutomationProvider::ActionOnSSLBlockingPage(const IPC::Message& message, + int handle, bool proceed) { + if (tab_tracker_->ContainsHandle(handle)) { + NavigationController* tab = tab_tracker_->GetResource(handle); + NavigationEntry* entry = tab->GetActiveEntry(); + if (entry->GetPageType() == NavigationEntry::INTERSTITIAL_PAGE) { + TabContents* tab_contents = tab->GetTabContents(TAB_CONTENTS_WEB); + SSLBlockingPage* ssl_blocking_page = + SSLBlockingPage::GetSSLBlockingPage(tab_contents); + if (ssl_blocking_page) { + if (proceed) { + AddNavigationStatusListener(tab, + new AutomationMsg_ActionOnSSLBlockingPageResponse( + message.routing_id(), true), + new AutomationMsg_ActionOnSSLBlockingPageResponse( + message.routing_id(), true)); + ssl_blocking_page->Proceed(); + return; + } + ssl_blocking_page->DontProceed(); + Send(new AutomationMsg_ActionOnSSLBlockingPageResponse( + message.routing_id(), true)); + return; + } + } + } + // We failed. + Send(new AutomationMsg_ActionOnSSLBlockingPageResponse(message.routing_id(), + false)); +} + +void AutomationProvider::BringBrowserToFront(const IPC::Message& message, + int browser_handle) { + if (browser_tracker_->ContainsHandle(browser_handle)) { + Browser* browser = browser_tracker_->GetResource(browser_handle); + browser->MoveToFront(true); + Send(new AutomationMsg_BringBrowserToFrontResponse(message.routing_id(), + true)); + } else { + Send(new AutomationMsg_BringBrowserToFrontResponse(message.routing_id(), + false)); + } +} + +void AutomationProvider::IsPageMenuCommandEnabled(const IPC::Message& message, + int browser_handle, + int message_num) { + if (browser_tracker_->ContainsHandle(browser_handle)) { + Browser* browser = browser_tracker_->GetResource(browser_handle); + bool menu_item_enabled = + browser->controller()->IsCommandEnabled(message_num); + Send(new AutomationMsg_IsPageMenuCommandEnabledResponse( + message.routing_id(), menu_item_enabled)); + } else { + Send(new AutomationMsg_IsPageMenuCommandEnabledResponse( + message.routing_id(), false)); + } +} + +void AutomationProvider::PrintNow(const IPC::Message& message, int tab_handle) { + if (tab_tracker_->ContainsHandle(tab_handle)) { + NavigationController* tab = tab_tracker_->GetResource(tab_handle); + FindAndActivateTab(tab); + WebContents* const web_contents = tab->active_contents()->AsWebContents(); + if (web_contents) { + notification_observer_list_.AddObserver( + new DocumentPrintedNotificationObserver(this, message.routing_id())); + if (web_contents->PrintNow()) + return; + } + } + Send(new AutomationMsg_PrintNowResponse(message.routing_id(), false)); +} + +void AutomationProvider::SavePage(const IPC::Message& message, + int tab_handle, + const std::wstring& file_name, + const std::wstring& dir_path, + int type) { + if (!tab_tracker_->ContainsHandle(tab_handle)) { + Send(new AutomationMsg_SavePageResponse(message.routing_id(), false)); + return; + } + + NavigationController* nav = tab_tracker_->GetResource(tab_handle); + Browser* browser = FindAndActivateTab(nav); + DCHECK(browser); + if (!browser->IsCommandEnabled(IDC_SAVEPAGE)) { + Send(new AutomationMsg_SavePageResponse(message.routing_id(), false)); + return; + } + + TabContents* tab_contents = nav->active_contents(); + if (tab_contents->type() != TAB_CONTENTS_WEB) { + Send(new AutomationMsg_SavePageResponse(message.routing_id(), false)); + return; + } + + SavePackage::SavePackageType save_type = + static_cast<SavePackage::SavePackageType>(type); + DCHECK(save_type >= SavePackage::SAVE_AS_ONLY_HTML && + save_type <= SavePackage::SAVE_AS_COMPLETE_HTML); + tab_contents->AsWebContents()->SavePage(file_name, dir_path, save_type); + + Send(new AutomationMsg_SavePageResponse( + message.routing_id(), true)); +} + +void AutomationProvider::GetAutocompleteEditText(const IPC::Message& message, + int autocomplete_edit_handle) { + bool success = false; + std::wstring text; + if (autocomplete_edit_tracker_->ContainsHandle(autocomplete_edit_handle)) { + AutocompleteEdit* edit = autocomplete_edit_tracker_->GetResource( + autocomplete_edit_handle); + text = edit->GetText(); + success = true; + } + Send(new AutomationMsg_AutocompleteEditGetTextResponse(message.routing_id(), + success, text)); +} + +void AutomationProvider::SetAutocompleteEditText(const IPC::Message& message, + int autocomplete_edit_handle, + const std::wstring& text) { + bool success = false; + if (autocomplete_edit_tracker_->ContainsHandle(autocomplete_edit_handle)) { + AutocompleteEdit* edit = autocomplete_edit_tracker_->GetResource( + autocomplete_edit_handle); + edit->SetUserText(text); + success = true; + } + Send(new AutomationMsg_AutocompleteEditSetTextResponse( + message.routing_id(), success)); +} + +void AutomationProvider::AutocompleteEditGetMatches( + const IPC::Message& message, + int autocomplete_edit_handle) { + bool success = false; + std::vector<AutocompleteMatchData> matches; + if (autocomplete_edit_tracker_->ContainsHandle(autocomplete_edit_handle)) { + AutocompleteEdit* edit = + autocomplete_edit_tracker_->GetResource(autocomplete_edit_handle); + const AutocompleteResult* result = edit->latest_result(); + for (AutocompleteResult::const_iterator i = result->begin(); + i != result->end(); ++i) + matches.push_back(AutocompleteMatchData(*i)); + success = true; + } + Send(new AutomationMsg_AutocompleteEditGetMatchesResponse( + message.routing_id(), success, matches)); +} + +void AutomationProvider::AutocompleteEditIsQueryInProgress( + const IPC::Message& message, + int autocomplete_edit_handle) { + bool success = false; + bool query_in_progress = false; + if (autocomplete_edit_tracker_->ContainsHandle(autocomplete_edit_handle)) { + AutocompleteEdit* edit = + autocomplete_edit_tracker_->GetResource(autocomplete_edit_handle); + query_in_progress = edit->query_in_progress(); + success = true; + } + Send(new AutomationMsg_AutocompleteEditIsQueryInProgressResponse( + message.routing_id(), success, query_in_progress)); +} + +TestingAutomationProvider::TestingAutomationProvider(Profile* profile) + : AutomationProvider(profile) { + BrowserList::AddObserver(this); + NotificationService::current()->AddObserver(this, NOTIFY_SESSION_END, + NotificationService::AllSources()); +} + +TestingAutomationProvider::~TestingAutomationProvider() { + NotificationService::current()->RemoveObserver(this, NOTIFY_SESSION_END, + NotificationService::AllSources()); + BrowserList::RemoveObserver(this); +} + +void TestingAutomationProvider::OnChannelError() { + BrowserList::CloseAllBrowsers(true); + AutomationProvider::OnChannelError(); +} + +void TestingAutomationProvider::OnBrowserRemoving(const Browser* browser) { + // For backwards compatibility with the testing automation interface, we + // want the automation provider (and hence the process) to go away when the + // last browser goes away. + if (BrowserList::size() == 1) { + // If you change this, update Observer for NOTIFY_SESSION_END below. + MessageLoop::current()->ReleaseSoon(FROM_HERE, this); + } +} + +void TestingAutomationProvider::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == NOTIFY_SESSION_END); + // OnBrowserRemoving does a ReleaseLater. When session end is received we exit + // before the task runs resulting in this object not being deleted. This + // Release balance out the Release scheduled by OnBrowserRemoving. + Release(); +} |