diff options
Diffstat (limited to 'chrome/browser/automation/automation_provider_observers.cc')
-rw-r--r-- | chrome/browser/automation/automation_provider_observers.cc | 1134 |
1 files changed, 1134 insertions, 0 deletions
diff --git a/chrome/browser/automation/automation_provider_observers.cc b/chrome/browser/automation/automation_provider_observers.cc new file mode 100644 index 0000000..f731309 --- /dev/null +++ b/chrome/browser/automation/automation_provider_observers.cc @@ -0,0 +1,1134 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/automation/automation_provider_observers.h" + +#include "base/basictypes.h" +#include "base/json/json_writer.h" +#include "base/string_util.h" +#include "chrome/app/chrome_dll_resource.h" +#include "chrome/browser/automation/automation_provider.h" +#include "chrome/browser/automation/automation_provider_json.h" +#include "chrome/browser/bookmarks/bookmark_model.h" +#include "chrome/browser/dom_operation_notification_details.h" +#include "chrome/browser/download/download_item.h" +#include "chrome/browser/download/save_package.h" +#include "chrome/browser/extensions/extension_host.h" +#include "chrome/browser/extensions/extension_process_manager.h" +#include "chrome/browser/extensions/extension_updater.h" +#include "chrome/browser/login_prompt.h" +#include "chrome/browser/metrics/metric_event_duration_details.h" +#include "chrome/browser/printing/print_job.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/tab_contents/navigation_controller.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/notification_service.h" +#include "chrome/test/automation/automation_constants.h" + +#if defined(OS_CHROMEOS) +#include "chrome/browser/chromeos/login/authentication_notification_details.h" +#endif + +// Holds onto start and stop timestamps for a particular tab +class InitialLoadObserver::TabTime { + public: + explicit TabTime(base::TimeTicks started) + : load_start_time_(started) { + } + void set_stop_time(base::TimeTicks stopped) { + load_stop_time_ = stopped; + } + base::TimeTicks stop_time() const { + return load_stop_time_; + } + base::TimeTicks start_time() const { + return load_start_time_; + } + private: + base::TimeTicks load_start_time_; + base::TimeTicks load_stop_time_; +}; + +InitialLoadObserver::InitialLoadObserver(size_t tab_count, + AutomationProvider* automation) + : automation_(automation), + outstanding_tab_count_(tab_count), + init_time_(base::TimeTicks::Now()) { + if (outstanding_tab_count_ > 0) { + registrar_.Add(this, NotificationType::LOAD_START, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::LOAD_STOP, + NotificationService::AllSources()); + } +} + +InitialLoadObserver::~InitialLoadObserver() { +} + +void InitialLoadObserver::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type == NotificationType::LOAD_START) { + if (outstanding_tab_count_ > loading_tabs_.size()) + loading_tabs_.insert(TabTimeMap::value_type( + source.map_key(), + TabTime(base::TimeTicks::Now()))); + } else if (type == NotificationType::LOAD_STOP) { + if (outstanding_tab_count_ > finished_tabs_.size()) { + TabTimeMap::iterator iter = loading_tabs_.find(source.map_key()); + if (iter != loading_tabs_.end()) { + finished_tabs_.insert(source.map_key()); + iter->second.set_stop_time(base::TimeTicks::Now()); + } + if (outstanding_tab_count_ == finished_tabs_.size()) + ConditionMet(); + } + } else { + NOTREACHED(); + } +} + +DictionaryValue* InitialLoadObserver::GetTimingInformation() const { + ListValue* items = new ListValue; + for (TabTimeMap::const_iterator it = loading_tabs_.begin(); + it != loading_tabs_.end(); + ++it) { + DictionaryValue* item = new DictionaryValue; + base::TimeDelta delta_start = it->second.start_time() - init_time_; + + item->SetReal(L"load_start_ms", delta_start.InMillisecondsF()); + if (it->second.stop_time().is_null()) { + item->Set(L"load_stop_ms", Value::CreateNullValue()); + } else { + base::TimeDelta delta_stop = it->second.stop_time() - init_time_; + item->SetReal(L"load_stop_ms", delta_stop.InMillisecondsF()); + } + items->Append(item); + } + DictionaryValue* return_value = new DictionaryValue; + return_value->Set(L"tabs", items); + return return_value; +} + +void InitialLoadObserver::ConditionMet() { + registrar_.RemoveAll(); + automation_->Send(new AutomationMsg_InitialLoadsComplete(0)); +} + +NewTabUILoadObserver::NewTabUILoadObserver(AutomationProvider* automation) + : automation_(automation) { + registrar_.Add(this, NotificationType::INITIAL_NEW_TAB_UI_LOAD, + NotificationService::AllSources()); +} + +NewTabUILoadObserver::~NewTabUILoadObserver() { +} + +void NewTabUILoadObserver::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type == NotificationType::INITIAL_NEW_TAB_UI_LOAD) { + Details<int> load_time(details); + automation_->Send( + new AutomationMsg_InitialNewTabUILoadComplete(0, *load_time.ptr())); + } else { + NOTREACHED(); + } +} + +NavigationControllerRestoredObserver::NavigationControllerRestoredObserver( + AutomationProvider* automation, + NavigationController* controller, + IPC::Message* reply_message) + : automation_(automation), + controller_(controller), + reply_message_(reply_message) { + if (FinishedRestoring()) { + SendDone(); + } else { + registrar_.Add(this, NotificationType::LOAD_STOP, + NotificationService::AllSources()); + } +} + +NavigationControllerRestoredObserver::~NavigationControllerRestoredObserver() { +} + +void NavigationControllerRestoredObserver::Observe( + NotificationType type, const NotificationSource& source, + const NotificationDetails& details) { + if (FinishedRestoring()) { + SendDone(); + registrar_.RemoveAll(); + } +} + +bool NavigationControllerRestoredObserver::FinishedRestoring() { + return (!controller_->needs_reload() && !controller_->pending_entry() && + !controller_->tab_contents()->is_loading()); +} + +void NavigationControllerRestoredObserver::SendDone() { + DCHECK(reply_message_ != NULL); + automation_->Send(reply_message_); +} + +NavigationNotificationObserver::NavigationNotificationObserver( + NavigationController* controller, + AutomationProvider* automation, + IPC::Message* reply_message, + int number_of_navigations, + bool include_current_navigation) + : automation_(automation), + reply_message_(reply_message), + controller_(controller), + navigations_remaining_(number_of_navigations), + navigation_started_(false) { + DCHECK_LT(0, navigations_remaining_); + Source<NavigationController> source(controller_); + registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED, source); + registrar_.Add(this, NotificationType::LOAD_START, source); + registrar_.Add(this, NotificationType::LOAD_STOP, source); + registrar_.Add(this, NotificationType::AUTH_NEEDED, source); + registrar_.Add(this, NotificationType::AUTH_SUPPLIED, source); + registrar_.Add(this, NotificationType::AUTH_CANCELLED, source); + + if (include_current_navigation && controller->tab_contents()->is_loading()) + navigation_started_ = true; +} + +NavigationNotificationObserver::~NavigationNotificationObserver() { + if (reply_message_) { + // This means we did not receive a notification for this navigation. + // Send over a failed navigation status back to the caller to ensure that + // the caller does not hang waiting for the response. + IPC::ParamTraits<AutomationMsg_NavigationResponseValues>::Write( + reply_message_, AUTOMATION_MSG_NAVIGATION_ERROR); + automation_->Send(reply_message_); + reply_message_ = NULL; + } + + automation_->RemoveNavigationStatusListener(this); +} + +void NavigationNotificationObserver::Observe( + NotificationType type, const NotificationSource& source, + const NotificationDetails& details) { + // We listen for 2 events to determine when the navigation started because: + // - when this is used by the WaitForNavigation method, we might be invoked + // afer the load has started (but not after the entry was committed, as + // WaitForNavigation compares times of the last navigation). + // - when this is used with a page requiring authentication, we will not get + // a NotificationType::NAV_ENTRY_COMMITTED until after we authenticate, so + // we need the NotificationType::LOAD_START. + if (type == NotificationType::NAV_ENTRY_COMMITTED || + type == NotificationType::LOAD_START) { + navigation_started_ = true; + } else if (type == NotificationType::LOAD_STOP) { + if (navigation_started_) { + navigation_started_ = false; + if (--navigations_remaining_ == 0) + ConditionMet(AUTOMATION_MSG_NAVIGATION_SUCCESS); + } + } else if (type == NotificationType::AUTH_SUPPLIED || + type == NotificationType::AUTH_CANCELLED) { + // 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 == NotificationType::AUTH_NEEDED) { + // Remember the login handler that wants authentication. + // We do this in all cases (not just when navigation_started_ == true) so + // tests can still wait for auth dialogs outside of navigation. + LoginHandler* handler = + Details<LoginNotificationDetails>(details)->handler(); + automation_->AddLoginHandler(controller_, handler); + + // Respond that authentication is needed. + navigation_started_ = false; + ConditionMet(AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED); + } else { + NOTREACHED(); + } +} + +void NavigationNotificationObserver::ConditionMet( + AutomationMsg_NavigationResponseValues navigation_result) { + DCHECK(reply_message_ != NULL); + + IPC::ParamTraits<AutomationMsg_NavigationResponseValues>::Write( + reply_message_, navigation_result); + automation_->Send(reply_message_); + reply_message_ = NULL; + + delete this; +} + +TabStripNotificationObserver::TabStripNotificationObserver( + NotificationType notification, AutomationProvider* automation) + : automation_(automation), + notification_(notification) { + registrar_.Add(this, notification_, NotificationService::AllSources()); +} + +TabStripNotificationObserver::~TabStripNotificationObserver() { +} + +void TabStripNotificationObserver::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(); + } +} + +TabAppendedNotificationObserver::TabAppendedNotificationObserver( + Browser* parent, AutomationProvider* automation, + IPC::Message* reply_message) + : TabStripNotificationObserver(NotificationType::TAB_PARENTED, automation), + parent_(parent), + reply_message_(reply_message) { +} + +void TabAppendedNotificationObserver::ObserveTab( + NavigationController* controller) { + if (automation_->GetIndexForNavigationController(controller, parent_) == + TabStripModel::kNoTab) { + // This tab notification doesn't belong to the parent_. + return; + } + + automation_->AddNavigationStatusListener(controller, reply_message_, 1, + false); +} + +TabClosedNotificationObserver::TabClosedNotificationObserver( + AutomationProvider* automation, bool wait_until_closed, + IPC::Message* reply_message) + : TabStripNotificationObserver(wait_until_closed ? + NotificationType::TAB_CLOSED : NotificationType::TAB_CLOSING, + automation), + reply_message_(reply_message), + for_browser_command_(false) { +} + +void TabClosedNotificationObserver::ObserveTab( + NavigationController* controller) { + if (for_browser_command_) { + AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_, + true); + } else { + AutomationMsg_CloseTab::WriteReplyParams(reply_message_, true); + } + automation_->Send(reply_message_); +} + +void TabClosedNotificationObserver::set_for_browser_command( + bool for_browser_command) { + for_browser_command_ = for_browser_command; +} + +bool DidExtensionHostsStopLoading(ExtensionProcessManager* manager) { + for (ExtensionProcessManager::const_iterator iter = manager->begin(); + iter != manager->end(); ++iter) { + if (!(*iter)->did_stop_loading()) + return false; + } + return true; +} + +ExtensionInstallNotificationObserver::ExtensionInstallNotificationObserver( + AutomationProvider* automation, int id, IPC::Message* reply_message) + : automation_(automation), + id_(id), + reply_message_(reply_message) { + registrar_.Add(this, NotificationType::EXTENSION_LOADED, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::EXTENSION_INSTALL_ERROR, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::EXTENSION_UPDATE_DISABLED, + NotificationService::AllSources()); +} + +ExtensionInstallNotificationObserver::~ExtensionInstallNotificationObserver() { +} + +void ExtensionInstallNotificationObserver::Observe( + NotificationType type, const NotificationSource& source, + const NotificationDetails& details) { + switch (type.value) { + case NotificationType::EXTENSION_LOADED: + SendResponse(AUTOMATION_MSG_EXTENSION_INSTALL_SUCCEEDED); + break; + case NotificationType::EXTENSION_INSTALL_ERROR: + case NotificationType::EXTENSION_UPDATE_DISABLED: + SendResponse(AUTOMATION_MSG_EXTENSION_INSTALL_FAILED); + break; + default: + NOTREACHED(); + break; + } + + delete this; +} + +void ExtensionInstallNotificationObserver::SendResponse( + AutomationMsg_ExtensionResponseValues response) { + if (reply_message_ != NULL) { + switch (id_) { + case AutomationMsg_InstallExtension::ID: + AutomationMsg_InstallExtension::WriteReplyParams(reply_message_, + response); + break; + case AutomationMsg_LoadExpandedExtension::ID: + AutomationMsg_LoadExpandedExtension::WriteReplyParams(reply_message_, + response); + break; + default: + NOTREACHED(); + break; + } + + automation_->Send(reply_message_); + reply_message_ = NULL; + } +} + +ExtensionReadyNotificationObserver::ExtensionReadyNotificationObserver( + ExtensionProcessManager* manager, AutomationProvider* automation, int id, + IPC::Message* reply_message) + : manager_(manager), + automation_(automation), + id_(id), + reply_message_(reply_message), + extension_(NULL) { + registrar_.Add(this, NotificationType::EXTENSION_HOST_DID_STOP_LOADING, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::EXTENSION_LOADED, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::EXTENSION_INSTALL_ERROR, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::EXTENSION_UPDATE_DISABLED, + NotificationService::AllSources()); +} + +ExtensionReadyNotificationObserver::~ExtensionReadyNotificationObserver() { +} + +void ExtensionReadyNotificationObserver::Observe( + NotificationType type, const NotificationSource& source, + const NotificationDetails& details) { + bool success = false; + switch (type.value) { + case NotificationType::EXTENSION_HOST_DID_STOP_LOADING: + // Only continue on with this method if our extension has been loaded + // and all the extension hosts have stopped loading. + if (!extension_ || !DidExtensionHostsStopLoading(manager_)) + return; + success = true; + break; + case NotificationType::EXTENSION_LOADED: + extension_ = Details<Extension>(details).ptr(); + if (!DidExtensionHostsStopLoading(manager_)) + return; + success = true; + break; + case NotificationType::EXTENSION_INSTALL_ERROR: + case NotificationType::EXTENSION_UPDATE_DISABLED: + success = false; + break; + default: + NOTREACHED(); + break; + } + + if (id_ == AutomationMsg_InstallExtensionAndGetHandle::ID) { + // A handle of zero indicates an error. + int extension_handle = 0; + if (extension_) + extension_handle = automation_->AddExtension(extension_); + AutomationMsg_InstallExtensionAndGetHandle::WriteReplyParams( + reply_message_, extension_handle); + } else if (id_ == AutomationMsg_EnableExtension::ID) { + AutomationMsg_EnableExtension::WriteReplyParams(reply_message_, true); + } else { + NOTREACHED(); + LOG(ERROR) << "Cannot write reply params for unknown message id."; + } + + automation_->Send(reply_message_); + delete this; +} + +ExtensionUnloadNotificationObserver::ExtensionUnloadNotificationObserver() + : did_receive_unload_notification_(false) { + registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::EXTENSION_UNLOADED_DISABLED, + NotificationService::AllSources()); +} + +ExtensionUnloadNotificationObserver::~ExtensionUnloadNotificationObserver() { +} + +void ExtensionUnloadNotificationObserver::Observe( + NotificationType type, const NotificationSource& source, + const NotificationDetails& details) { + if (type.value == NotificationType::EXTENSION_UNLOADED || + type.value == NotificationType::EXTENSION_UNLOADED_DISABLED) { + did_receive_unload_notification_ = true; + } else { + NOTREACHED(); + } +} + +ExtensionTestResultNotificationObserver:: + ExtensionTestResultNotificationObserver(AutomationProvider* automation) + : automation_(automation) { + registrar_.Add(this, NotificationType::EXTENSION_TEST_PASSED, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::EXTENSION_TEST_FAILED, + NotificationService::AllSources()); +} + +ExtensionTestResultNotificationObserver:: + ~ExtensionTestResultNotificationObserver() { +} + +void ExtensionTestResultNotificationObserver::Observe( + NotificationType type, const NotificationSource& source, + const NotificationDetails& details) { + switch (type.value) { + case NotificationType::EXTENSION_TEST_PASSED: + results_.push_back(true); + messages_.push_back(""); + break; + + case NotificationType::EXTENSION_TEST_FAILED: + results_.push_back(false); + messages_.push_back(*(Details<std::string>(details).ptr())); + break; + + default: + NOTREACHED(); + } + // There may be a reply message waiting for this event, so check. + MaybeSendResult(); +} + +void ExtensionTestResultNotificationObserver::MaybeSendResult() { + if (results_.size() > 0) { + // This release method should return the automation's current + // reply message, or NULL if there is no current one. If it is not + // NULL, we are stating that we will handle this reply message. + IPC::Message* reply_message = automation_->reply_message_release(); + // Send the result back if we have a reply message. + if (reply_message) { + AutomationMsg_WaitForExtensionTestResult::WriteReplyParams( + reply_message, results_.front(), messages_.front()); + results_.pop_front(); + messages_.pop_front(); + automation_->Send(reply_message); + } + } +} + +BrowserOpenedNotificationObserver::BrowserOpenedNotificationObserver( + AutomationProvider* automation, IPC::Message* reply_message) + : automation_(automation), + reply_message_(reply_message), + for_browser_command_(false) { + registrar_.Add(this, NotificationType::BROWSER_OPENED, + NotificationService::AllSources()); +} + +BrowserOpenedNotificationObserver::~BrowserOpenedNotificationObserver() { +} + +void BrowserOpenedNotificationObserver::Observe( + NotificationType type, const NotificationSource& source, + const NotificationDetails& details) { + if (type == NotificationType::BROWSER_OPENED) { + if (for_browser_command_) { + AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_, + true); + } + automation_->Send(reply_message_); + delete this; + } else { + NOTREACHED(); + } +} + +void BrowserOpenedNotificationObserver::set_for_browser_command( + bool for_browser_command) { + for_browser_command_ = for_browser_command; +} + +BrowserClosedNotificationObserver::BrowserClosedNotificationObserver( + Browser* browser, + AutomationProvider* automation, + IPC::Message* reply_message) + : automation_(automation), + reply_message_(reply_message), + for_browser_command_(false) { + registrar_.Add(this, NotificationType::BROWSER_CLOSED, + Source<Browser>(browser)); +} + +void BrowserClosedNotificationObserver::Observe( + NotificationType type, const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == NotificationType::BROWSER_CLOSED); + Details<bool> close_app(details); + DCHECK(reply_message_ != NULL); + if (for_browser_command_) { + AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_, + true); + } else { + AutomationMsg_CloseBrowser::WriteReplyParams(reply_message_, true, + *(close_app.ptr())); + } + automation_->Send(reply_message_); + reply_message_ = NULL; + delete this; +} + +void BrowserClosedNotificationObserver::set_for_browser_command( + bool for_browser_command) { + for_browser_command_ = for_browser_command; +} + +BrowserCountChangeNotificationObserver::BrowserCountChangeNotificationObserver( + int target_count, + AutomationProvider* automation, + IPC::Message* reply_message) + : target_count_(target_count), + automation_(automation), + reply_message_(reply_message) { + registrar_.Add(this, NotificationType::BROWSER_OPENED, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::BROWSER_CLOSED, + NotificationService::AllSources()); +} + +void BrowserCountChangeNotificationObserver::Observe( + NotificationType type, const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == NotificationType::BROWSER_OPENED || + type == NotificationType::BROWSER_CLOSED); + int current_count = static_cast<int>(BrowserList::size()); + if (type == NotificationType::BROWSER_CLOSED) { + // At the time of the notification the browser being closed is not removed + // from the list. The real count is one less than the reported count. + DCHECK_LT(0, current_count); + current_count--; + } + if (current_count == target_count_) { + AutomationMsg_WaitForBrowserWindowCountToBecome::WriteReplyParams( + reply_message_, true); + automation_->Send(reply_message_); + reply_message_ = NULL; + delete this; + } +} + +AppModalDialogShownObserver::AppModalDialogShownObserver( + AutomationProvider* automation, IPC::Message* reply_message) + : automation_(automation), + reply_message_(reply_message) { + registrar_.Add(this, NotificationType::APP_MODAL_DIALOG_SHOWN, + NotificationService::AllSources()); +} + +AppModalDialogShownObserver::~AppModalDialogShownObserver() { +} + +void AppModalDialogShownObserver::Observe( + NotificationType type, const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == NotificationType::APP_MODAL_DIALOG_SHOWN); + + AutomationMsg_WaitForAppModalDialogToBeShown::WriteReplyParams( + reply_message_, true); + automation_->Send(reply_message_); + reply_message_ = NULL; + delete this; +} + +namespace { + +// Define mapping from command to notification +struct CommandNotification { + int command; + NotificationType::Type notification_type; +}; + +const struct CommandNotification command_notifications[] = { + {IDC_DUPLICATE_TAB, NotificationType::TAB_PARENTED}, + {IDC_NEW_TAB, NotificationType::INITIAL_NEW_TAB_UI_LOAD}, + + // Returns as soon as the restored tab is created. To further wait until + // the content page is loaded, use WaitForTabToBeRestored. + {IDC_RESTORE_TAB, NotificationType::TAB_PARENTED}, + + // For the following commands, we need to wait for a new tab to be created, + // load to finish, and title to change. + {IDC_MANAGE_EXTENSIONS, NotificationType::TAB_CONTENTS_TITLE_UPDATED}, + {IDC_OPTIONS, NotificationType::TAB_CONTENTS_TITLE_UPDATED}, + {IDC_SHOW_DOWNLOADS, NotificationType::TAB_CONTENTS_TITLE_UPDATED}, + {IDC_SHOW_HISTORY, NotificationType::TAB_CONTENTS_TITLE_UPDATED}, +}; + +} // namespace + +ExecuteBrowserCommandObserver::~ExecuteBrowserCommandObserver() { +} + +// static +bool ExecuteBrowserCommandObserver::CreateAndRegisterObserver( + AutomationProvider* automation, Browser* browser, int command, + IPC::Message* reply_message) { + bool result = true; + switch (command) { + case IDC_NEW_WINDOW: + case IDC_NEW_INCOGNITO_WINDOW: { + BrowserOpenedNotificationObserver* observer = + new BrowserOpenedNotificationObserver(automation, reply_message); + observer->set_for_browser_command(true); + break; + } + case IDC_CLOSE_WINDOW: { + BrowserClosedNotificationObserver* observer = + new BrowserClosedNotificationObserver(browser, automation, + reply_message); + observer->set_for_browser_command(true); + break; + } + case IDC_CLOSE_TAB: { + TabClosedNotificationObserver* observer = + new TabClosedNotificationObserver(automation, true, reply_message); + observer->set_for_browser_command(true); + break; + } + case IDC_BACK: + case IDC_FORWARD: + case IDC_RELOAD: { + automation->AddNavigationStatusListener( + &browser->GetSelectedTabContents()->controller(), + reply_message, 1, false); + break; + } + default: { + ExecuteBrowserCommandObserver* observer = + new ExecuteBrowserCommandObserver(automation, reply_message); + if (!observer->Register(command)) { + delete observer; + result = false; + } + break; + } + } + return result; +} + +void ExecuteBrowserCommandObserver::Observe( + NotificationType type, const NotificationSource& source, + const NotificationDetails& details) { + if (type == notification_type_) { + AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_, + true); + automation_->Send(reply_message_); + delete this; + } else { + NOTREACHED(); + } +} + +ExecuteBrowserCommandObserver::ExecuteBrowserCommandObserver( + AutomationProvider* automation, IPC::Message* reply_message) + : automation_(automation), + reply_message_(reply_message) { +} + +bool ExecuteBrowserCommandObserver::Register(int command) { + if (!GetNotificationType(command, ¬ification_type_)) + return false; + registrar_.Add(this, notification_type_, NotificationService::AllSources()); + return true; +} + +bool ExecuteBrowserCommandObserver::GetNotificationType( + int command, NotificationType::Type* type) { + if (!type) + return false; + bool found = false; + for (unsigned int i = 0; i < arraysize(command_notifications); i++) { + if (command_notifications[i].command == command) { + *type = command_notifications[i].notification_type; + found = true; + break; + } + } + return found; +} + +FindInPageNotificationObserver::FindInPageNotificationObserver( + AutomationProvider* automation, TabContents* parent_tab, + IPC::Message* reply_message) + : automation_(automation), + active_match_ordinal_(-1), + reply_message_(reply_message) { + registrar_.Add(this, NotificationType::FIND_RESULT_AVAILABLE, + Source<TabContents>(parent_tab)); +} + +FindInPageNotificationObserver::~FindInPageNotificationObserver() { +} + +void FindInPageNotificationObserver::Observe( + NotificationType type, const NotificationSource& source, + const NotificationDetails& details) { + if (type == NotificationType::FIND_RESULT_AVAILABLE) { + Details<FindNotificationDetails> find_details(details); + if (find_details->request_id() == kFindInPageRequestId) { + // We get multiple responses and one of those will contain the ordinal. + // This message comes to us before the final update is sent. + if (find_details->active_match_ordinal() > -1) + active_match_ordinal_ = find_details->active_match_ordinal(); + if (find_details->final_update()) { + if (reply_message_ != NULL) { + AutomationMsg_FindInPage::WriteReplyParams(reply_message_, + active_match_ordinal_, find_details->number_of_matches()); + automation_->Send(reply_message_); + reply_message_ = NULL; + } else { + DLOG(WARNING) << "Multiple final Find messages observed."; + } + } else { + DLOG(INFO) << "Ignoring, since we only care about the final message"; + } + } + } else { + NOTREACHED(); + } +} + +// static +const int FindInPageNotificationObserver::kFindInPageRequestId = -1; + +DomOperationNotificationObserver::DomOperationNotificationObserver( + AutomationProvider* automation) + : automation_(automation) { + registrar_.Add(this, NotificationType::DOM_OPERATION_RESPONSE, + NotificationService::AllSources()); +} + +DomOperationNotificationObserver::~DomOperationNotificationObserver() { +} + +void DomOperationNotificationObserver::Observe( + NotificationType type, const NotificationSource& source, + const NotificationDetails& details) { + if (NotificationType::DOM_OPERATION_RESPONSE == type) { + Details<DomOperationNotificationDetails> dom_op_details(details); + + IPC::Message* reply_message = automation_->reply_message_release(); + if (reply_message) { + AutomationMsg_DomOperation::WriteReplyParams(reply_message, + dom_op_details->json()); + automation_->Send(reply_message); + } + } +} + +DocumentPrintedNotificationObserver::DocumentPrintedNotificationObserver( + AutomationProvider* automation, IPC::Message* reply_message) + : automation_(automation), + success_(false), + reply_message_(reply_message) { + registrar_.Add(this, NotificationType::PRINT_JOB_EVENT, + NotificationService::AllSources()); +} + +DocumentPrintedNotificationObserver::~DocumentPrintedNotificationObserver() { + DCHECK(reply_message_ != NULL); + AutomationMsg_PrintNow::WriteReplyParams(reply_message_, success_); + automation_->Send(reply_message_); + automation_->RemoveNavigationStatusListener(this); +} + +void DocumentPrintedNotificationObserver::Observe( + NotificationType type, const NotificationSource& source, + const NotificationDetails& details) { + using namespace printing; + DCHECK(type == NotificationType::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; + } + } +} + +MetricEventDurationObserver::MetricEventDurationObserver() { + registrar_.Add(this, NotificationType::METRIC_EVENT_DURATION, + NotificationService::AllSources()); +} + +int MetricEventDurationObserver::GetEventDurationMs( + const std::string& event_name) { + EventDurationMap::const_iterator it = durations_.find(event_name); + if (it == durations_.end()) + return -1; + return it->second; +} + +void MetricEventDurationObserver::Observe(NotificationType type, + const NotificationSource& source, const NotificationDetails& details) { + if (type != NotificationType::METRIC_EVENT_DURATION) { + NOTREACHED(); + return; + } + MetricEventDurationDetails* metric_event_duration = + Details<MetricEventDurationDetails>(details).ptr(); + durations_[metric_event_duration->event_name] = + metric_event_duration->duration_ms; +} + +#if defined(OS_CHROMEOS) +LoginManagerObserver::LoginManagerObserver( + AutomationProvider* automation, + IPC::Message* reply_message) + : automation_(automation), + reply_message_(reply_message) { + + registrar_.Add(this, NotificationType::LOGIN_AUTHENTICATION, + NotificationService::AllSources()); +} + +void LoginManagerObserver::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == NotificationType::LOGIN_AUTHENTICATION); + Details<AuthenticationNotificationDetails> auth_details(details); + AutomationMsg_LoginWithUserAndPass::WriteReplyParams(reply_message_, + auth_details->success()); + automation_->Send(reply_message_); + delete this; +} +#endif + +AutomationProviderBookmarkModelObserver::AutomationProviderBookmarkModelObserver( + AutomationProvider* provider, + IPC::Message* reply_message, + BookmarkModel* model) { + automation_provider_ = provider; + reply_message_ = reply_message; + model_ = model; + model_->AddObserver(this); +} + +AutomationProviderBookmarkModelObserver::~AutomationProviderBookmarkModelObserver() { + model_->RemoveObserver(this); +} + +void AutomationProviderBookmarkModelObserver::ReplyAndDelete(bool success) { + AutomationMsg_WaitForBookmarkModelToLoad::WriteReplyParams( + reply_message_, success); + automation_provider_->Send(reply_message_); + delete this; +} + +void AutomationProviderDownloadItemObserver::OnDownloadFileCompleted( + DownloadItem* download) { + download->RemoveObserver(this); + if (--downloads_ == 0) { + AutomationJSONReply(provider_, reply_message_).SendSuccess(NULL); + delete this; + } +} + +void AutomationProviderHistoryObserver::HistoryQueryComplete( + HistoryService::Handle request_handle, + history::QueryResults* results) { + scoped_ptr<DictionaryValue> return_value(new DictionaryValue); + + ListValue* history_list = new ListValue; + for (size_t i = 0; i < results->size(); ++i) { + DictionaryValue* page_value = new DictionaryValue; + history::URLResult const &page = (*results)[i]; + page_value->SetStringFromUTF16(L"title", page.title()); + page_value->SetString(L"url", page.url().spec()); + page_value->SetReal(L"time", + static_cast<double>(page.visit_time().ToDoubleT())); + page_value->SetStringFromUTF16(L"snippet", page.snippet().text()); + page_value->SetBoolean( + L"starred", + provider_->profile()->GetBookmarkModel()->IsBookmarked(page.url())); + history_list->Append(page_value); + } + + return_value->Set(L"history", history_list); + // Return history info. + AutomationJSONReply reply(provider_, reply_message_); + reply.SendSuccess(return_value.get()); + delete this; +} + +void AutomationProviderImportSettingsObserver::ImportEnded() { + // Send back an empty success message. + AutomationJSONReply(provider_, reply_message_).SendSuccess(NULL); + delete this; +} + +void AutomationProviderGetPasswordsObserver::OnPasswordStoreRequestDone( + int handle, const std::vector<webkit_glue::PasswordForm*>& result) { + scoped_ptr<DictionaryValue> return_value(new DictionaryValue); + + ListValue* passwords = new ListValue; + for (std::vector<webkit_glue::PasswordForm*>::const_iterator it = + result.begin(); it != result.end(); ++it) { + DictionaryValue* password_val = new DictionaryValue; + webkit_glue::PasswordForm* password_form = *it; + password_val->SetStringFromUTF16(L"username", + password_form->username_value); + password_val->SetStringFromUTF16(L"password", + password_form->password_value); + password_val->SetReal( + L"time", static_cast<double>( + password_form->date_created.ToDoubleT())); + passwords->Append(password_val); + } + + return_value->Set(L"passwords", passwords); + AutomationJSONReply(provider_, reply_message_).SendSuccess( + return_value.get()); + delete this; +} + +void AutomationProviderBrowsingDataObserver::OnBrowsingDataRemoverDone() { + // Send back an empty success message + AutomationJSONReply(provider_, reply_message_).SendSuccess(NULL); + delete this; +} + +OmniboxAcceptNotificationObserver::OmniboxAcceptNotificationObserver( + NavigationController* controller, + AutomationProvider* automation, + IPC::Message* reply_message) + : automation_(automation), + reply_message_(reply_message), + controller_(controller) { + Source<NavigationController> source(controller_); + registrar_.Add(this, NotificationType::LOAD_STOP, source); + // Pages requiring auth don't send LOAD_STOP. + registrar_.Add(this, NotificationType::AUTH_NEEDED, source); +} + +OmniboxAcceptNotificationObserver::~OmniboxAcceptNotificationObserver() { + automation_->RemoveNavigationStatusListener(this); +} + +void OmniboxAcceptNotificationObserver::Observe( + NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type == NotificationType::LOAD_STOP || + type == NotificationType::AUTH_NEEDED) { + AutomationJSONReply(automation_, reply_message_).SendSuccess(NULL); + delete this; + } else { + NOTREACHED(); + } +} + +SavePackageNotificationObserver::SavePackageNotificationObserver( + SavePackage* save_package, + AutomationProvider* automation, + IPC::Message* reply_message) : automation_(automation), + reply_message_(reply_message) { + Source<SavePackage> source(save_package); + registrar_.Add(this, NotificationType::SAVE_PACKAGE_SUCCESSFULLY_FINISHED, + source); +} + +void SavePackageNotificationObserver::Observe( + NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type == NotificationType::SAVE_PACKAGE_SUCCESSFULLY_FINISHED) { + AutomationJSONReply(automation_, reply_message_).SendSuccess(NULL); + delete this; + } else { + NOTREACHED(); + } +} + +WaitForInfobarCountObserver::WaitForInfobarCountObserver( + AutomationProvider* automation, + IPC::Message* reply_message, + TabContents* tab_contents, + int count) + : automation_(automation), + reply_message_(reply_message), + tab_contents_(tab_contents), + count_(count) { + if (tab_contents->infobar_delegate_count() == count) { + ConditionMet(); + return; + } + registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_ADDED, + Source<TabContents>(tab_contents_)); + registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_REMOVED, + Source<TabContents>(tab_contents_)); +} + +void WaitForInfobarCountObserver::Observe( + NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == NotificationType::TAB_CONTENTS_INFOBAR_ADDED || + type == NotificationType::TAB_CONTENTS_INFOBAR_REMOVED); + if (tab_contents_->infobar_delegate_count() == count_) { + ConditionMet(); + } +} + +void WaitForInfobarCountObserver::ConditionMet() { + registrar_.RemoveAll(); + AutomationJSONReply(automation_, reply_message_).SendSuccess(NULL); + delete this; +} |