diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/automation | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2 |
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/automation')
21 files changed, 4308 insertions, 0 deletions
diff --git a/chrome/browser/automation/automation_autocomplete_edit_tracker.h b/chrome/browser/automation/automation_autocomplete_edit_tracker.h new file mode 100644 index 0000000..24d688a --- /dev/null +++ b/chrome/browser/automation/automation_autocomplete_edit_tracker.h @@ -0,0 +1,59 @@ +// 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. + +#ifndef CHROME_BROWSER_AUTOMATION_AUTOMATION_AUTOCOMPLETE_EDIT_TRACKER_H__ +#define CHROME_BROWSER_AUTOMATION_AUTOMATION_AUTOCOMPLETE_EDIT_TRACKER_H__ + +#include "chrome/browser/autocomplete/autocomplete_edit.h" +#include "chrome/browser/automation/automation_resource_tracker.h" + +class AutomationAutocompleteEditTracker: + public AutomationResourceTracker<AutocompleteEdit*> { + public: + explicit AutomationAutocompleteEditTracker(IPC::Message::Sender* automation) + : AutomationResourceTracker(automation) { } + + virtual ~AutomationAutocompleteEditTracker() { + ClearAllMappings(); + } + + virtual void AddObserver(AutocompleteEdit* resource) { + NotificationService::current()->AddObserver( + this, NOTIFY_AUTOCOMPLETE_EDIT_DESTROYED, + Source<AutocompleteEdit>(resource)); + } + + virtual void RemoveObserver(AutocompleteEdit* resource) { + NotificationService::current()->RemoveObserver( + this, NOTIFY_AUTOCOMPLETE_EDIT_DESTROYED, + Source<AutocompleteEdit>(resource)); + } +}; + +#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_AUTOCOMPLETE_EDIT_TRACKER_H__ diff --git a/chrome/browser/automation/automation_browser_tracker.h b/chrome/browser/automation/automation_browser_tracker.h new file mode 100644 index 0000000..2a4dfd9 --- /dev/null +++ b/chrome/browser/automation/automation_browser_tracker.h @@ -0,0 +1,58 @@ +// 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. + +#ifndef CHROME_BROWSER_AUTOMATION_AUTOMATION_BROWSER_TRACKER_H__ +#define CHROME_BROWSER_AUTOMATION_AUTOMATION_BROWSER_TRACKER_H__ + +#include "chrome/browser/automation/automation_resource_tracker.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/browser_process.h" + +// Tracks Browser objects. +class AutomationBrowserTracker : public AutomationResourceTracker<Browser*> { +public: + AutomationBrowserTracker(IPC::Message::Sender* automation) + : AutomationResourceTracker(automation) { } + + virtual ~AutomationBrowserTracker() { + ClearAllMappings(); + } + + virtual void AddObserver(Browser* resource) { + NotificationService::current()->AddObserver( + this, NOTIFY_BROWSER_CLOSED, Source<Browser>(resource)); + } + + virtual void RemoveObserver(Browser* resource) { + NotificationService::current()->RemoveObserver( + this, NOTIFY_BROWSER_CLOSED, Source<Browser>(resource)); + } +}; + +#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_BROWSER_TRACKER_H__ diff --git a/chrome/browser/automation/automation_constrained_window_tracker.h b/chrome/browser/automation/automation_constrained_window_tracker.h new file mode 100644 index 0000000..e6abd94 --- /dev/null +++ b/chrome/browser/automation/automation_constrained_window_tracker.h @@ -0,0 +1,59 @@ +// 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. + +#ifndef CHROME_BROWSER_AUTOMATION_AUTOMATION_CONSTRAINED_WINDOW_TRACKER_H__ +#define CHROME_BROWSER_AUTOMATION_AUTOMATION_CONSTRAINED_WINDOW_TRACKER_H__ + +#include "chrome/browser/automation/automation_resource_tracker.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/constrained_window.h" + +class AutomationConstrainedWindowTracker + : public AutomationResourceTracker<ConstrainedWindow*> { +public: + AutomationConstrainedWindowTracker(IPC::Message::Sender* automation) + : AutomationResourceTracker(automation) {} + + virtual ~AutomationConstrainedWindowTracker() { + ClearAllMappings(); + } + + virtual void AddObserver(ConstrainedWindow* resource) { + NotificationService::current()->AddObserver( + this, NOTIFY_CWINDOW_CLOSED, Source<ConstrainedWindow>(resource)); + } + + virtual void RemoveObserver(ConstrainedWindow* resource) { + NotificationService::current()->RemoveObserver( + this, NOTIFY_CWINDOW_CLOSED, Source<ConstrainedWindow>(resource)); + } +}; + +#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_CONSTRAINED_WINDOW_TRACKER_H__
\ No newline at end of file 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(); +} diff --git a/chrome/browser/automation/automation_provider.h b/chrome/browser/automation/automation_provider.h new file mode 100644 index 0000000..249bc7e --- /dev/null +++ b/chrome/browser/automation/automation_provider.h @@ -0,0 +1,379 @@ +// 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. + +// This implements a browser-side endpoint for UI automation activity. +// The client-side endpoint is implemented by AutomationProxy. +// The entire lifetime of this object should be contained within that of +// the BrowserProcess, and in particular the NotificationService that's +// hung off of it. + +#ifndef CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_H__ +#define CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_H__ + +#include <map> +#include <string> +#include <vector> + +#include "chrome/browser/automation/automation_browser_tracker.h" +#include "chrome/browser/automation/automation_constrained_window_tracker.h" +#include "chrome/browser/automation/automation_tab_tracker.h" +#include "chrome/browser/automation/automation_window_tracker.h" +#include "chrome/browser/automation/automation_autocomplete_edit_tracker.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/history/history.h" +#include "chrome/common/ipc_channel.h" +#include "chrome/common/ipc_message.h" +#include "chrome/common/notification_service.h" + +class LoginHandler; +class NavigationControllerRestoredObserver; + +class AutomationProvider : public base::RefCounted<AutomationProvider>, + public IPC::Channel::Listener, + public IPC::Message::Sender { + public: + AutomationProvider(Profile* profile); + virtual ~AutomationProvider(); + + // Establishes a connection to an automation client, if present. + // An AutomationProxy should be established (probably in a different process) + // before calling this. + void ConnectToChannel(const std::wstring& channel_id); + + // Sets the number of tabs that we expect; when this number of tabs has + // loaded, an AutomationMsg_InitialLoadsComplete message is sent. + void SetExpectedTabCount(size_t expected_tabs); + + // Add a listener for navigation status notification. Currently only + // navigation completion is observed; when the navigation completes, the + // completed_response object is sent; if the server requires authentication, + // we instead send the auth_needed_response object. A pointer to the added + // navigation observer is returned. This object should NOT be deleted and + // should be released by calling the corresponding + // RemoveNavigationStatusListener method. + NotificationObserver* AddNavigationStatusListener( + NavigationController* tab, IPC::Message* completed_response, + IPC::Message* auth_needed_response); + void RemoveNavigationStatusListener(NotificationObserver* obs); + + // Add an observer for the TabStrip. Currently only Tab append is observed. A + // navigation listener is created on successful notification of tab append. A + // pointer to the added navigation observer is returned. This object should + // NOT be deleted and should be released by calling the corresponding + // RemoveTabStripObserver method. + NotificationObserver* AddTabStripObserver(Browser* parent, + int32 routing_id); + void RemoveTabStripObserver(NotificationObserver* obs); + + // Get the index of a particular NavigationController object + // in the given parent window. This method uses + // TabStrip::GetIndexForNavigationController to get the index. + int GetIndexForNavigationController(const NavigationController* controller, + const Browser* parent) const; + + // Add or remove a non-owning reference to a tab's LoginHandler. This is for + // when a login prompt is shown for HTTP/FTP authentication. + // TODO(mpcomplete): The login handling is a fairly special purpose feature. + // Eventually we'll probably want ways to interact with the ChromeView of the + // login window in a generic manner, such that it can be used for anything, + // not just logins. + void AddLoginHandler(NavigationController* tab, LoginHandler* handler); + void RemoveLoginHandler(NavigationController* tab); + + // IPC implementations + virtual bool Send(IPC::Message* msg); + virtual void OnMessageReceived(const IPC::Message& msg); + virtual void OnChannelError(); + + // Received response from inspector controller + void ReceivedInspectElementResponse(int num_resources); + + private: + // IPC Message callbacks. + void CloseBrowser(const IPC::Message& message, int handle); + void ActivateTab(const IPC::Message& message, int handle, int at_index); + void AppendTab(const IPC::Message& message, int handle, const GURL& url); + void CloseTab(const IPC::Message& message, + int tab_handle, + bool wait_until_closed); + + void GetActiveTabIndex(const IPC::Message& message, int handle); + void GetCookies(const IPC::Message& message, const GURL& url, int handle); + void SetCookie(const IPC::Message& message, + const GURL& url, + const std::string value, + int handle); + void GetBrowserWindowCount(const IPC::Message& message); + void GetBrowserWindow(const IPC::Message& message, int index); + void GetLastActiveBrowserWindow(const IPC::Message& message); + void GetActiveWindow(const IPC::Message& message); + void GetWindowHWND(const IPC::Message& message, int handle); + void WindowGetViewBounds(const IPC::Message& message, + int handle, + int view_id, + bool screen_coordinates); + void WindowSimulateDrag(const IPC::Message& message, + int handle, + std::vector<POINT> drag_path, + int flags); + void WindowSimulateClick(const IPC::Message& message, + int handle, + POINT click, + int flags); + void WindowSimulateKeyPress(const IPC::Message& message, + int handle, + wchar_t key, + int flags); + void SetWindowVisible(const IPC::Message& message, int handle, bool visible); + void IsWindowActive(const IPC::Message& message, int handle); + void ActivateWindow(const IPC::Message& message, int handle); + + void GetTabCount(const IPC::Message& message, int handle); + void GetTab(const IPC::Message& message, int win_handle, int tab_index); + void GetTabHWND(const IPC::Message& message, int handle); + void GetTabProcessID(const IPC::Message& message, int handle); + void GetTabTitle(const IPC::Message& message, int handle); + void GetTabURL(const IPC::Message& message, int handle); + void HandleUnused(const IPC::Message& message, int handle); + void NavigateToURL(const IPC::Message& message, int handle, const GURL& url); + void NavigationAsync(const IPC::Message& message, + int handle, + const GURL& url); + void GoBack(const IPC::Message& message, int handle); + void GoForward(const IPC::Message& message, int handle); + void Reload(const IPC::Message& message, int handle); + void SetAuth(const IPC::Message& message, int tab_handle, + const std::wstring& username, const std::wstring& password); + void CancelAuth(const IPC::Message& message, int tab_handle); + void NeedsAuth(const IPC::Message& message, int tab_handle); + void GetRedirectsFrom(const IPC::Message& message, + int tab_handle, + const GURL& source_url); + void ExecuteJavascript(const IPC::Message& message, + int handle, + const std::wstring& frame_xpath, + const std::wstring& script); + void GetShelfVisibility(const IPC::Message& message, int handle); + void SetFilteredInet(const IPC::Message& message, bool enabled); + + void ScheduleMouseEvent(ChromeViews::View* view, + ChromeViews::Event::EventType type, + POINT point, + int flags); + void GetFocusedViewID(const IPC::Message& message, int handle); + + // Helper function to find the browser window that contains a given + // NavigationController and activate that tab. + // Returns the Browser if found. + Browser* FindAndActivateTab(NavigationController* contents); + + // Apply an accelerator with id (like IDC_BACK, IDC_FORWARD ...) + // to the Browser with given handle. + void ApplyAccelerator(int handle, int id); + + void GetConstrainedWindowCount(const IPC::Message& message, + int handle); + void GetConstrainedWindow(const IPC::Message& message, int handle, int index); + + void GetConstrainedTitle(const IPC::Message& message, int handle); + + void GetConstrainedWindowBounds(const IPC::Message& message, + int handle); + + // Responds to the FindInPage request, retrieves the search query parameters, + // launches an observer to listen for results and issues a StartFind request. + void HandleFindInPageRequest(const IPC::Message& message, + int handle, + const std::wstring& find_request, + int forward, + int match_case); + + // Responds to InspectElement request + void HandleInspectElementRequest(const IPC::Message& message, + int handle, + int x, + int y); + + void GetDownloadDirectory(const IPC::Message& message, int handle); + + // Retrieves a Browser from a Window and vice-versa. + void GetWindowForBrowser(const IPC::Message& message, int window_handle); + void GetBrowserForWindow(const IPC::Message& message, int browser_handle); + + void GetAutocompleteEditForBrowser(const IPC::Message& message, + int browser_handle); + + void OpenNewBrowserWindow(int show_command); + + void ShowInterstitialPage(const IPC::Message& message, + int tab_handle, + const std::string& html_text); + void HideInterstitialPage(const IPC::Message& message, int tab_handle); + + void CreateExternalTab(const IPC::Message& message); + void NavigateInExternalTab(const IPC::Message& message, int handle, + const GURL& url); + // The container of an externally hosted tab calls this to reflect any + // accelerator keys that it did not process. This gives the tab a chance + // to handle the keys + void ProcessUnhandledAccelerator(const IPC::Message& message, int handle, + const MSG& msg); + + // See comment in AutomationMsg_WaitForTabToBeRestored. + void WaitForTabToBeRestored(const IPC::Message& message, int tab_handle); + + // This sets the keyboard accelerators to be used by an externally + // hosted tab. This call is not valid on a regular tab hosted within + // Chrome. + void SetAcceleratorsForTab(const IPC::Message& message, int handle, + HACCEL accel_table, int accel_entry_count); + + // Gets the security state for the tab associated to the specified |handle|. + void GetSecurityState(const IPC::Message& message, int handle); + + // Gets the page type for the tab associated to the specified |handle|. + void GetPageType(const IPC::Message& message, int handle); + + // Simulates an action on the SSL blocking page at the tab specified by + // |handle|. If |proceed| is true, it is equivalent to the user pressing the + // 'Proceed' button, if false the 'Get me out of there button'. + // Not that this fails if the tab is not displaying a SSL blocking page. + void ActionOnSSLBlockingPage(const IPC::Message& message, + int handle, + bool proceed); + + // Brings the browser window to the front and activates it. + void BringBrowserToFront(const IPC::Message& message, int browser_handle); + + // Checks to see if a command on the browser's CommandController is enabled. + void IsPageMenuCommandEnabled(const IPC::Message& message, + int browser_handle, + int message_num); + + // Prints the current tab immediately. + void PrintNow(const IPC::Message& message, int tab_handle); + + // Save the current web page. + void SavePage(const IPC::Message& message, + int tab_handle, + const std::wstring& file_name, + const std::wstring& dir_path, + int type); + + // Retrieves the visible text from the autocomplete edit. + void GetAutocompleteEditText(const IPC::Message& message, + int autocomplete_edit_handle); + + // Sets the visible text from the autocomplete edit. + void SetAutocompleteEditText(const IPC::Message& message, + int autocomplete_edit_handle, + const std::wstring& text); + + // Retrieves if a query to an autocomplete provider is in progress. + void AutocompleteEditIsQueryInProgress(const IPC::Message& message, + int autocomplete_edit_handle); + + // Retrieves the individual autocomplete matches displayed by the popup. + void AutocompleteEditGetMatches(const IPC::Message& message, + int autocomplete_edit_handle); + + // Callback for history redirect queries. + virtual void OnRedirectQueryComplete( + HistoryService::Handle request_handle, + GURL from_url, + bool success, + HistoryService::RedirectList* redirects); + + typedef ObserverList<NotificationObserver> NotificationObserverList; + typedef std::map<NavigationController*, LoginHandler*> LoginHandlerMap; + + bool connected_; + scoped_ptr<IPC::Channel> channel_; + scoped_ptr<NotificationObserver> initial_load_observer_; + scoped_ptr<NotificationObserver> new_tab_ui_load_observer_; + scoped_ptr<NotificationObserver> find_in_page_observer_; + scoped_ptr<NotificationObserver> dom_operation_observer_; + scoped_ptr<NotificationObserver> dom_inspector_observer_; + scoped_ptr<AutomationTabTracker> tab_tracker_; + scoped_ptr<AutomationConstrainedWindowTracker> cwindow_tracker_; + scoped_ptr<AutomationWindowTracker> window_tracker_; + scoped_ptr<AutomationBrowserTracker> browser_tracker_; + scoped_ptr<AutomationAutocompleteEditTracker> autocomplete_edit_tracker_; + scoped_ptr<NavigationControllerRestoredObserver> restore_tracker_; + LoginHandlerMap login_handler_map_; + NotificationObserverList notification_observer_list_; + + // Handle for an in-process redirect query. We expect only one redirect query + // at a time (we should have only one caller, and it will block while waiting + // for the results) so there is only one handle. When non-0, indicates a + // query in progress. The routing ID will be set when the query is valid so + // we know where to send the response. + HistoryService::Handle redirect_query_; + int redirect_query_routing_id_; + + // routing id for inspect element request so that we can send back the + // response later + int inspect_element_routing_id_; + + // Consumer for asynchronous history queries. + CancelableRequestConsumer consumer_; + + Profile* profile_; + + DISALLOW_EVIL_CONSTRUCTORS(AutomationProvider); +}; + +// When life started, the AutomationProvider class was a singleton and was meant +// only for UI tests. It had specific behavior (like for example, when the channel +// was shut down. it closed all open Browsers). The new AutomationProvider serves +// other purposes than just UI testing. This class is meant to provide the OLD +// functionality for backward compatibility +class TestingAutomationProvider : public AutomationProvider, + public BrowserList::Observer, + public NotificationObserver { + public: + explicit TestingAutomationProvider(Profile* profile); + virtual ~TestingAutomationProvider(); + + // BrowserList::Observer implementation + // Called immediately after a browser is added to the list + virtual void OnBrowserAdded(const Browser* browser) { + } + // Called immediately before a browser is removed from the list + virtual void OnBrowserRemoving(const Browser* browser); + + // IPC implementations + virtual void OnChannelError(); + + private: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); +}; +#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_H__ diff --git a/chrome/browser/automation/automation_provider_list.cc b/chrome/browser/automation/automation_provider_list.cc new file mode 100644 index 0000000..4d9bb93 --- /dev/null +++ b/chrome/browser/automation/automation_provider_list.cc @@ -0,0 +1,78 @@ +// 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_list.h" + +#include <algorithm> +#include "base/logging.h" +#include "chrome/browser/automation/automation_provider.h" + +AutomationProviderList* AutomationProviderList::instance_ = NULL; + +AutomationProviderList::AutomationProviderList() { +} + +AutomationProviderList::~AutomationProviderList() { + iterator iter = automation_providers_.begin(); + while (iter != automation_providers_.end()) { + AutomationProvider* provider = (*iter); + // Delete the entry first and update the iterator first because the d'tor + // of AutomationProvider will call RemoveProvider, making this iterator + // invalid + iter = automation_providers_.erase(iter); + g_browser_process->ReleaseModule(); + delete provider; + } + instance_ = NULL; +} + +bool AutomationProviderList::AddProvider(AutomationProvider* provider) { + automation_providers_.push_back(provider); + g_browser_process->AddRefModule(); + return true; +} + +bool AutomationProviderList::RemoveProvider(AutomationProvider* provider) { + const iterator remove_provider = + find(automation_providers_.begin(), automation_providers_.end(), provider); + if (remove_provider != automation_providers_.end()) { + automation_providers_.erase(remove_provider); + g_browser_process->ReleaseModule(); + return true; + } + return false; +} + +AutomationProviderList* AutomationProviderList::GetInstance() { + if (!instance_) { + instance_ = new AutomationProviderList; + } + DCHECK(NULL != instance_); + return instance_; +} diff --git a/chrome/browser/automation/automation_provider_list.h b/chrome/browser/automation/automation_provider_list.h new file mode 100644 index 0000000..0397725 --- /dev/null +++ b/chrome/browser/automation/automation_provider_list.h @@ -0,0 +1,71 @@ +// 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. +#ifndef CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_LIST_H__ +#define CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_LIST_H__ + +#include <vector> +#include "base/basictypes.h" + +class AutomationProvider; + +// Stores a list of all AutomationProvider objects. +class AutomationProviderList { + public: + ~AutomationProviderList(); + typedef std::vector<AutomationProvider*> list_type; + typedef list_type::iterator iterator; + typedef list_type::const_iterator const_iterator; + + // Adds and removes automation providers from the global list. + bool AddProvider(AutomationProvider* provider); + bool RemoveProvider(AutomationProvider* provider); + + const_iterator begin() { + return automation_providers_.begin(); + } + + const_iterator end() { + return automation_providers_.end(); + } + + size_t size() { + return automation_providers_.size(); + } + + static AutomationProviderList* GetInstance(); + +private: + AutomationProviderList(); + list_type automation_providers_; + static AutomationProviderList* instance_; + + DISALLOW_EVIL_CONSTRUCTORS(AutomationProviderList); +}; + +#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_LIST_H__ diff --git a/chrome/browser/automation/automation_resource_tracker.cc b/chrome/browser/automation/automation_resource_tracker.cc new file mode 100644 index 0000000..177f230 --- /dev/null +++ b/chrome/browser/automation/automation_resource_tracker.cc @@ -0,0 +1,107 @@ +// 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_resource_tracker.h" + +#include "chrome/test/automation/automation_messages.h" + +int AutomationResourceTrackerImpl::AddImpl(void* resource) { + if (ContainsResourceImpl(resource)) + return resource_to_handle_[resource]; + + int handle = GenerateHandle(); + DCHECK(!ContainsHandleImpl(handle)); + + resource_to_handle_[resource] = handle; + handle_to_resource_[handle] = resource; + + AddObserverTypeProxy(resource); + + return handle; +} + +void AutomationResourceTrackerImpl::RemoveImpl(void* resource) { + if (!ContainsResourceImpl(resource)) + return; + + int handle = resource_to_handle_[resource]; + DCHECK(handle_to_resource_[handle] == resource); + + RemoveObserverTypeProxy(resource); + + resource_to_handle_.erase(resource); + handle_to_resource_.erase(handle); +} + +int AutomationResourceTrackerImpl::GenerateHandle() { + static int handle = 0; + return ++handle; +} + +bool AutomationResourceTrackerImpl::ContainsResourceImpl(void* resource) { + return resource_to_handle_.find(resource) != resource_to_handle_.end(); +} + +bool AutomationResourceTrackerImpl::ContainsHandleImpl(int handle) { + return handle_to_resource_.find(handle) != handle_to_resource_.end(); +} + +void AutomationResourceTrackerImpl::ClearAllMappingsImpl() { + while (!resource_to_handle_.empty()) { + RemoveImpl(resource_to_handle_.begin()->first); + } + cleared_mappings_ = true; +} + +void* AutomationResourceTrackerImpl::GetResourceImpl(int handle) { + HandleToResourceMap::const_iterator iter = handle_to_resource_.find(handle); + if (iter == handle_to_resource_.end()) + return NULL; + + return iter->second; +} + +int AutomationResourceTrackerImpl::GetHandleImpl(void* resource) { + ResourceToHandleMap::const_iterator iter = + resource_to_handle_.find(resource); + if (iter == resource_to_handle_.end()) + return 0; + + return iter->second; +} + +void AutomationResourceTrackerImpl::HandleCloseNotification(void* resource) { + if (!ContainsResourceImpl(resource)) + return; + + sender_->Send( + new AutomationMsg_InvalidateHandle(0, resource_to_handle_[resource])); + + RemoveImpl(resource); +} diff --git a/chrome/browser/automation/automation_resource_tracker.h b/chrome/browser/automation/automation_resource_tracker.h new file mode 100644 index 0000000..f2380e9 --- /dev/null +++ b/chrome/browser/automation/automation_resource_tracker.h @@ -0,0 +1,180 @@ +// 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. + +#ifndef CHROME_BROWSER_AUTOMATION_AUTOMATION_RESOURCE_TRACKER_H__ +#define CHROME_BROWSER_AUTOMATION_AUTOMATION_RESOURCE_TRACKER_H__ + +#include <map> + +#include "base/basictypes.h" +#include "chrome/common/ipc_message.h" +#include "chrome/common/notification_service.h" + +// Template trick so that AutomationResourceTracker can be used with non-pointer +// types. +template <class T> +struct AutomationResourceTraits { + typedef T ValueType; +}; + +template <class T> +struct AutomationResourceTraits<T*> { + typedef T ValueType; +}; + +// This class exists for the sole purpose of allowing some of the implementation +// of AutomationResourceTracker to live in a .cc file. +class AutomationResourceTrackerImpl { +public: + AutomationResourceTrackerImpl(IPC::Message::Sender* sender) + :sender_(sender), cleared_mappings_(false) {} + + virtual ~AutomationResourceTrackerImpl() {} + + // These need to be implemented in AutomationResourceTracker, + // since it needs to call the subclass's type-specific notification + // registration functions. + virtual void AddObserverTypeProxy(void* resource) = 0; + virtual void RemoveObserverTypeProxy(void* resource) = 0; + + int AddImpl(void* resource); + void RemoveImpl(void* resource); + int GenerateHandle(); + bool ContainsResourceImpl(void* resource); + bool ContainsHandleImpl(int handle); + void ClearAllMappingsImpl(); + void* GetResourceImpl(int handle); + int GetHandleImpl(void* resource); + void HandleCloseNotification(void* resource); + +protected: + bool cleared_mappings_; + typedef std::map<void*, int> ResourceToHandleMap; + typedef std::map<int, void*> HandleToResourceMap; + ResourceToHandleMap resource_to_handle_; + HandleToResourceMap handle_to_resource_; + +private: + DISALLOW_EVIL_CONSTRUCTORS(AutomationResourceTrackerImpl); + + IPC::Message::Sender* sender_; +}; + +// This template defines a superclass for an object that wants to track +// a particular kind of application resource (like windows or tabs) for +// automation purposes. The only things that a subclass should need to +// define are AddObserver and RemoveObserver for the given resource's +// close notifications, ***and a destructor that calls ClearAllMappings***. +template <class T> +class AutomationResourceTracker : public NotificationObserver, + private AutomationResourceTrackerImpl { + public: + AutomationResourceTracker(IPC::Message::Sender* automation) + : AutomationResourceTrackerImpl(automation) {} + + virtual ~AutomationResourceTracker() { + // NOTE: Be sure to call ClearAllMappings() from the destructor of your + // subclass! It can't be called here, because it eventually uses + // the subclass's RemoveObserver, which no longer exists by the time + // this base class destructor is executed. + DCHECK(cleared_mappings_); + } + + // Removes all mappings from this tracker, including unregistering from + // any associated resource notifications (via Remove calling RemoveObserver). + void ClearAllMappings() { + ClearAllMappingsImpl(); + } + + // The implementations for these should call the NotificationService + // to add and remove this object as an observer for the appropriate + // resource closing notification. + virtual void AddObserver(T resource) = 0; + virtual void RemoveObserver(T resource) = 0; + + // Adds the given resource to this tracker, and returns a handle that + // can be used to refer to that resource. If the resource is already + // being tracked, the handle may be the same as one returned previously. + int Add(T resource) { + return AddImpl(resource); + } + + // Removes the given resource from this tracker. If the resource is not + // currently present in the tracker, this is a no-op. + void Remove(T resource) { + RemoveImpl(resource); + } + + // Returns true if this tracker currently tracks the resource pointed to + // by the parameter. + bool ContainsResource(T resource) { + return ContainsResourceImpl(resource); + } + + // Returns true if this tracker currently tracks the given handle. + bool ContainsHandle(int handle) { + return ContainsHandleImpl(handle); + } + + // Returns the resource pointer associated with a given handle, or NULL + // if that handle is not present in the mapping. + T GetResource(int handle) { + return static_cast<T>(GetResourceImpl(handle)); + } + + // Returns the handle associated with a given resource pointer, or 0 if + // the resource is not currently in the mapping. + int GetHandle(T resource) { + return GetHandleImpl(resource); + } + + // NotificationObserver implementation--the only thing that this tracker + // does in response to notifications is to tell the AutomationProxy + // that the associated handle is now invalid. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details){ + T resource = + Source<typename AutomationResourceTraits<T>::ValueType>(source).ptr(); + + HandleCloseNotification(resource); + } + private: + // These proxy calls from the base Impl class to the template's subclss. + virtual void AddObserverTypeProxy(void* resource) { + AddObserver(static_cast<T>(resource)); + } + virtual void RemoveObserverTypeProxy(void* resource) { + RemoveObserver(static_cast<T>(resource)); + } + + DISALLOW_EVIL_CONSTRUCTORS(AutomationResourceTracker); +}; + +#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_RESOURCE_TRACKER_H__ diff --git a/chrome/browser/automation/automation_tab_tracker.h b/chrome/browser/automation/automation_tab_tracker.h new file mode 100644 index 0000000..6e9d930 --- /dev/null +++ b/chrome/browser/automation/automation_tab_tracker.h @@ -0,0 +1,67 @@ +// 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. + +#ifndef CHROME_BROWSER_AUTOMATION_AUTOMATION_TAB_TRACKER_H__ +#define CHROME_BROWSER_AUTOMATION_AUTOMATION_TAB_TRACKER_H__ + +#include "chrome/browser/automation/automation_resource_tracker.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/navigation_controller.h" + +class AutomationTabTracker + : public AutomationResourceTracker<NavigationController*> { +public: + AutomationTabTracker(IPC::Message::Sender* automation) + : AutomationResourceTracker(automation) {} + + virtual ~AutomationTabTracker() { + ClearAllMappings(); + } + + virtual void AddObserver(NavigationController* resource) { + // This tab could either be a regular tab or an external tab + // Register for both notifications + NotificationService::current()->AddObserver( + this, NOTIFY_TAB_CLOSING, Source<NavigationController>(resource)); + NotificationService::current()->AddObserver( + this, NOTIFY_EXTERNAL_TAB_CLOSED, + Source<NavigationController>(resource)); + } + + virtual void RemoveObserver(NavigationController* resource) { + NotificationService::current()->RemoveObserver( + this, NOTIFY_TAB_CLOSING, Source<NavigationController>(resource)); + NotificationService::current()->RemoveObserver( + this, NOTIFY_EXTERNAL_TAB_CLOSED, + Source<NavigationController>(resource)); + } +}; + +#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_TAB_TRACKER_H__ diff --git a/chrome/browser/automation/automation_window_tracker.h b/chrome/browser/automation/automation_window_tracker.h new file mode 100644 index 0000000..d1f18a0 --- /dev/null +++ b/chrome/browser/automation/automation_window_tracker.h @@ -0,0 +1,56 @@ +// 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. + +#ifndef CHROME_BROWSER_AUTOMATION_AUTOMATION_WINDOW_TRACKER_H__ +#define CHROME_BROWSER_AUTOMATION_AUTOMATION_WINDOW_TRACKER_H__ + +#include "chrome/browser/automation/automation_resource_tracker.h" +#include "chrome/views/hwnd_notification_source.h" + +class AutomationWindowTracker + : public AutomationResourceTracker<HWND> { + public: + AutomationWindowTracker(IPC::Message::Sender* automation) : + AutomationResourceTracker(automation) { } + virtual ~AutomationWindowTracker() { + ClearAllMappings(); + } + + virtual void AddObserver(HWND resource) { + NotificationService::current()->AddObserver( + this, NOTIFY_WINDOW_CLOSED, Source<HWND>(resource)); + } + + virtual void RemoveObserver(HWND resource) { + NotificationService::current()->RemoveObserver( + this, NOTIFY_WINDOW_CLOSED, Source<HWND>(resource)); + } +}; + +#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_WINDOW_TRACKER_H__ diff --git a/chrome/browser/automation/ui_controls.cc b/chrome/browser/automation/ui_controls.cc new file mode 100644 index 0000000..811bd89 --- /dev/null +++ b/chrome/browser/automation/ui_controls.cc @@ -0,0 +1,182 @@ +// 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/ui_controls.h" + +#include "base/logging.h" + +// Methods private to this file + +// Populate the INPUT structure with the appropriate keyboard event +// parameters required by SendInput +bool FillKeyboardInput(wchar_t key, INPUT* input, bool key_up) { + memset(input, 0, sizeof(INPUT)); + input->type = INPUT_KEYBOARD; + input->ki.wVk = static_cast<WORD>(key); + input->ki.dwFlags = key_up ? KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP : + KEYEVENTF_EXTENDEDKEY; + + return true; +} + +// Send a key event (up/down) +bool SendKeyEvent(wchar_t key, bool up) { + INPUT input = { 0 }; + + if (!FillKeyboardInput(key, &input, up)) + return false; + + if (!::SendInput(1, &input, sizeof(INPUT))) + return false; + + return true; +} + +namespace ui_controls { + +bool SendKeyPress(wchar_t key, bool control, bool shift, bool alt) { + INPUT input[8] = { 0 }; // 8, assuming all the modifiers are activated + + int i = 0; + if (control) { + if (!FillKeyboardInput(VK_CONTROL, &input[i], false)) + return false; + i++; + } + + if (shift) { + if (!FillKeyboardInput(VK_SHIFT, &input[i], false)) + return false; + i++; + } + + if (alt) { + if (!FillKeyboardInput(VK_MENU, &input[i], false)) + return false; + i++; + } + + if (!FillKeyboardInput(key, &input[i], false)) + return false; + i++; + + if (!FillKeyboardInput(key, &input[i], true)) + return false; + i++; + + if (alt) { + if (!FillKeyboardInput(VK_MENU, &input[i], true)) + return false; + i++; + } + + if (shift) { + if (!FillKeyboardInput(VK_SHIFT, &input[i], true)) + return false; + i++; + } + + if (control) { + if (!FillKeyboardInput(VK_CONTROL, &input[i], true)) + return false; + i++; + } + + + unsigned int rv = ::SendInput(i, input, sizeof(INPUT)); + + return rv == i; +} + +bool SendKeyDown(wchar_t key) { + return SendKeyEvent(key, false); +} + +bool SendKeyUp(wchar_t key) { + return SendKeyEvent(key, true); +} + +bool SendMouseMove(long x, long y) { + INPUT input = { 0 }; + + int screen_width = ::GetSystemMetrics(SM_CXSCREEN) - 1; + int screen_height = ::GetSystemMetrics(SM_CYSCREEN) - 1; + LONG pixel_x = static_cast<LONG>(x * (65535.0f / screen_width)); + LONG pixel_y = static_cast<LONG>(y * (65535.0f / screen_height)); + + input.type = INPUT_MOUSE; + input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; + input.mi.dx = pixel_x; + input.mi.dy = pixel_y; + + if (!::SendInput(1, &input, sizeof(INPUT))) + return false; + return true; +} + +bool SendMouseClick(MouseButton type) { + + DWORD down_flags = MOUSEEVENTF_ABSOLUTE; + DWORD up_flags = MOUSEEVENTF_ABSOLUTE; + + switch(type) { + case LEFT: + down_flags |= MOUSEEVENTF_LEFTDOWN; + up_flags |= MOUSEEVENTF_LEFTUP; + + break; + case MIDDLE: + down_flags |= MOUSEEVENTF_MIDDLEDOWN; + up_flags |= MOUSEEVENTF_MIDDLEUP; + + break; + case RIGHT: + down_flags |= MOUSEEVENTF_RIGHTDOWN; + up_flags |= MOUSEEVENTF_RIGHTUP; + + break; + default: + NOTREACHED(); + return false; + } + + INPUT input = { 0 }; + input.type = INPUT_MOUSE; + input.mi.dwFlags = down_flags; + if (!::SendInput(1, &input, sizeof(INPUT))) + return false; + + input.mi.dwFlags = up_flags; + if (!::SendInput(1, &input, sizeof(INPUT))) + return false; + + return true; +} + +} // ui_controls diff --git a/chrome/browser/automation/ui_controls.h b/chrome/browser/automation/ui_controls.h new file mode 100644 index 0000000..4a655f5 --- /dev/null +++ b/chrome/browser/automation/ui_controls.h @@ -0,0 +1,62 @@ +// 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. + +#ifndef CHROME_BROWSER_AUTOMATION_UI_CONTROLS_H__ +#define CHROME_BROWSER_AUTOMATION_UI_CONTROLS_H__ + +#include <string> +#include <wtypes.h> + +namespace ui_controls { + +// Send a key press with/without modifier keys. +bool SendKeyPress(wchar_t key, bool control, bool shift, bool alt); + +// Send a key down event. Use VK_CONTROL for ctrl key, +// VK_MENU for alt key and VK_SHIFT for shift key. +// Refer MSDN for more virtual key codes. +bool SendKeyDown(wchar_t key); +bool SendKeyUp(wchar_t key); + +// Simulate a mouse move. (x,y) are absolute +// screen coordinates. +bool SendMouseMove(long x, long y); + +enum MouseButton { + LEFT = 0, + MIDDLE, + RIGHT, +}; + +// Simulate a single mouse click with given button type. +bool SendMouseClick(MouseButton type); + +} // ui_controls + +#endif // CHROME_BROWSER_AUTOMATION_UI_CONTROLS_H__
\ No newline at end of file diff --git a/chrome/browser/automation/url_request_failed_dns_job.cc b/chrome/browser/automation/url_request_failed_dns_job.cc new file mode 100644 index 0000000..1272b6d --- /dev/null +++ b/chrome/browser/automation/url_request_failed_dns_job.cc @@ -0,0 +1,61 @@ +// 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/url_request_failed_dns_job.h" + +#include "base/message_loop.h" +#include "net/base/net_errors.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_filter.h" + +const wchar_t URLRequestFailedDnsJob::kTestUrl[] = + L"http://url.handled.by.fake.dns/"; + +void URLRequestFailedDnsJob::Start() { + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + this, &URLRequestFailedDnsJob::StartAsync)); +} + +/* static */ +void URLRequestFailedDnsJob::AddUITestUrls() { + URLRequestFilter* filter = URLRequestFilter::GetInstance(); + filter->AddUrlHandler(GURL(kTestUrl), + &URLRequestFailedDnsJob::Factory); +} + +/*static */ +URLRequestJob* URLRequestFailedDnsJob::Factory(URLRequest* request, + const std::string& scheme) { + return new URLRequestFailedDnsJob(request); +} + +void URLRequestFailedDnsJob::StartAsync() { + NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, + net::ERR_NAME_NOT_RESOLVED)); +} diff --git a/chrome/browser/automation/url_request_failed_dns_job.h b/chrome/browser/automation/url_request_failed_dns_job.h new file mode 100644 index 0000000..1020ae3 --- /dev/null +++ b/chrome/browser/automation/url_request_failed_dns_job.h @@ -0,0 +1,58 @@ +// 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. +// This class simulates what wininet does when a dns lookup fails. + +#ifndef CHROME_BROWSER_AUTOMATION_URL_REQUEST_FAILED_DNS_JOB_H__ +#define CHROME_BROWSER_AUTOMATION_URL_REQUEST_FAILED_DNS_JOB_H__ + +#include "googleurl/src/gurl.h" +#include "net/url_request/url_request_job.h" + +class URLRequestFailedDnsJob : public URLRequestJob { + public: + URLRequestFailedDnsJob(URLRequest* request) + : URLRequestJob(request) { } + + virtual void Start(); + + static URLRequestJob* Factory(URLRequest* request, + const std::string& scheme); + + // A test URL that can be used in UI tests. + static const wchar_t kTestUrl[]; + + // For UI tests: adds the testing URLs to the URLRequestFilter. + static void AddUITestUrls(); + + private: + // Simulate a DNS failure. + void StartAsync(); +}; + +#endif // CHROME_BROWSER_AUTOMATION_URL_REQUEST_FAILED_DNS_JOB_H__ diff --git a/chrome/browser/automation/url_request_mock_http_job.cc b/chrome/browser/automation/url_request_mock_http_job.cc new file mode 100644 index 0000000..5df0ed7 --- /dev/null +++ b/chrome/browser/automation/url_request_mock_http_job.cc @@ -0,0 +1,111 @@ +// 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 <windows.h> + +#include "chrome/browser/automation/url_request_mock_http_job.h" + +#include "base/file_util.h" +#include "base/message_loop.h" +#include "base/string_util.h" +#include "net/base/net_util.h" +#include "net/url_request/url_request_filter.h" + +static const char kMockHostname[] = "mock.http"; +static const wchar_t kMockHeaderFileSuffix[] = L".mock-http-headers"; + +std::wstring URLRequestMockHTTPJob::base_path_ = L""; + +/* static */ +URLRequestJob* URLRequestMockHTTPJob::Factory(URLRequest* request, + const std::string& scheme) { + std::wstring file_url(L"file:///"); + file_url += base_path_; + const std::string& url = request->url().spec(); + // Fix up the url to be the file url we're loading from disk. + std::string host_prefix("http://"); + host_prefix.append(kMockHostname); + size_t host_prefix_len = host_prefix.length(); + if (url.compare(0, host_prefix_len, host_prefix.data(), + host_prefix_len) == 0) { + file_url += UTF8ToWide(url.substr(host_prefix_len)); + } + + // Convert the file:/// URL to a path on disk. + std::wstring file_path; + net_util::FileURLToFilePath(GURL(file_url), &file_path); + URLRequestMockHTTPJob* job = new URLRequestMockHTTPJob(request); + job->file_path_ = file_path; + return job; +} + +/* static */ +void URLRequestMockHTTPJob::AddUITestUrls(const std::wstring& base_path) { + base_path_ = base_path; + + // Add kMockHostname to URLRequestFilter. + URLRequestFilter* filter = URLRequestFilter::GetInstance(); + filter->AddHostnameHandler("http", kMockHostname, + URLRequestMockHTTPJob::Factory); +} + +/* static */ +GURL URLRequestMockHTTPJob::GetMockUrl(const std::wstring& path) { + std::wstring url = L"http://"; + url.append(UTF8ToWide(kMockHostname)); + url.append(L"/"); + url.append(path); + return GURL(url); +} + +URLRequestMockHTTPJob::URLRequestMockHTTPJob(URLRequest* request) + : URLRequestFileJob(request) { } + +void URLRequestMockHTTPJob::GetResponseInfo(net::HttpResponseInfo* info) { + std::wstring header_file = file_path_ + kMockHeaderFileSuffix; + std::string raw_headers; + if (!file_util::ReadFileToString(header_file, &raw_headers)) + return; + + // ParseRawHeaders expects \0 to end each header line. + ReplaceSubstringsAfterOffset(&raw_headers, 0, "\n", std::string("\0", 1)); + info->headers = new net::HttpResponseHeaders(raw_headers); +} + +bool URLRequestMockHTTPJob::GetMimeType(std::string* mime_type) { + net::HttpResponseInfo info; + GetResponseInfo(&info); + return info.headers && info.headers->GetMimeType(mime_type); +} + +bool URLRequestMockHTTPJob::GetCharset(std::string* charset) { + net::HttpResponseInfo info; + GetResponseInfo(&info); + return info.headers && info.headers->GetCharset(charset); +} diff --git a/chrome/browser/automation/url_request_mock_http_job.h b/chrome/browser/automation/url_request_mock_http_job.h new file mode 100644 index 0000000..0c64587 --- /dev/null +++ b/chrome/browser/automation/url_request_mock_http_job.h @@ -0,0 +1,60 @@ +// 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. +// +// A URLRequestJob class that pulls the content and http headers from disk. + +#ifndef CHROME_BROWSER_AUTOMATION_URL_REQUEST_MOCK_HTTP_JOB_H__ +#define CHROME_BROWSER_AUTOMATION_URL_REQUEST_MOCK_HTTP_JOB_H__ + +#include "net/url_request/url_request_file_job.h" + +class URLRequestMockHTTPJob : public URLRequestFileJob { + public: + URLRequestMockHTTPJob(URLRequest* request); + virtual ~URLRequestMockHTTPJob() { } + + virtual bool GetMimeType(std::string* mime_type); + virtual bool GetCharset(std::string* charset); + virtual void GetResponseInfo(net::HttpResponseInfo* info); + + static URLRequest::ProtocolFactory Factory; + + // For UI tests: adds the testing URLs to the URLRequestFilter. + static void AddUITestUrls(const std::wstring& base_path); + + // Given the path to a file relative to base_path_, construct a mock URL. + static GURL GetMockUrl(const std::wstring& path); + + private: + // This is the file path leading to the root of the directory to use as the + // root of the http server. + static std::wstring base_path_; +}; + +# endif // CHROME_BROWSER_AUTOMATION_URL_REQUEST_MOCK_HTTP_JOB_H__ diff --git a/chrome/browser/automation/url_request_mock_net_error_job.cc b/chrome/browser/automation/url_request_mock_net_error_job.cc new file mode 100644 index 0000000..06adfb7 --- /dev/null +++ b/chrome/browser/automation/url_request_mock_net_error_job.cc @@ -0,0 +1,125 @@ +// 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/url_request_mock_net_error_job.h" + +#include "base/file_util.h" +#include "base/message_loop.h" +#include "base/string_util.h" +#include "net/base/net_util.h" +#include "net/url_request/url_request_filter.h" + +// static +URLRequestMockNetErrorJob::URLMockInfoMap + URLRequestMockNetErrorJob::url_mock_info_map_; + +// static +void URLRequestMockNetErrorJob::AddMockedURL(const GURL& url, + const std::wstring& base, + const std::vector<int>& errors, + X509Certificate* ssl_cert) { +#ifndef NDEBUG + URLMockInfoMap::const_iterator iter = url_mock_info_map_.find(url); + DCHECK(iter == url_mock_info_map_.end()); +#endif + + url_mock_info_map_[url] = MockInfo(base, errors, ssl_cert); + URLRequestFilter::GetInstance() + ->AddUrlHandler(url, &URLRequestMockNetErrorJob::Factory); +} + +// static +void URLRequestMockNetErrorJob::RemoveMockedURL(const GURL& url) { + URLMockInfoMap::iterator iter = url_mock_info_map_.find(url); + DCHECK(iter != url_mock_info_map_.end()); + url_mock_info_map_.erase(iter); + URLRequestFilter::GetInstance()->RemoveUrlHandler(url); +} + +// static +URLRequestJob* URLRequestMockNetErrorJob::Factory(URLRequest* request, + const std::string& scheme) { + GURL url = request->url(); + + URLMockInfoMap::const_iterator iter = url_mock_info_map_.find(url); + DCHECK(iter != url_mock_info_map_.end()); + + MockInfo mock_info = iter->second; + URLRequestMockNetErrorJob* job = + new URLRequestMockNetErrorJob(request, mock_info.errors, + mock_info.ssl_cert); + + // URLRequestMockNetErrorJob derives from URLRequestFileJob. We set the + // file_path_ of the job so that the URLRequestFileJob methods will do the + // loading from the files. + std::wstring file_url(L"file:///"); + file_url.append(mock_info.base); + file_url.append(UTF8ToWide(url.path())); + // Convert the file:/// URL to a path on disk. + std::wstring file_path; + net_util::FileURLToFilePath(GURL(file_url), &file_path); + job->file_path_ = file_path; + return job; +} + +URLRequestMockNetErrorJob::URLRequestMockNetErrorJob(URLRequest* request, + const std::vector<int>& errors, X509Certificate* cert) + : URLRequestMockHTTPJob(request), + errors_(errors), + ssl_cert_(cert) { +} + +URLRequestMockNetErrorJob::~URLRequestMockNetErrorJob() { +} + +void URLRequestMockNetErrorJob::Start() { + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + this, &URLRequestMockNetErrorJob::StartAsync)); +} + +void URLRequestMockNetErrorJob::StartAsync() { + if (errors_.empty()) { + URLRequestMockHTTPJob::Start(); + } else { + int error = errors_[0]; + errors_.erase(errors_.begin()); + + if (net::IsCertificateError(error)) { + DCHECK(ssl_cert_); + request_->delegate()->OnSSLCertificateError(request_, error, + ssl_cert_.get()); + } else { + NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, error)); + } + } +} + +void URLRequestMockNetErrorJob::ContinueDespiteLastError() { + Start(); +} diff --git a/chrome/browser/automation/url_request_mock_net_error_job.h b/chrome/browser/automation/url_request_mock_net_error_job.h new file mode 100644 index 0000000..bee1c31 --- /dev/null +++ b/chrome/browser/automation/url_request_mock_net_error_job.h @@ -0,0 +1,95 @@ +// 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. + +// A URLRequestJob class that simulates network errors (including https +// related). +// It is based on URLRequestMockHttpJob. + +#ifndef CHROME_BROWSER_AUTOMATION_URL_REQUEST_MOCK_NET_ERROR_H__ +#define CHROME_BROWSER_AUTOMATION_URL_REQUEST_MOCK_NET_ERROR_H__ + +#include "chrome/browser/automation/url_request_mock_http_job.h" +#include "net/base/net_errors.h" + +class URLRequestMockNetErrorJob : public URLRequestMockHTTPJob { + public: + URLRequestMockNetErrorJob(URLRequest* request, + const std::vector<int>& errors, + X509Certificate* ssl_cert); + virtual ~URLRequestMockNetErrorJob(); + + virtual void Start(); + virtual void ContinueDespiteLastError(); + + // Add the specified URL to the list of URLs that should be mocked. When this + // URL is hit, the specified |errors| will be played. If any of these errors + // is a cert error, |ssl_cert| will be used as the ssl cert when notifying of + // the error. |ssl_cert| can be NULL if |errors| does not contain any cert + // errors. |base| is the location on disk where the file mocking the URL + // contents and http-headers should be retrieved from. + static void AddMockedURL(const GURL& url, + const std::wstring& base, + const std::vector<int>& errors, + X509Certificate* ssl_cert); + + // Removes the specified |url| from the list of mocked urls. + static void RemoveMockedURL(const GURL& url); + + private: + struct MockInfo { + MockInfo() : ssl_cert(NULL) { } + MockInfo(std::wstring base, + std::vector<int> errors, + X509Certificate* ssl_cert) + : base(base), + errors(errors), + ssl_cert(ssl_cert) { } + + std::wstring base; + std::vector<int> errors; + scoped_refptr<X509Certificate> ssl_cert; + }; + + static URLRequest::ProtocolFactory Factory; + + void StartAsync(); + + // The errors to simulate. + std::vector<int> errors_; + + // The certificate to use for SSL errors. + scoped_refptr<X509Certificate> ssl_cert_; + + typedef std::map<GURL, MockInfo> URLMockInfoMap; + static URLMockInfoMap url_mock_info_map_; + + DISALLOW_EVIL_CONSTRUCTORS(URLRequestMockNetErrorJob); +}; + +#endif // #define CHROME_BROWSER_AUTOMATION_URL_REQUEST_MOCK_NET_ERROR_H__ diff --git a/chrome/browser/automation/url_request_slow_download_job.cc b/chrome/browser/automation/url_request_slow_download_job.cc new file mode 100644 index 0000000..cac0cab --- /dev/null +++ b/chrome/browser/automation/url_request_slow_download_job.cc @@ -0,0 +1,186 @@ +// 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/url_request_slow_download_job.h" + +#include "base/message_loop.h" +#include "base/string_util.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_filter.h" + +const int kFirstDownloadSize = 1024 * 35; +const int kSecondDownloadSize = 1024 * 10; + +const wchar_t URLRequestSlowDownloadJob::kUnknownSizeUrl[] = + L"http://url.handled.by.slow.download/download-unknown-size"; +const wchar_t URLRequestSlowDownloadJob::kKnownSizeUrl[] = + L"http://url.handled.by.slow.download/download-known-size"; +const wchar_t URLRequestSlowDownloadJob::kFinishDownloadUrl[] = + L"http://url.handled.by.slow.download/download-finish"; + +std::vector<URLRequestSlowDownloadJob*> + URLRequestSlowDownloadJob::kPendingRequests; + +void URLRequestSlowDownloadJob::Start() { + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(this, + &URLRequestSlowDownloadJob::StartAsync)); +} + +/* static */ +void URLRequestSlowDownloadJob::AddUITestUrls() { + URLRequestFilter* filter = URLRequestFilter::GetInstance(); + filter->AddUrlHandler(GURL(kUnknownSizeUrl), + &URLRequestSlowDownloadJob::Factory); + filter->AddUrlHandler(GURL(kKnownSizeUrl), + &URLRequestSlowDownloadJob::Factory); + filter->AddUrlHandler(GURL(kFinishDownloadUrl), + &URLRequestSlowDownloadJob::Factory); +} + +/*static */ +URLRequestJob* URLRequestSlowDownloadJob::Factory(URLRequest* request, + const std::string& scheme) { + URLRequestSlowDownloadJob* job = new URLRequestSlowDownloadJob(request); + if (request->url().spec() != WideToUTF8(kFinishDownloadUrl)) + URLRequestSlowDownloadJob::kPendingRequests.push_back(job); + return job; +} + +/* static */ +void URLRequestSlowDownloadJob::FinishPendingRequests() { + typedef std::vector<URLRequestSlowDownloadJob*> JobList; + for (JobList::iterator it = kPendingRequests.begin(); it != + kPendingRequests.end(); ++it) { + (*it)->set_should_finish_download(); + } + kPendingRequests.clear(); +} + +URLRequestSlowDownloadJob::URLRequestSlowDownloadJob(URLRequest* request) + : URLRequestJob(request), + first_download_size_remaining_(kFirstDownloadSize), + should_finish_download_(false), + should_send_second_chunk_(false) { +} + +void URLRequestSlowDownloadJob::StartAsync() { + if (LowerCaseEqualsASCII(kFinishDownloadUrl, request_->url().spec().c_str())) + URLRequestSlowDownloadJob::FinishPendingRequests(); + + NotifyHeadersComplete(); +} + +bool URLRequestSlowDownloadJob::ReadRawData(char* buf, int buf_size, + int *bytes_read) { + if (LowerCaseEqualsASCII(kFinishDownloadUrl, + request_->url().spec().c_str())) { + *bytes_read = 0; + return true; + } + + if (should_send_second_chunk_) { + DCHECK(buf_size > kSecondDownloadSize); + for (int i = 0; i < kSecondDownloadSize; ++i) { + buf[i] = '*'; + } + *bytes_read = kSecondDownloadSize; + should_send_second_chunk_ = false; + return true; + } + + if (first_download_size_remaining_ > 0) { + int send_size = std::min(first_download_size_remaining_, buf_size); + for (int i = 0; i < send_size; ++i) { + buf[i] = '*'; + } + *bytes_read = send_size; + first_download_size_remaining_ -= send_size; + + SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); + DCHECK(!is_done()); + return true; + } + + if (should_finish_download_) { + *bytes_read = 0; + return true; + } + + // If we make it here, the first chunk has been sent and we need to wait + // until a request is made for kFinishDownloadUrl. + MessageLoop::current()->PostDelayedTask(FROM_HERE, NewRunnableMethod( + this, &URLRequestSlowDownloadJob::CheckDoneStatus), 100); + AddRef(); + + // Return false to signal there is pending data. + return false; +} + +void URLRequestSlowDownloadJob::CheckDoneStatus() { + if (should_finish_download_) { + should_send_second_chunk_ = true; + SetStatus(URLRequestStatus()); + NotifyReadComplete(kSecondDownloadSize); + Release(); + } else { + MessageLoop::current()->PostDelayedTask(FROM_HERE, NewRunnableMethod( + this, &URLRequestSlowDownloadJob::CheckDoneStatus), 100); + } +} + +void URLRequestSlowDownloadJob::GetResponseInfo(net::HttpResponseInfo* info) { + // Send back mock headers. + std::string raw_headers; + if (LowerCaseEqualsASCII(kFinishDownloadUrl, + request_->url().spec().c_str())) { + raw_headers.append( + "HTTP/1.1 200 OK\n" + "Content-type: text/plain\n"); + } else { + raw_headers.append( + "HTTP/1.1 200 OK\n" + "Content-type: application/octet-stream\n" + "Cache-Control: max-age=0\n"); + + if (LowerCaseEqualsASCII(kKnownSizeUrl, request_->url().spec().c_str())) { + raw_headers.append(StringPrintf("Content-Length: %d\n", + kFirstDownloadSize + kSecondDownloadSize)); + } + } + + // ParseRawHeaders expects \0 to end each header line. + ReplaceSubstringsAfterOffset(&raw_headers, 0, "\n", std::string("\0", 1)); + info->headers = new net::HttpResponseHeaders(raw_headers); +} + +bool URLRequestSlowDownloadJob::GetMimeType(std::string* mime_type) { + net::HttpResponseInfo info; + GetResponseInfo(&info); + return info.headers && info.headers->GetMimeType(mime_type); +} diff --git a/chrome/browser/automation/url_request_slow_download_job.h b/chrome/browser/automation/url_request_slow_download_job.h new file mode 100644 index 0000000..d05f850 --- /dev/null +++ b/chrome/browser/automation/url_request_slow_download_job.h @@ -0,0 +1,82 @@ +// 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. +// This class simulates a slow download. This used in a UI test to test the +// download manager. Requests to |kUnknownSizeUrl| and |kKnownSizeUrl| start +// downloads that pause after the first + +#ifndef CHROME_BROWSER_AUTOMATION_URL_REQUEST_SLOW_DOWNLOAD_JOB_H__ +#define CHROME_BROWSER_AUTOMATION_URL_REQUEST_SLOW_DOWNLOAD_JOB_H__ + +#include <vector> + +#include "googleurl/src/gurl.h" +#include "net/url_request/url_request_job.h" + +class URLRequestSlowDownloadJob : public URLRequestJob { + public: + URLRequestSlowDownloadJob(URLRequest* request); + virtual ~URLRequestSlowDownloadJob() { } + + // Timer callback, used to check to see if we should finish our download and + // send the second chunk. + void CheckDoneStatus(); + + // URLRequestJob methods + virtual void Start(); + virtual bool GetMimeType(std::string* mime_type); + virtual void GetResponseInfo(net::HttpResponseInfo* info); + virtual bool ReadRawData(char* buf, int buf_size, int *bytes_read); + + static URLRequestJob* Factory(URLRequest* request, + const std::string& scheme); + + // Test URLs. + static const wchar_t kUnknownSizeUrl[]; + static const wchar_t kKnownSizeUrl[]; + static const wchar_t kFinishDownloadUrl[]; + + // For UI tests: adds the testing URLs to the URLRequestFilter. + static void AddUITestUrls(); + + private: + // Mark all pending requests to be finished. We keep track of pending + // requests in |kPendingRequests|. + static void FinishPendingRequests(); + static std::vector<URLRequestSlowDownloadJob*> kPendingRequests; + + void StartAsync(); + + void set_should_finish_download() { should_finish_download_ = true; } + + int first_download_size_remaining_; + bool should_finish_download_; + bool should_send_second_chunk_; +}; + +#endif // CHROME_BROWSER_AUTOMATION_URL_REQUEST_SLOW_DOWNLOAD_JOB_H__ |