// Copyright (c) 2012 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 #include #include #include "base/basictypes.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/callback.h" #include "base/file_util.h" #include "base/json/json_writer.h" #include "base/memory/scoped_ptr.h" #include "base/stl_util.h" #include "base/string_util.h" #include "base/stringprintf.h" #include "base/strings/string_number_conversions.h" #include "base/threading/thread_restrictions.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/app/chrome_command_ids.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/bookmarks/bookmark_model_factory.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/content_settings/tab_specific_content_settings.h" #include "chrome/browser/extensions/crx_installer.h" #include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/extension_process_manager.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/extensions/extension_tab_util.h" #include "chrome/browser/history/history_types.h" #include "chrome/browser/history/top_sites.h" #include "chrome/browser/infobars/confirm_infobar_delegate.h" #include "chrome/browser/infobars/infobar_service.h" #include "chrome/browser/metrics/metric_event_duration_details.h" #include "chrome/browser/notifications/balloon.h" #include "chrome/browser/notifications/balloon_collection.h" #include "chrome/browser/notifications/balloon_host.h" #include "chrome/browser/notifications/balloon_notification_ui_manager.h" #include "chrome/browser/notifications/notification.h" #include "chrome/browser/password_manager/password_store_change.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/renderer_host/chrome_render_message_filter.h" #include "chrome/browser/search_engines/template_url_service.h" #include "chrome/browser/search_engines/template_url_service_factory.h" #include "chrome/browser/sessions/session_tab_helper.h" #include "chrome/browser/sessions/tab_restore_service.h" #include "chrome/browser/sessions/tab_restore_service_factory.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_iterator.h" #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/find_bar/find_notification_details.h" #include "chrome/browser/ui/host_desktop.h" #include "chrome/browser/ui/login/login_prompt.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/webui/ntp/app_launcher_handler.h" #include "chrome/browser/ui/webui/ntp/most_visited_handler.h" #include "chrome/browser/ui/webui/ntp/new_tab_ui.h" #include "chrome/browser/ui/webui/ntp/recently_closed_tabs_handler.h" #include "chrome/common/automation_constants.h" #include "chrome/common/automation_messages.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/content_settings_types.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/manifest.h" #include "content/public/browser/dom_operation_notification_details.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "content/public/common/process_type.h" #include "extensions/common/view_type.h" #include "googleurl/src/gurl.h" #include "ui/gfx/codec/png_codec.h" #include "ui/gfx/rect.h" using content::BrowserThread; using content::DomOperationNotificationDetails; using content::DownloadItem; using content::DownloadManager; using content::NavigationController; using content::RenderViewHost; using content::WebContents; // 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->AsWeakPtr()), crashed_tab_count_(0), outstanding_tab_count_(tab_count), init_time_(base::TimeTicks::Now()) { if (outstanding_tab_count_ > 0) { registrar_.Add(this, content::NOTIFICATION_LOAD_START, content::NotificationService::AllSources()); registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, content::NotificationService::AllSources()); registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, content::NotificationService::AllSources()); } } InitialLoadObserver::~InitialLoadObserver() { } void InitialLoadObserver::Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (type == content::NOTIFICATION_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 == content::NOTIFICATION_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()); } } } else if (type == content::NOTIFICATION_RENDERER_PROCESS_CLOSED) { base::TerminationStatus status = content::Details( details)->status; switch (status) { case base::TERMINATION_STATUS_NORMAL_TERMINATION: break; case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: case base::TERMINATION_STATUS_PROCESS_CRASHED: crashed_tab_count_++; break; case base::TERMINATION_STATUS_STILL_RUNNING: LOG(ERROR) << "Got RENDERER_PROCESS_CLOSED notification, " << "but the process is still running. We may miss further " << "crash notification, resulting in hangs."; break; default: LOG(ERROR) << "Unhandled termination status " << status; NOTREACHED(); break; } } else { NOTREACHED(); } if (finished_tabs_.size() + crashed_tab_count_ >= outstanding_tab_count_) ConditionMet(); } 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->SetDouble("load_start_ms", delta_start.InMillisecondsF()); if (it->second.stop_time().is_null()) { item->Set("load_stop_ms", Value::CreateNullValue()); } else { base::TimeDelta delta_stop = it->second.stop_time() - init_time_; item->SetDouble("load_stop_ms", delta_stop.InMillisecondsF()); } items->Append(item); } DictionaryValue* return_value = new DictionaryValue; return_value->Set("tabs", items); return return_value; } void InitialLoadObserver::ConditionMet() { registrar_.RemoveAll(); if (automation_) automation_->OnInitialTabLoadsComplete(); } NewTabUILoadObserver::NewTabUILoadObserver(AutomationProvider* automation, Profile* profile) : automation_(automation->AsWeakPtr()) { registrar_.Add(this, chrome::NOTIFICATION_INITIAL_NEW_TAB_UI_LOAD, content::Source(profile)); } NewTabUILoadObserver::~NewTabUILoadObserver() { } void NewTabUILoadObserver::Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (type == chrome::NOTIFICATION_INITIAL_NEW_TAB_UI_LOAD) { content::Details load_time(details); if (automation_) { automation_->Send( new AutomationMsg_InitialNewTabUILoadComplete(*load_time.ptr())); } } else { NOTREACHED(); } } NavigationControllerRestoredObserver::NavigationControllerRestoredObserver( AutomationProvider* automation, NavigationController* controller, IPC::Message* reply_message) : automation_(automation->AsWeakPtr()), controller_(controller), reply_message_(reply_message) { if (FinishedRestoring()) { SendDone(); } else { registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, content::NotificationService::AllSources()); } } NavigationControllerRestoredObserver::~NavigationControllerRestoredObserver() { } void NavigationControllerRestoredObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (FinishedRestoring()) { registrar_.RemoveAll(); SendDone(); } } bool NavigationControllerRestoredObserver::FinishedRestoring() { return (!controller_->NeedsReload() && !controller_->GetPendingEntry() && !controller_->GetWebContents()->IsLoading()); } void NavigationControllerRestoredObserver::SendDone() { if (automation_) { AutomationJSONReply(automation_, reply_message_.release()) .SendSuccess(NULL); } delete this; } NavigationNotificationObserver::NavigationNotificationObserver( NavigationController* controller, AutomationProvider* automation, IPC::Message* reply_message, int number_of_navigations, bool include_current_navigation, bool use_json_interface) : automation_(automation->AsWeakPtr()), reply_message_(reply_message), controller_(controller), navigations_remaining_(number_of_navigations), navigation_started_(false), use_json_interface_(use_json_interface) { if (number_of_navigations == 0) { ConditionMet(AUTOMATION_MSG_NAVIGATION_SUCCESS); return; } DCHECK_LT(0, navigations_remaining_); content::Source source(controller_); registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, source); registrar_.Add(this, content::NOTIFICATION_LOAD_START, source); registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, source); registrar_.Add(this, chrome::NOTIFICATION_AUTH_NEEDED, source); registrar_.Add(this, chrome::NOTIFICATION_AUTH_SUPPLIED, source); registrar_.Add(this, chrome::NOTIFICATION_AUTH_CANCELLED, source); registrar_.Add(this, chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN, content::NotificationService::AllSources()); if (include_current_navigation && controller->GetWebContents()->IsLoading()) navigation_started_ = true; } NavigationNotificationObserver::~NavigationNotificationObserver() { } void NavigationNotificationObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (!automation_) { delete this; return; } // 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 chrome::NAV_ENTRY_COMMITTED until after we authenticate, so // we need the chrome::LOAD_START. if (type == content::NOTIFICATION_NAV_ENTRY_COMMITTED || type == content::NOTIFICATION_LOAD_START) { navigation_started_ = true; } else if (type == content::NOTIFICATION_LOAD_STOP) { if (navigation_started_) { navigation_started_ = false; if (--navigations_remaining_ == 0) ConditionMet(AUTOMATION_MSG_NAVIGATION_SUCCESS); } } else if (type == chrome::NOTIFICATION_AUTH_SUPPLIED || type == chrome::NOTIFICATION_AUTH_CANCELLED) { // Treat this as if navigation started again, since load start/stop don't // occur while authentication is ongoing. navigation_started_ = true; } else if (type == chrome::NOTIFICATION_AUTH_NEEDED) { // Respond that authentication is needed. navigation_started_ = false; ConditionMet(AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED); } else if (type == chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN) { ConditionMet(AUTOMATION_MSG_NAVIGATION_BLOCKED_BY_MODAL_DIALOG); } else { NOTREACHED(); } } void NavigationNotificationObserver::ConditionMet( AutomationMsg_NavigationResponseValues navigation_result) { if (automation_) { if (use_json_interface_) { if (navigation_result == AUTOMATION_MSG_NAVIGATION_SUCCESS) { DictionaryValue dict; dict.SetInteger("result", navigation_result); AutomationJSONReply(automation_, reply_message_.release()).SendSuccess( &dict); } else { AutomationJSONReply(automation_, reply_message_.release()).SendError( base::StringPrintf("Navigation failed with error code=%d.", navigation_result)); } } else { IPC::ParamTraits::Write( reply_message_.get(), navigation_result); automation_->Send(reply_message_.release()); } } delete this; } TabStripNotificationObserver::TabStripNotificationObserver( int notification, AutomationProvider* automation) : automation_(automation->AsWeakPtr()), notification_(notification) { registrar_.Add(this, notification_, content::NotificationService::AllSources()); } TabStripNotificationObserver::~TabStripNotificationObserver() { } void TabStripNotificationObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { DCHECK_EQ(notification_, type); if (type == chrome::NOTIFICATION_TAB_PARENTED) { ObserveTab(&content::Source(source)->GetController()); } else if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) { ObserveTab(&content::Source(source)->GetController()); } else { ObserveTab(content::Source(source).ptr()); } delete this; } TabAppendedNotificationObserver::TabAppendedNotificationObserver( Browser* parent, AutomationProvider* automation, IPC::Message* reply_message, bool use_json_interface) : TabStripNotificationObserver(chrome::NOTIFICATION_TAB_PARENTED, automation), parent_(parent), reply_message_(reply_message), use_json_interface_(use_json_interface) { } TabAppendedNotificationObserver::~TabAppendedNotificationObserver() {} void TabAppendedNotificationObserver::ObserveTab( NavigationController* controller) { if (!automation_ || !reply_message_.get()) return; if (automation_->GetIndexForNavigationController(controller, parent_) == TabStripModel::kNoTab) { // This tab notification doesn't belong to the parent_. return; } new NavigationNotificationObserver(controller, automation_, reply_message_.release(), 1, false, use_json_interface_); } IPC::Message* TabAppendedNotificationObserver::ReleaseReply() { return reply_message_.release(); } TabClosedNotificationObserver::TabClosedNotificationObserver( AutomationProvider* automation, bool wait_until_closed, IPC::Message* reply_message, bool use_json_interface) : TabStripNotificationObserver((wait_until_closed ? static_cast(content::NOTIFICATION_WEB_CONTENTS_DESTROYED) : static_cast(chrome::NOTIFICATION_TAB_CLOSING)), automation), reply_message_(reply_message), use_json_interface_(use_json_interface), for_browser_command_(false) { } TabClosedNotificationObserver::~TabClosedNotificationObserver() {} void TabClosedNotificationObserver::ObserveTab( NavigationController* controller) { if (!automation_) return; if (use_json_interface_) { AutomationJSONReply(automation_, reply_message_.release()).SendSuccess(NULL); } else { if (for_browser_command_) { AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_.get(), true); } else { AutomationMsg_CloseTab::WriteReplyParams(reply_message_.get(), true); } automation_->Send(reply_message_.release()); } } void TabClosedNotificationObserver::set_for_browser_command( bool for_browser_command) { for_browser_command_ = for_browser_command; } TabCountChangeObserver::TabCountChangeObserver(AutomationProvider* automation, Browser* browser, IPC::Message* reply_message, int target_tab_count) : automation_(automation->AsWeakPtr()), reply_message_(reply_message), tab_strip_model_(browser->tab_strip_model()), target_tab_count_(target_tab_count) { tab_strip_model_->AddObserver(this); CheckTabCount(); } TabCountChangeObserver::~TabCountChangeObserver() { tab_strip_model_->RemoveObserver(this); } void TabCountChangeObserver::TabInsertedAt(WebContents* contents, int index, bool foreground) { CheckTabCount(); } void TabCountChangeObserver::TabDetachedAt(WebContents* contents, int index) { CheckTabCount(); } void TabCountChangeObserver::TabStripModelDeleted() { if (automation_) { AutomationMsg_WaitForTabCountToBecome::WriteReplyParams( reply_message_.get(), false); automation_->Send(reply_message_.release()); } delete this; } void TabCountChangeObserver::CheckTabCount() { if (tab_strip_model_->count() != target_tab_count_) return; if (automation_) { AutomationMsg_WaitForTabCountToBecome::WriteReplyParams( reply_message_.get(), true); automation_->Send(reply_message_.release()); } delete this; } bool DidExtensionViewsStopLoading(ExtensionProcessManager* manager) { ExtensionProcessManager::ViewSet all_views = manager->GetAllViews(); for (ExtensionProcessManager::ViewSet::const_iterator iter = all_views.begin(); iter != all_views.end(); ++iter) { if ((*iter)->IsLoading()) return false; } return true; } ExtensionUninstallObserver::ExtensionUninstallObserver( AutomationProvider* automation, IPC::Message* reply_message, const std::string& id) : automation_(automation->AsWeakPtr()), reply_message_(reply_message), id_(id) { registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED, content::NotificationService::AllSources()); registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALL_NOT_ALLOWED, content::NotificationService::AllSources()); } ExtensionUninstallObserver::~ExtensionUninstallObserver() { } void ExtensionUninstallObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (!automation_) { delete this; return; } switch (type) { case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: { if (id_ == content::Details(details).ptr()->id()) { scoped_ptr return_value(new DictionaryValue); return_value->SetBoolean("success", true); AutomationJSONReply(automation_, reply_message_.release()) .SendSuccess(return_value.get()); delete this; return; } break; } case chrome::NOTIFICATION_EXTENSION_UNINSTALL_NOT_ALLOWED: { const extensions::Extension* extension = content::Details(details).ptr(); if (id_ == extension->id()) { scoped_ptr return_value(new DictionaryValue); return_value->SetBoolean("success", false); AutomationJSONReply(automation_, reply_message_.release()) .SendSuccess(return_value.get()); delete this; return; } break; } default: NOTREACHED(); } } ExtensionReadyNotificationObserver::ExtensionReadyNotificationObserver( ExtensionProcessManager* manager, ExtensionService* service, AutomationProvider* automation, IPC::Message* reply_message) : manager_(manager), service_(service), automation_(automation->AsWeakPtr()), reply_message_(reply_message), extension_(NULL) { Init(); } ExtensionReadyNotificationObserver::~ExtensionReadyNotificationObserver() { } void ExtensionReadyNotificationObserver::Init() { registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, content::NotificationService::AllSources()); registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, content::NotificationService::AllSources()); registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOAD_ERROR, content::NotificationService::AllSources()); registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR, content::NotificationService::AllSources()); registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED, content::NotificationService::AllSources()); } void ExtensionReadyNotificationObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (!automation_) { delete this; return; } switch (type) { case content::NOTIFICATION_LOAD_STOP: // Only continue on with this method if our extension has been loaded // and all the extension views have stopped loading. if (!extension_ || !DidExtensionViewsStopLoading(manager_)) return; break; case chrome::NOTIFICATION_EXTENSION_LOADED: { const extensions::Extension* loaded_extension = content::Details(details).ptr(); // Only track an internal or unpacked extension load. extensions::Manifest::Location location = loaded_extension->location(); if (location != extensions::Manifest::INTERNAL && !extensions::Manifest::IsUnpackedLocation(location)) return; extension_ = loaded_extension; if (!DidExtensionViewsStopLoading(manager_)) return; // For some reason, the background extension view is not yet // created at this point so just checking whether all extension views // are loaded is not sufficient. If background page is not ready, // we wait for NOTIFICATION_LOAD_STOP. if (!service_->IsBackgroundPageReady(extension_)) return; break; } case chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR: case chrome::NOTIFICATION_EXTENSION_LOAD_ERROR: case chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED: break; default: NOTREACHED(); break; } AutomationJSONReply reply(automation_, reply_message_.release()); if (extension_) { DictionaryValue dict; dict.SetString("id", extension_->id()); reply.SendSuccess(&dict); } else { reply.SendError("Extension could not be installed"); } delete this; } ExtensionUnloadNotificationObserver::ExtensionUnloadNotificationObserver() : did_receive_unload_notification_(false) { registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, content::NotificationService::AllSources()); } ExtensionUnloadNotificationObserver::~ExtensionUnloadNotificationObserver() { } void ExtensionUnloadNotificationObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) { did_receive_unload_notification_ = true; } else { NOTREACHED(); } } ExtensionsUpdatedObserver::ExtensionsUpdatedObserver( ExtensionProcessManager* manager, AutomationProvider* automation, IPC::Message* reply_message) : manager_(manager), automation_(automation->AsWeakPtr()), reply_message_(reply_message), updater_finished_(false) { registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, content::NotificationService::AllSources()); } ExtensionsUpdatedObserver::~ExtensionsUpdatedObserver() { } void ExtensionsUpdatedObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (!automation_) { delete this; return; } DCHECK(type == content::NOTIFICATION_LOAD_STOP); MaybeReply(); } void ExtensionsUpdatedObserver::UpdateCheckFinished() { if (!automation_) { delete this; return; } // Extension updater has completed updating all extensions. updater_finished_ = true; MaybeReply(); } void ExtensionsUpdatedObserver::MaybeReply() { // Send the reply if (1) the extension updater has finished updating all // extensions; and (2) all extension views have stopped loading. if (updater_finished_ && DidExtensionViewsStopLoading(manager_)) { AutomationJSONReply reply(automation_, reply_message_.release()); reply.SendSuccess(NULL); delete this; } } BrowserOpenedNotificationObserver::BrowserOpenedNotificationObserver( AutomationProvider* automation, IPC::Message* reply_message, bool use_json_interface) : automation_(automation->AsWeakPtr()), reply_message_(reply_message), new_window_id_(extension_misc::kUnknownWindowId), use_json_interface_(use_json_interface), for_browser_command_(false) { registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED, content::NotificationService::AllBrowserContextsAndSources()); registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, content::NotificationService::AllBrowserContextsAndSources()); } BrowserOpenedNotificationObserver::~BrowserOpenedNotificationObserver() { } void BrowserOpenedNotificationObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (!automation_) { delete this; return; } if (type == chrome::NOTIFICATION_BROWSER_OPENED) { // Store the new browser ID and continue waiting for a new tab within it // to stop loading. new_window_id_ = ExtensionTabUtil::GetWindowId( content::Source(source).ptr()); } else { DCHECK_EQ(content::NOTIFICATION_LOAD_STOP, type); // Only send the result if the loaded tab is in the new window. NavigationController* controller = content::Source(source).ptr(); SessionTabHelper* session_tab_helper = SessionTabHelper::FromWebContents(controller->GetWebContents()); int window_id = session_tab_helper ? session_tab_helper->window_id().id() : -1; if (window_id == new_window_id_) { if (use_json_interface_) { AutomationJSONReply(automation_, reply_message_.release()).SendSuccess(NULL); } else { if (for_browser_command_) { AutomationMsg_WindowExecuteCommand::WriteReplyParams( reply_message_.get(), true); } automation_->Send(reply_message_.release()); } delete this; return; } } } 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, bool use_json_interface) : automation_(automation->AsWeakPtr()), reply_message_(reply_message), use_json_interface_(use_json_interface), for_browser_command_(false) { registrar_.Add(this, chrome::NOTIFICATION_BROWSER_CLOSED, content::Source(browser)); } BrowserClosedNotificationObserver::~BrowserClosedNotificationObserver() {} void BrowserClosedNotificationObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { DCHECK_EQ(chrome::NOTIFICATION_BROWSER_CLOSED, type); if (!automation_) { delete this; return; } // The automation layer doesn't support non-native desktops. int browser_count = static_cast(BrowserList::GetInstance( chrome::HOST_DESKTOP_TYPE_NATIVE)->size()); // We get the notification before the browser is removed from the BrowserList. bool app_closing = browser_count == 1; if (use_json_interface_) { AutomationJSONReply(automation_, reply_message_.release()).SendSuccess(NULL); } else { if (for_browser_command_) { AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_.get(), true); } else { AutomationMsg_CloseBrowser::WriteReplyParams(reply_message_.get(), true, app_closing); } automation_->Send(reply_message_.release()); } 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->AsWeakPtr()), reply_message_(reply_message) { registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED, content::NotificationService::AllBrowserContextsAndSources()); registrar_.Add(this, chrome::NOTIFICATION_BROWSER_CLOSED, content::NotificationService::AllBrowserContextsAndSources()); } BrowserCountChangeNotificationObserver:: ~BrowserCountChangeNotificationObserver() {} void BrowserCountChangeNotificationObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { DCHECK(type == chrome::NOTIFICATION_BROWSER_OPENED || type == chrome::NOTIFICATION_BROWSER_CLOSED); // The automation layer doesn't support non-native desktops. int current_count = static_cast(BrowserList::GetInstance( chrome::HOST_DESKTOP_TYPE_NATIVE)->size()); if (type == chrome::NOTIFICATION_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 (!automation_) { delete this; return; } if (current_count == target_count_) { AutomationMsg_WaitForBrowserWindowCountToBecome::WriteReplyParams( reply_message_.get(), true); automation_->Send(reply_message_.release()); delete this; } } namespace { // Define mapping from command to notification struct CommandNotification { int command; int notification_type; }; const struct CommandNotification command_notifications[] = { {IDC_DUPLICATE_TAB, chrome::NOTIFICATION_TAB_PARENTED}, // Returns as soon as the restored tab is created. To further wait until // the content page is loaded, use WaitForTabToBeRestored. {IDC_RESTORE_TAB, chrome::NOTIFICATION_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, content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED}, {IDC_OPTIONS, content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED}, {IDC_PRINT, content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED}, {IDC_SHOW_DOWNLOADS, content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED}, {IDC_SHOW_HISTORY, content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED}, }; } // namespace ExecuteBrowserCommandObserver::~ExecuteBrowserCommandObserver() { } // static bool ExecuteBrowserCommandObserver::CreateAndRegisterObserver( AutomationProvider* automation, Browser* browser, int command, IPC::Message* reply_message, bool use_json_interface) { bool result = true; switch (command) { case IDC_NEW_TAB: { new NewTabObserver(automation, reply_message, use_json_interface); break; } case IDC_NEW_WINDOW: case IDC_NEW_INCOGNITO_WINDOW: { BrowserOpenedNotificationObserver* observer = new BrowserOpenedNotificationObserver(automation, reply_message, use_json_interface); observer->set_for_browser_command(true); break; } case IDC_CLOSE_WINDOW: { BrowserClosedNotificationObserver* observer = new BrowserClosedNotificationObserver(browser, automation, reply_message, use_json_interface); observer->set_for_browser_command(true); break; } case IDC_CLOSE_TAB: { TabClosedNotificationObserver* observer = new TabClosedNotificationObserver(automation, true, reply_message, use_json_interface); observer->set_for_browser_command(true); break; } case IDC_BACK: case IDC_FORWARD: case IDC_RELOAD: { new NavigationNotificationObserver( &browser->tab_strip_model()->GetActiveWebContents()->GetController(), automation, reply_message, 1, false, use_json_interface); break; } default: { ExecuteBrowserCommandObserver* observer = new ExecuteBrowserCommandObserver(automation, reply_message, use_json_interface); if (!observer->Register(command)) { observer->ReleaseReply(); delete observer; result = false; } break; } } return result; } void ExecuteBrowserCommandObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (type == notification_type_) { if (automation_) { if (use_json_interface_) { AutomationJSONReply(automation_, reply_message_.release()).SendSuccess(NULL); } else { AutomationMsg_WindowExecuteCommand::WriteReplyParams( reply_message_.get(), true); automation_->Send(reply_message_.release()); } } delete this; } else { NOTREACHED(); } } ExecuteBrowserCommandObserver::ExecuteBrowserCommandObserver( AutomationProvider* automation, IPC::Message* reply_message, bool use_json_interface) : automation_(automation->AsWeakPtr()), notification_type_(content::NOTIFICATION_ALL), reply_message_(reply_message), use_json_interface_(use_json_interface) { } bool ExecuteBrowserCommandObserver::Register(int command) { if (!Getint(command, ¬ification_type_)) return false; registrar_.Add(this, notification_type_, content::NotificationService::AllSources()); return true; } bool ExecuteBrowserCommandObserver::Getint( int command, int* 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; } IPC::Message* ExecuteBrowserCommandObserver::ReleaseReply() { return reply_message_.release(); } FindInPageNotificationObserver::FindInPageNotificationObserver( AutomationProvider* automation, WebContents* parent_tab, bool reply_with_json, IPC::Message* reply_message) : automation_(automation->AsWeakPtr()), active_match_ordinal_(-1), reply_with_json_(reply_with_json), reply_message_(reply_message) { registrar_.Add(this, chrome::NOTIFICATION_FIND_RESULT_AVAILABLE, content::Source(parent_tab)); } FindInPageNotificationObserver::~FindInPageNotificationObserver() { } void FindInPageNotificationObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { content::Details find_details(details); if (!(find_details->final_update() && reply_message_ != NULL)) { DVLOG(1) << "Ignoring, since we only care about the final message"; return; } if (!automation_) { delete this; return; } // 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->request_id() == kFindInPageRequestId) { if (reply_with_json_) { scoped_ptr return_value(new DictionaryValue); return_value->SetInteger("match_count", find_details->number_of_matches()); gfx::Rect rect = find_details->selection_rect(); // If MatchCount is > 0, then rect should not be Empty. // We dont guard it here because we want to let the test // code catch this invalid case if needed. if (!rect.IsEmpty()) { return_value->SetInteger("match_left", rect.x()); return_value->SetInteger("match_top", rect.y()); return_value->SetInteger("match_right", rect.right()); return_value->SetInteger("match_bottom", rect.bottom()); } AutomationJSONReply(automation_, reply_message_.release()) .SendSuccess(return_value.get()); delete this; } else { if (find_details->active_match_ordinal() > -1) { active_match_ordinal_ = find_details->active_match_ordinal(); AutomationMsg_Find::WriteReplyParams(reply_message_.get(), active_match_ordinal_, find_details->number_of_matches()); automation_->Send(reply_message_.release()); } } } } // static const int FindInPageNotificationObserver::kFindInPageRequestId = -1; DomOperationObserver::DomOperationObserver(int automation_id) : automation_id_(automation_id) { registrar_.Add(this, content::NOTIFICATION_DOM_OPERATION_RESPONSE, content::NotificationService::AllSources()); registrar_.Add(this, chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN, content::NotificationService::AllSources()); registrar_.Add(this, chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED, content::NotificationService::AllSources()); } DomOperationObserver::~DomOperationObserver() {} void DomOperationObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (type == content::NOTIFICATION_DOM_OPERATION_RESPONSE) { content::Details dom_op_details(details); if (dom_op_details->automation_id == automation_id_) OnDomOperationCompleted(dom_op_details->json); } else if (type == chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN) { OnModalDialogShown(); } else { DCHECK_EQ(chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED, type); WebContents* web_contents = content::Source(source).ptr(); if (web_contents) { TabSpecificContentSettings* tab_content_settings = TabSpecificContentSettings::FromWebContents(web_contents); if (tab_content_settings && tab_content_settings->IsContentBlocked( CONTENT_SETTINGS_TYPE_JAVASCRIPT)) OnJavascriptBlocked(); } } } DomOperationMessageSender::DomOperationMessageSender( AutomationProvider* automation, IPC::Message* reply_message, bool use_json_interface) : DomOperationObserver(0), automation_(automation->AsWeakPtr()), reply_message_(reply_message), use_json_interface_(use_json_interface) { } DomOperationMessageSender::~DomOperationMessageSender() {} void DomOperationMessageSender::OnDomOperationCompleted( const std::string& json) { if (automation_) { if (use_json_interface_) { DictionaryValue dict; dict.SetString("result", json); AutomationJSONReply(automation_, reply_message_.release()) .SendSuccess(&dict); } else { AutomationMsg_DomOperation::WriteReplyParams(reply_message_.get(), json); automation_->Send(reply_message_.release()); } } delete this; } void DomOperationMessageSender::OnModalDialogShown() { if (automation_ && use_json_interface_) { AutomationJSONReply(automation_, reply_message_.release()) .SendErrorCode(automation::kBlockedByModalDialog); delete this; } } void DomOperationMessageSender::OnJavascriptBlocked() { if (automation_ && use_json_interface_) { AutomationJSONReply(automation_, reply_message_.release()) .SendError("Javascript execution was blocked"); delete this; } } MetricEventDurationObserver::MetricEventDurationObserver() { registrar_.Add(this, chrome::NOTIFICATION_METRIC_EVENT_DURATION, content::NotificationService::AllSources()); } MetricEventDurationObserver::~MetricEventDurationObserver() {} 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( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (type != chrome::NOTIFICATION_METRIC_EVENT_DURATION) { NOTREACHED(); return; } MetricEventDurationDetails* metric_event_duration = content::Details(details).ptr(); durations_[metric_event_duration->event_name] = metric_event_duration->duration_ms; } InfoBarCountObserver::InfoBarCountObserver(AutomationProvider* automation, IPC::Message* reply_message, WebContents* web_contents, size_t target_count) : automation_(automation->AsWeakPtr()), reply_message_(reply_message), web_contents_(web_contents), target_count_(target_count) { content::Source source( InfoBarService::FromWebContents(web_contents)); registrar_.Add(this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED, source); registrar_.Add(this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED, source); CheckCount(); } InfoBarCountObserver::~InfoBarCountObserver() {} void InfoBarCountObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { DCHECK(type == chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED || type == chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED); CheckCount(); } void InfoBarCountObserver::CheckCount() { InfoBarService* infobar_service = InfoBarService::FromWebContents(web_contents_); if (infobar_service->infobar_count() != target_count_) return; if (automation_) { AutomationMsg_WaitForInfoBarCount::WriteReplyParams(reply_message_.get(), true); automation_->Send(reply_message_.release()); } delete this; } AutomationProviderBookmarkModelObserver:: AutomationProviderBookmarkModelObserver( AutomationProvider* provider, IPC::Message* reply_message, BookmarkModel* model, bool use_json_interface) : automation_provider_(provider->AsWeakPtr()), reply_message_(reply_message), model_(model), use_json_interface_(use_json_interface) { model_->AddObserver(this); } AutomationProviderBookmarkModelObserver:: ~AutomationProviderBookmarkModelObserver() { model_->RemoveObserver(this); } void AutomationProviderBookmarkModelObserver::Loaded(BookmarkModel* model, bool ids_reassigned) { ReplyAndDelete(true); } void AutomationProviderBookmarkModelObserver::BookmarkModelBeingDeleted( BookmarkModel* model) { ReplyAndDelete(false); } IPC::Message* AutomationProviderBookmarkModelObserver::ReleaseReply() { return reply_message_.release(); } void AutomationProviderBookmarkModelObserver::ReplyAndDelete(bool success) { if (automation_provider_) { if (use_json_interface_) { AutomationJSONReply(automation_provider_, reply_message_.release()).SendSuccess(NULL); } else { AutomationMsg_WaitForBookmarkModelToLoad::WriteReplyParams( reply_message_.get(), success); automation_provider_->Send(reply_message_.release()); } } delete this; } AutomationProviderDownloadUpdatedObserver:: AutomationProviderDownloadUpdatedObserver( AutomationProvider* provider, IPC::Message* reply_message, bool wait_for_open, bool incognito) : provider_(provider->AsWeakPtr()), reply_message_(reply_message), wait_for_open_(wait_for_open), incognito_(incognito) { } AutomationProviderDownloadUpdatedObserver:: ~AutomationProviderDownloadUpdatedObserver() {} void AutomationProviderDownloadUpdatedObserver::OnDownloadUpdated( DownloadItem* download) { // If this observer is watching for open, only send the reply if the download // has been auto-opened. if (wait_for_open_ && !download->GetAutoOpened()) return; download->RemoveObserver(this); if (provider_) { scoped_ptr return_value( provider_->GetDictionaryFromDownloadItem(download, incognito_)); AutomationJSONReply(provider_, reply_message_.release()).SendSuccess( return_value.get()); } delete this; } void AutomationProviderDownloadUpdatedObserver::OnDownloadOpened( DownloadItem* download) { download->RemoveObserver(this); if (provider_) { scoped_ptr return_value( provider_->GetDictionaryFromDownloadItem(download, incognito_)); AutomationJSONReply(provider_, reply_message_.release()).SendSuccess( return_value.get()); } delete this; } AutomationProviderDownloadModelChangedObserver:: AutomationProviderDownloadModelChangedObserver( AutomationProvider* provider, IPC::Message* reply_message, DownloadManager* download_manager) : provider_(provider->AsWeakPtr()), reply_message_(reply_message), notifier_(download_manager, this) { } AutomationProviderDownloadModelChangedObserver:: ~AutomationProviderDownloadModelChangedObserver() {} void AutomationProviderDownloadModelChangedObserver::ModelChanged() { if (provider_) AutomationJSONReply(provider_, reply_message_.release()).SendSuccess(NULL); delete this; } void AutomationProviderDownloadModelChangedObserver::OnDownloadCreated( DownloadManager* manager, DownloadItem* item) { ModelChanged(); } void AutomationProviderDownloadModelChangedObserver::OnDownloadRemoved( DownloadManager* manager, DownloadItem* item) { ModelChanged(); } AllDownloadsCompleteObserver::AllDownloadsCompleteObserver( AutomationProvider* provider, IPC::Message* reply_message, DownloadManager* download_manager, ListValue* pre_download_ids) : provider_(provider->AsWeakPtr()), reply_message_(reply_message), download_manager_(download_manager) { for (ListValue::iterator it = pre_download_ids->begin(); it != pre_download_ids->end(); ++it) { int val = 0; if ((*it)->GetAsInteger(&val)) { pre_download_ids_.insert(val); } else { AutomationJSONReply(provider_, reply_message_.release()) .SendError("Cannot convert ID of prior download to integer."); delete this; return; } } download_manager_->AddObserver(this); DownloadManager::DownloadVector all_items; download_manager->GetAllDownloads(&all_items); for (DownloadManager::DownloadVector::const_iterator it = all_items.begin(); it != all_items.end(); ++it) { OnDownloadCreated(download_manager_, *it); } ReplyIfNecessary(); } AllDownloadsCompleteObserver::~AllDownloadsCompleteObserver() { if (download_manager_) { download_manager_->RemoveObserver(this); download_manager_ = NULL; } for (std::set::const_iterator it = pending_downloads_.begin(); it != pending_downloads_.end(); ++it) { (*it)->RemoveObserver(this); } pending_downloads_.clear(); } void AllDownloadsCompleteObserver::ManagerGoingDown(DownloadManager* manager) { DCHECK_EQ(manager, download_manager_); download_manager_->RemoveObserver(this); download_manager_ = NULL; } void AllDownloadsCompleteObserver::OnDownloadCreated( DownloadManager* manager, DownloadItem* item) { // This method is also called in the c-tor for previously existing items. if (pre_download_ids_.find(item->GetId()) == pre_download_ids_.end() && item->GetState() == DownloadItem::IN_PROGRESS) { item->AddObserver(this); pending_downloads_.insert(item); } } void AllDownloadsCompleteObserver::OnDownloadUpdated(DownloadItem* download) { // If the current download's status has changed to a final state (not state // "in progress"), remove it from the pending list. if (download->GetState() != DownloadItem::IN_PROGRESS) { download->RemoveObserver(this); pending_downloads_.erase(download); ReplyIfNecessary(); } } void AllDownloadsCompleteObserver::ReplyIfNecessary() { if (!pending_downloads_.empty()) return; download_manager_->RemoveObserver(this); if (provider_) AutomationJSONReply(provider_, reply_message_.release()).SendSuccess(NULL); delete this; } AutomationProviderSearchEngineObserver::AutomationProviderSearchEngineObserver( AutomationProvider* provider, Profile* profile, IPC::Message* reply_message) : provider_(provider->AsWeakPtr()), profile_(profile), reply_message_(reply_message) { } AutomationProviderSearchEngineObserver:: ~AutomationProviderSearchEngineObserver() {} void AutomationProviderSearchEngineObserver::OnTemplateURLServiceChanged() { if (provider_) { TemplateURLService* url_service = TemplateURLServiceFactory::GetForProfile(profile_); url_service->RemoveObserver(this); AutomationJSONReply(provider_, reply_message_.release()).SendSuccess(NULL); } delete this; } AutomationProviderHistoryObserver::AutomationProviderHistoryObserver( AutomationProvider* provider, IPC::Message* reply_message) : provider_(provider->AsWeakPtr()), reply_message_(reply_message) { } AutomationProviderHistoryObserver::~AutomationProviderHistoryObserver() {} void AutomationProviderHistoryObserver::HistoryQueryComplete( HistoryService::Handle request_handle, history::QueryResults* results) { if (!provider_) { delete this; return; } scoped_ptr 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->SetString("title", page.title()); page_value->SetString("url", page.url().spec()); page_value->SetDouble("time", static_cast(page.visit_time().ToDoubleT())); page_value->SetString("snippet", page.snippet().text()); page_value->SetBoolean( "starred", BookmarkModelFactory::GetForProfile( provider_->profile())->IsBookmarked(page.url())); history_list->Append(page_value); } return_value->Set("history", history_list); // Return history info. AutomationJSONReply reply(provider_, reply_message_.release()); reply.SendSuccess(return_value.get()); delete this; } AutomationProviderImportSettingsObserver:: AutomationProviderImportSettingsObserver( AutomationProvider* provider, IPC::Message* reply_message) : provider_(provider->AsWeakPtr()), reply_message_(reply_message) { } AutomationProviderImportSettingsObserver:: ~AutomationProviderImportSettingsObserver() {} void AutomationProviderImportSettingsObserver::ImportStarted() { } void AutomationProviderImportSettingsObserver::ImportItemStarted( importer::ImportItem item) { } void AutomationProviderImportSettingsObserver::ImportItemEnded( importer::ImportItem item) { } void AutomationProviderImportSettingsObserver::ImportEnded() { if (provider_) AutomationJSONReply(provider_, reply_message_.release()).SendSuccess(NULL); delete this; } AutomationProviderGetPasswordsObserver::AutomationProviderGetPasswordsObserver( AutomationProvider* provider, IPC::Message* reply_message) : provider_(provider->AsWeakPtr()), reply_message_(reply_message) { } AutomationProviderGetPasswordsObserver:: ~AutomationProviderGetPasswordsObserver() {} void AutomationProviderGetPasswordsObserver::OnPasswordStoreRequestDone( CancelableRequestProvider::Handle handle, const std::vector& result) { if (!provider_) { delete this; return; } scoped_ptr return_value(new DictionaryValue); ListValue* passwords = new ListValue; for (std::vector::const_iterator it = result.begin(); it != result.end(); ++it) { DictionaryValue* password_val = new DictionaryValue; content::PasswordForm* password_form = *it; password_val->SetString("username_value", password_form->username_value); password_val->SetString("password_value", password_form->password_value); password_val->SetString("signon_realm", password_form->signon_realm); password_val->SetDouble( "time", static_cast(password_form->date_created.ToDoubleT())); password_val->SetString("origin_url", password_form->origin.spec()); password_val->SetString("username_element", password_form->username_element); password_val->SetString("password_element", password_form->password_element); password_val->SetString("submit_element", password_form->submit_element); password_val->SetString("action_target", password_form->action.spec()); password_val->SetBoolean("blacklist", password_form->blacklisted_by_user); passwords->Append(password_val); } return_value->Set("passwords", passwords); AutomationJSONReply(provider_, reply_message_.release()).SendSuccess( return_value.get()); delete this; } void AutomationProviderGetPasswordsObserver::OnGetPasswordStoreResults( const std::vector& results) { // TODO(kaiwang): Implement when I refactor // PasswordManager::GetAutofillableLogins. NOTIMPLEMENTED(); } PasswordStoreLoginsChangedObserver::PasswordStoreLoginsChangedObserver( AutomationProvider* automation, IPC::Message* reply_message, PasswordStoreChange::Type expected_type, const std::string& result_key) : automation_(automation->AsWeakPtr()), reply_message_(reply_message), expected_type_(expected_type), result_key_(result_key), done_event_(false, false) { AddRef(); } PasswordStoreLoginsChangedObserver::~PasswordStoreLoginsChangedObserver() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); } void PasswordStoreLoginsChangedObserver::Init() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); BrowserThread::PostTask( BrowserThread::DB, FROM_HERE, base::Bind(&PasswordStoreLoginsChangedObserver::RegisterObserversTask, this)); done_event_.Wait(); } void PasswordStoreLoginsChangedObserver::RegisterObserversTask() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); registrar_.reset(new content::NotificationRegistrar); registrar_->Add(this, chrome::NOTIFICATION_LOGINS_CHANGED, content::NotificationService::AllSources()); done_event_.Signal(); } void PasswordStoreLoginsChangedObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); DCHECK(type == chrome::NOTIFICATION_LOGINS_CHANGED); registrar_.reset(); // Must be done from the DB thread. PasswordStoreChangeList* change_details = content::Details(details).ptr(); if (change_details->size() != 1 || change_details->front().type() != expected_type_) { // Notify the UI thread that there's an error. std::string error = "Unexpected password store login change details."; BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&PasswordStoreLoginsChangedObserver::IndicateError, this, error)); return; } // Notify the UI thread that we're done listening. BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&PasswordStoreLoginsChangedObserver::IndicateDone, this)); } void PasswordStoreLoginsChangedObserver::IndicateDone() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (automation_) { if (result_key_.empty()) { AutomationJSONReply(automation_, reply_message_.release()) .SendSuccess(NULL); } else { scoped_ptr return_value(new DictionaryValue); return_value->SetBoolean(result_key_, true); AutomationJSONReply(automation_, reply_message_.release()) .SendSuccess(return_value.get()); } } Release(); } void PasswordStoreLoginsChangedObserver::IndicateError( const std::string& error) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (automation_) AutomationJSONReply(automation_, reply_message_.release()).SendError(error); Release(); } OmniboxAcceptNotificationObserver::OmniboxAcceptNotificationObserver( NavigationController* controller, AutomationProvider* automation, IPC::Message* reply_message) : automation_(automation->AsWeakPtr()), reply_message_(reply_message), controller_(controller) { content::Source source(controller_); registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, source); // Pages requiring auth don't send LOAD_STOP. registrar_.Add(this, chrome::NOTIFICATION_AUTH_NEEDED, source); } OmniboxAcceptNotificationObserver::~OmniboxAcceptNotificationObserver() { } void OmniboxAcceptNotificationObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (type == content::NOTIFICATION_LOAD_STOP || type == chrome::NOTIFICATION_AUTH_NEEDED) { if (automation_) { AutomationJSONReply(automation_, reply_message_.release()).SendSuccess(NULL); } delete this; } else { NOTREACHED(); } } SavePackageNotificationObserver::SavePackageNotificationObserver( content::DownloadManager* download_manager, AutomationProvider* automation, IPC::Message* reply_message) : download_manager_(download_manager), automation_(automation->AsWeakPtr()), reply_message_(reply_message) { download_manager_->AddObserver(this); } SavePackageNotificationObserver::~SavePackageNotificationObserver() { download_manager_->RemoveObserver(this); } void SavePackageNotificationObserver::OnSavePackageSuccessfullyFinished( content::DownloadManager* manager, content::DownloadItem* item) { if (automation_) { AutomationJSONReply(automation_, reply_message_.release()).SendSuccess(NULL); } delete this; } void SavePackageNotificationObserver::ManagerGoingDown( content::DownloadManager* manager) { delete this; } namespace { // Returns a vector of dictionaries containing information about installed apps, // as identified from a given list of extensions. The caller takes ownership // of the created vector. std::vector* GetAppInfoFromExtensions( const ExtensionSet* extensions, ExtensionService* ext_service) { std::vector* apps_list = new std::vector(); for (ExtensionSet::const_iterator ext = extensions->begin(); ext != extensions->end(); ++ext) { // Only return information about extensions that are actually apps. if ((*ext)->is_app()) { DictionaryValue* app_info = new DictionaryValue(); AppLauncherHandler::CreateAppInfo(*ext, ext_service, app_info); app_info->SetBoolean("is_component_extension", (*ext)->location() == extensions::Manifest::COMPONENT); // Convert the launch_type integer into a more descriptive string. int launch_type; const char* kLaunchType = "launch_type"; if (!app_info->GetInteger(kLaunchType, &launch_type)) { NOTREACHED() << "Can't get integer from key " << kLaunchType; continue; } if (launch_type == extensions::ExtensionPrefs::LAUNCH_PINNED) { app_info->SetString(kLaunchType, "pinned"); } else if (launch_type == extensions::ExtensionPrefs::LAUNCH_REGULAR) { app_info->SetString(kLaunchType, "regular"); } else if (launch_type == extensions::ExtensionPrefs::LAUNCH_FULLSCREEN) { app_info->SetString(kLaunchType, "fullscreen"); } else if (launch_type == extensions::ExtensionPrefs::LAUNCH_WINDOW) { app_info->SetString(kLaunchType, "window"); } else { app_info->SetString(kLaunchType, "unknown"); } apps_list->push_back(app_info); } } return apps_list; } } // namespace NTPInfoObserver::NTPInfoObserver(AutomationProvider* automation, IPC::Message* reply_message) : automation_(automation->AsWeakPtr()), reply_message_(reply_message), request_(0), ntp_info_(new DictionaryValue) { top_sites_ = automation_->profile()->GetTopSites(); if (!top_sites_) { AutomationJSONReply(automation_, reply_message_.release()) .SendError("Profile does not have service for querying the top sites."); return; } TabRestoreService* service = TabRestoreServiceFactory::GetForProfile(automation_->profile()); if (!service) { AutomationJSONReply(automation_, reply_message_.release()) .SendError("No TabRestoreService."); return; } // Collect information about the apps in the new tab page. ExtensionService* ext_service = extensions::ExtensionSystem::Get( automation_->profile())->extension_service(); if (!ext_service) { AutomationJSONReply(automation_, reply_message_.release()) .SendError("No ExtensionService."); return; } // Process enabled extensions. ListValue* apps_list = new ListValue(); const ExtensionSet* extensions = ext_service->extensions(); std::vector* enabled_apps = GetAppInfoFromExtensions( extensions, ext_service); for (std::vector::const_iterator app = enabled_apps->begin(); app != enabled_apps->end(); ++app) { (*app)->SetBoolean("is_disabled", false); apps_list->Append(*app); } delete enabled_apps; // Process disabled extensions. const ExtensionSet* disabled_extensions = ext_service->disabled_extensions(); std::vector* disabled_apps = GetAppInfoFromExtensions( disabled_extensions, ext_service); for (std::vector::const_iterator app = disabled_apps->begin(); app != disabled_apps->end(); ++app) { (*app)->SetBoolean("is_disabled", true); apps_list->Append(*app); } delete disabled_apps; // Process terminated extensions. const ExtensionSet* terminated_extensions = ext_service->terminated_extensions(); std::vector* terminated_apps = GetAppInfoFromExtensions( terminated_extensions, ext_service); for (std::vector::const_iterator app = terminated_apps->begin(); app != terminated_apps->end(); ++app) { (*app)->SetBoolean("is_disabled", true); apps_list->Append(*app); } delete terminated_apps; ntp_info_->Set("apps", apps_list); // Get the info that would be displayed in the recently closed section. ListValue* recently_closed_list = new ListValue; RecentlyClosedTabsHandler::CreateRecentlyClosedValues(service->entries(), recently_closed_list); ntp_info_->Set("recently_closed", recently_closed_list); // Add default site URLs. ListValue* default_sites_list = new ListValue; history::MostVisitedURLList urls = top_sites_->GetPrepopulatePages(); for (size_t i = 0; i < urls.size(); ++i) { default_sites_list->Append(Value::CreateStringValue( urls[i].url.possibly_invalid_spec())); } ntp_info_->Set("default_sites", default_sites_list); registrar_.Add(this, chrome::NOTIFICATION_TOP_SITES_UPDATED, content::Source(top_sites_)); if (top_sites_->loaded()) { OnTopSitesLoaded(); } else { registrar_.Add(this, chrome::NOTIFICATION_TOP_SITES_LOADED, content::Source(automation_->profile())); } } NTPInfoObserver::~NTPInfoObserver() {} void NTPInfoObserver::Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (type == chrome::NOTIFICATION_TOP_SITES_LOADED) { OnTopSitesLoaded(); } else if (type == chrome::NOTIFICATION_TOP_SITES_UPDATED) { content::Details request_details( details); if (request_ == *request_details.ptr()) { top_sites_->GetMostVisitedURLs( base::Bind(&NTPInfoObserver::OnTopSitesReceived, base::Unretained(this))); } } } void NTPInfoObserver::OnTopSitesLoaded() { request_ = top_sites_->StartQueryForMostVisited(); } void NTPInfoObserver::OnTopSitesReceived( const history::MostVisitedURLList& visited_list) { if (!automation_) { delete this; return; } ListValue* list_value = new ListValue; for (size_t i = 0; i < visited_list.size(); ++i) { const history::MostVisitedURL& visited = visited_list[i]; if (visited.url.spec().empty()) break; // This is the signal that there are no more real visited sites. DictionaryValue* dict = new DictionaryValue; dict->SetString("url", visited.url.spec()); dict->SetString("title", visited.title); list_value->Append(dict); } ntp_info_->Set("most_visited", list_value); AutomationJSONReply(automation_, reply_message_.release()).SendSuccess(ntp_info_.get()); delete this; } AppLaunchObserver::AppLaunchObserver( NavigationController* controller, AutomationProvider* automation, IPC::Message* reply_message, extension_misc::LaunchContainer launch_container) : controller_(controller), automation_(automation->AsWeakPtr()), reply_message_(reply_message), launch_container_(launch_container), new_window_id_(extension_misc::kUnknownWindowId) { if (launch_container_ == extension_misc::LAUNCH_TAB) { // Need to wait for the currently-active tab to reload. content::Source source(controller_); registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, source); } else { // Need to wait for a new tab in a new window to load. registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, content::NotificationService::AllSources()); registrar_.Add(this, chrome::NOTIFICATION_BROWSER_WINDOW_READY, content::NotificationService::AllSources()); } } AppLaunchObserver::~AppLaunchObserver() {} void AppLaunchObserver::Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (type == chrome::NOTIFICATION_BROWSER_WINDOW_READY) { new_window_id_ = ExtensionTabUtil::GetWindowId(content::Source(source).ptr()); return; } DCHECK_EQ(content::NOTIFICATION_LOAD_STOP, type); SessionTabHelper* session_tab_helper = SessionTabHelper::FromWebContents( content::Source(source)->GetWebContents()); if ((launch_container_ == extension_misc::LAUNCH_TAB) || (session_tab_helper && (session_tab_helper->window_id().id() == new_window_id_))) { if (automation_) { AutomationJSONReply(automation_, reply_message_.release()).SendSuccess(NULL); } delete this; } } namespace { // Returns whether all active notifications have an associated process ID. bool AreActiveNotificationProcessesReady() { BalloonNotificationUIManager* manager = BalloonNotificationUIManager::GetInstanceForTesting(); const BalloonCollection::Balloons& balloons = manager->balloon_collection()->GetActiveBalloons(); BalloonCollection::Balloons::const_iterator iter; for (iter = balloons.begin(); iter != balloons.end(); ++iter) { BalloonHost* balloon_host = (*iter)->balloon_view()->GetHost(); if (balloon_host && !balloon_host->IsRenderViewReady()) return false; } return true; } } // namespace GetAllNotificationsObserver::GetAllNotificationsObserver( AutomationProvider* automation, IPC::Message* reply_message) : automation_(automation->AsWeakPtr()), reply_message_(reply_message) { if (AreActiveNotificationProcessesReady()) { SendMessage(); } else { registrar_.Add(this, chrome::NOTIFICATION_NOTIFY_BALLOON_CONNECTED, content::NotificationService::AllSources()); } } GetAllNotificationsObserver::~GetAllNotificationsObserver() {} void GetAllNotificationsObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (!automation_) { delete this; return; } if (AreActiveNotificationProcessesReady()) SendMessage(); } base::DictionaryValue* GetAllNotificationsObserver::NotificationToJson( const Notification* note) { DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("content_url", note->content_url().spec()); dict->SetString("origin_url", note->origin_url().spec()); dict->SetString("display_source", note->display_source()); dict->SetString("id", note->notification_id()); return dict; } void GetAllNotificationsObserver::SendMessage() { BalloonNotificationUIManager* manager = BalloonNotificationUIManager::GetInstanceForTesting(); const BalloonCollection::Balloons& balloons = manager->balloon_collection()->GetActiveBalloons(); DictionaryValue return_value; ListValue* list = new ListValue; return_value.Set("notifications", list); BalloonCollection::Balloons::const_iterator balloon_iter; for (balloon_iter = balloons.begin(); balloon_iter != balloons.end(); ++balloon_iter) { base::DictionaryValue* note = NotificationToJson( &(*balloon_iter)->notification()); BalloonHost* balloon_host = (*balloon_iter)->balloon_view()->GetHost(); if (balloon_host) { int pid = base::GetProcId(balloon_host->web_contents()-> GetRenderViewHost()->GetProcess()->GetHandle()); note->SetInteger("pid", pid); } list->Append(note); } std::vector queued_notes; manager->GetQueuedNotificationsForTesting(&queued_notes); std::vector::const_iterator queued_iter; for (queued_iter = queued_notes.begin(); queued_iter != queued_notes.end(); ++queued_iter) { list->Append(NotificationToJson(*queued_iter)); } AutomationJSONReply(automation_, reply_message_.release()).SendSuccess(&return_value); delete this; } NewNotificationBalloonObserver::NewNotificationBalloonObserver( AutomationProvider* provider, IPC::Message* reply_message) : automation_(provider->AsWeakPtr()), reply_message_(reply_message) { registrar_.Add(this, chrome::NOTIFICATION_NOTIFY_BALLOON_CONNECTED, content::NotificationService::AllSources()); } NewNotificationBalloonObserver::~NewNotificationBalloonObserver() { } void NewNotificationBalloonObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (automation_) { AutomationJSONReply(automation_, reply_message_.release()).SendSuccess(NULL); } delete this; } OnNotificationBalloonCountObserver::OnNotificationBalloonCountObserver( AutomationProvider* provider, IPC::Message* reply_message, int count) : automation_(provider->AsWeakPtr()), reply_message_(reply_message), collection_(BalloonNotificationUIManager::GetInstanceForTesting()-> balloon_collection()), count_(count) { registrar_.Add(this, chrome::NOTIFICATION_NOTIFY_BALLOON_CONNECTED, content::NotificationService::AllSources()); collection_->set_on_collection_changed_callback( base::Bind(&OnNotificationBalloonCountObserver::CheckBalloonCount, base::Unretained(this))); CheckBalloonCount(); } OnNotificationBalloonCountObserver::~OnNotificationBalloonCountObserver() { } void OnNotificationBalloonCountObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { CheckBalloonCount(); } void OnNotificationBalloonCountObserver::CheckBalloonCount() { bool balloon_count_met = AreActiveNotificationProcessesReady() && static_cast(collection_->GetActiveBalloons().size()) == count_; if (balloon_count_met && automation_) { AutomationJSONReply(automation_, reply_message_.release()).SendSuccess(NULL); } if (balloon_count_met || !automation_) { collection_->set_on_collection_changed_callback(base::Closure()); delete this; } } RendererProcessClosedObserver::RendererProcessClosedObserver( AutomationProvider* automation, IPC::Message* reply_message) : automation_(automation->AsWeakPtr()), reply_message_(reply_message) { registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, content::NotificationService::AllSources()); } RendererProcessClosedObserver::~RendererProcessClosedObserver() {} void RendererProcessClosedObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (automation_) { AutomationJSONReply(automation_, reply_message_.release()).SendSuccess(NULL); } delete this; } InputEventAckNotificationObserver::InputEventAckNotificationObserver( AutomationProvider* automation, IPC::Message* reply_message, int event_type, int count) : automation_(automation->AsWeakPtr()), reply_message_(reply_message), event_type_(event_type), count_(count) { DCHECK(1 <= count); registrar_.Add( this, content::NOTIFICATION_RENDER_WIDGET_HOST_DID_RECEIVE_INPUT_EVENT_ACK, content::NotificationService::AllSources()); registrar_.Add( this, chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN, content::NotificationService::AllSources()); } InputEventAckNotificationObserver::~InputEventAckNotificationObserver() {} void InputEventAckNotificationObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (type == chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN) { AutomationJSONReply(automation_, reply_message_.release()).SendSuccess(NULL); delete this; return; } content::Details request_details(details); // If the event type matches for |count_| times, replies with a JSON message. if (event_type_ == *request_details.ptr()) { if (--count_ == 0 && automation_) { AutomationJSONReply(automation_, reply_message_.release()).SendSuccess(NULL); delete this; } } else { LOG(WARNING) << "Ignoring unexpected event type: " << *request_details.ptr() << " (expected: " << event_type_ << ")"; } } NewTabObserver::NewTabObserver(AutomationProvider* automation, IPC::Message* reply_message, bool use_json_interface) : automation_(automation->AsWeakPtr()), reply_message_(reply_message), use_json_interface_(use_json_interface) { // Use TAB_PARENTED to detect the new tab. registrar_.Add(this, chrome::NOTIFICATION_TAB_PARENTED, content::NotificationService::AllSources()); } void NewTabObserver::Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) { DCHECK_EQ(chrome::NOTIFICATION_TAB_PARENTED, type); NavigationController* controller = &(content::Source(source).ptr()->GetController()); if (automation_) { // TODO(phajdan.jr): Clean up this hack. We write the correct return type // here, but don't send the message. NavigationNotificationObserver // will wait properly for the load to finish, and send the message, // but it will also append its own return value at the end of the reply. if (!use_json_interface_) AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_.get(), true); new NavigationNotificationObserver(controller, automation_, reply_message_.release(), 1, false, use_json_interface_); } delete this; } NewTabObserver::~NewTabObserver() { } WaitForProcessLauncherThreadToGoIdleObserver:: WaitForProcessLauncherThreadToGoIdleObserver( AutomationProvider* automation, IPC::Message* reply_message) : automation_(automation->AsWeakPtr()), reply_message_(reply_message) { // Balanced in RunOnUIThread. AddRef(); BrowserThread::PostTask( BrowserThread::PROCESS_LAUNCHER, FROM_HERE, base::Bind( &WaitForProcessLauncherThreadToGoIdleObserver:: RunOnProcessLauncherThread, this)); } WaitForProcessLauncherThreadToGoIdleObserver:: ~WaitForProcessLauncherThreadToGoIdleObserver() { } void WaitForProcessLauncherThreadToGoIdleObserver:: RunOnProcessLauncherThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER)); BrowserThread::PostTask( BrowserThread::PROCESS_LAUNCHER, FROM_HERE, base::Bind( &WaitForProcessLauncherThreadToGoIdleObserver:: RunOnProcessLauncherThread2, this)); } void WaitForProcessLauncherThreadToGoIdleObserver:: RunOnProcessLauncherThread2() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER)); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&WaitForProcessLauncherThreadToGoIdleObserver::RunOnUIThread, this)); } void WaitForProcessLauncherThreadToGoIdleObserver::RunOnUIThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (automation_) automation_->Send(reply_message_.release()); Release(); } DragTargetDropAckNotificationObserver::DragTargetDropAckNotificationObserver( AutomationProvider* automation, IPC::Message* reply_message) : automation_(automation->AsWeakPtr()), reply_message_(reply_message) { registrar_.Add( this, content::NOTIFICATION_RENDER_VIEW_HOST_DID_RECEIVE_DRAG_TARGET_DROP_ACK, content::NotificationService::AllSources()); registrar_.Add( this, chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN, content::NotificationService::AllSources()); } DragTargetDropAckNotificationObserver:: ~DragTargetDropAckNotificationObserver() {} void DragTargetDropAckNotificationObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (automation_) { AutomationJSONReply(automation_, reply_message_.release()).SendSuccess(NULL); } delete this; } ProcessInfoObserver::ProcessInfoObserver( AutomationProvider* automation, IPC::Message* reply_message) : automation_(automation->AsWeakPtr()), reply_message_(reply_message) {} ProcessInfoObserver::~ProcessInfoObserver() {} void ProcessInfoObserver::OnDetailsAvailable() { scoped_ptr return_value(new DictionaryValue); ListValue* browser_proc_list = new ListValue(); const std::vector& all_processes = processes(); for (size_t index = 0; index < all_processes.size(); ++index) { DictionaryValue* browser_data = new DictionaryValue(); browser_data->SetString("name", all_processes[index].name); browser_data->SetString("process_name", all_processes[index].process_name); ListValue* proc_list = new ListValue(); for (ProcessMemoryInformationList::const_iterator iterator = all_processes[index].processes.begin(); iterator != all_processes[index].processes.end(); ++iterator) { DictionaryValue* proc_data = new DictionaryValue(); proc_data->SetInteger("pid", iterator->pid); // Working set (resident) memory usage, in KBytes. DictionaryValue* working_set = new DictionaryValue(); working_set->SetInteger("priv", iterator->working_set.priv); working_set->SetInteger("shareable", iterator->working_set.shareable); working_set->SetInteger("shared", iterator->working_set.shared); proc_data->Set("working_set_mem", working_set); // Committed (resident + paged) memory usage, in KBytes. DictionaryValue* committed = new DictionaryValue(); committed->SetInteger("priv", iterator->committed.priv); committed->SetInteger("mapped", iterator->committed.mapped); committed->SetInteger("image", iterator->committed.image); proc_data->Set("committed_mem", committed); proc_data->SetString("version", iterator->version); proc_data->SetString("product_name", iterator->product_name); proc_data->SetInteger("num_processes", iterator->num_processes); proc_data->SetBoolean("is_diagnostics", iterator->is_diagnostics); // Process type, if this is a child process of Chrome (e.g., 'plugin'). std::string process_type = "Unknown"; // The following condition avoids a DCHECK in debug builds when the // process type passed to |GetTypeNameInEnglish| is unknown. if (iterator->process_type != content::PROCESS_TYPE_UNKNOWN) { process_type = content::GetProcessTypeNameInEnglish(iterator->process_type); } proc_data->SetString("child_process_type", process_type); // Renderer type, if this is a renderer process. std::string renderer_type = "Unknown"; if (iterator->renderer_type != ProcessMemoryInformation::RENDERER_UNKNOWN) { renderer_type = ProcessMemoryInformation::GetRendererTypeNameInEnglish( iterator->renderer_type); } proc_data->SetString("renderer_type", renderer_type); // Titles associated with this process. ListValue* titles = new ListValue(); for (size_t title_index = 0; title_index < iterator->titles.size(); ++title_index) titles->Append(Value::CreateStringValue(iterator->titles[title_index])); proc_data->Set("titles", titles); proc_list->Append(proc_data); } browser_data->Set("processes", proc_list); browser_proc_list->Append(browser_data); } return_value->Set("browsers", browser_proc_list); if (automation_) { AutomationJSONReply(automation_, reply_message_.release()) .SendSuccess(return_value.get()); } } V8HeapStatsObserver::V8HeapStatsObserver( AutomationProvider* automation, IPC::Message* reply_message, base::ProcessId renderer_id) : automation_(automation->AsWeakPtr()), reply_message_(reply_message), renderer_id_(renderer_id) { registrar_.Add( this, chrome::NOTIFICATION_RENDERER_V8_HEAP_STATS_COMPUTED, content::NotificationService::AllSources()); } V8HeapStatsObserver::~V8HeapStatsObserver() {} void V8HeapStatsObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { DCHECK(type == chrome::NOTIFICATION_RENDERER_V8_HEAP_STATS_COMPUTED); base::ProcessId updated_renderer_id = *(content::Source(source).ptr()); // Only return information for the renderer ID we're interested in. if (renderer_id_ != updated_renderer_id) return; ChromeRenderMessageFilter::V8HeapStatsDetails* v8_heap_details = content::Details(details) .ptr(); scoped_ptr return_value(new DictionaryValue); return_value->SetInteger("renderer_id", updated_renderer_id); return_value->SetInteger("v8_memory_allocated", v8_heap_details->v8_memory_allocated()); return_value->SetInteger("v8_memory_used", v8_heap_details->v8_memory_used()); if (automation_) { AutomationJSONReply(automation_, reply_message_.release()) .SendSuccess(return_value.get()); } delete this; } FPSObserver::FPSObserver( AutomationProvider* automation, IPC::Message* reply_message, base::ProcessId renderer_id, int routing_id) : automation_(automation->AsWeakPtr()), reply_message_(reply_message), renderer_id_(renderer_id), routing_id_(routing_id) { registrar_.Add( this, chrome::NOTIFICATION_RENDERER_FPS_COMPUTED, content::NotificationService::AllSources()); } FPSObserver::~FPSObserver() {} void FPSObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { DCHECK(type == chrome::NOTIFICATION_RENDERER_FPS_COMPUTED); base::ProcessId updated_renderer_id = *(content::Source(source).ptr()); // Only return information for the renderer ID we're interested in. if (renderer_id_ != updated_renderer_id) return; ChromeRenderMessageFilter::FPSDetails* fps_details = content::Details(details).ptr(); // Only return information for the routing id of the host render view we're // interested in. if (routing_id_ != fps_details->routing_id()) return; scoped_ptr return_value(new DictionaryValue); return_value->SetInteger("renderer_id", updated_renderer_id); return_value->SetInteger("routing_id", fps_details->routing_id()); return_value->SetDouble("fps", fps_details->fps()); if (automation_) { AutomationJSONReply(automation_, reply_message_.release()) .SendSuccess(return_value.get()); } delete this; } BrowserOpenedWithNewProfileNotificationObserver:: BrowserOpenedWithNewProfileNotificationObserver( AutomationProvider* automation, IPC::Message* reply_message) : automation_(automation->AsWeakPtr()), reply_message_(reply_message), new_window_id_(extension_misc::kUnknownWindowId) { registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CREATED, content::NotificationService::AllBrowserContextsAndSources()); registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED, content::NotificationService::AllBrowserContextsAndSources()); registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, content::NotificationService::AllBrowserContextsAndSources()); } BrowserOpenedWithNewProfileNotificationObserver:: ~BrowserOpenedWithNewProfileNotificationObserver() { } void BrowserOpenedWithNewProfileNotificationObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (!automation_) { delete this; return; } if (type == chrome::NOTIFICATION_PROFILE_CREATED) { // As part of multi-profile creation, a new browser window will // automatically be opened. Profile* profile = content::Source(source).ptr(); if (!profile) { AutomationJSONReply(automation_, reply_message_.release()).SendError("Profile could not be created."); return; } } else if (type == chrome::NOTIFICATION_BROWSER_OPENED) { // Store the new browser ID and continue waiting for a new tab within it // to stop loading. new_window_id_ = ExtensionTabUtil::GetWindowId( content::Source(source).ptr()); } else { DCHECK_EQ(content::NOTIFICATION_LOAD_STOP, type); // Only send the result if the loaded tab is in the new window. NavigationController* controller = content::Source(source).ptr(); SessionTabHelper* session_tab_helper = SessionTabHelper::FromWebContents(controller->GetWebContents()); int window_id = session_tab_helper ? session_tab_helper->window_id().id() : -1; if (window_id == new_window_id_) { if (automation_) { AutomationJSONReply(automation_, reply_message_.release()) .SendSuccess(NULL); } delete this; } } } ExtensionPopupObserver::ExtensionPopupObserver( AutomationProvider* automation, IPC::Message* reply_message, const std::string& extension_id) : automation_(automation->AsWeakPtr()), reply_message_(reply_message), extension_id_(extension_id) { registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING, content::NotificationService::AllSources()); } ExtensionPopupObserver::~ExtensionPopupObserver() { } void ExtensionPopupObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (!automation_) { delete this; return; } extensions::ExtensionHost* host = content::Details(details).ptr(); if (host->extension_id() == extension_id_ && host->extension_host_type() == extensions::VIEW_TYPE_EXTENSION_POPUP) { AutomationJSONReply(automation_, reply_message_.release()) .SendSuccess(NULL); delete this; } } #if defined(OS_LINUX) WindowMaximizedObserver::WindowMaximizedObserver( AutomationProvider* automation, IPC::Message* reply_message) : automation_(automation->AsWeakPtr()), reply_message_(reply_message) { registrar_.Add(this, chrome::NOTIFICATION_BROWSER_WINDOW_MAXIMIZED, content::NotificationService::AllSources()); } WindowMaximizedObserver::~WindowMaximizedObserver() {} void WindowMaximizedObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { DCHECK_EQ(chrome::NOTIFICATION_BROWSER_WINDOW_MAXIMIZED, type); if (automation_) { AutomationJSONReply(automation_, reply_message_.release()) .SendSuccess(NULL); } delete this; } #endif // defined(OS_LINUX) BrowserOpenedWithExistingProfileNotificationObserver:: BrowserOpenedWithExistingProfileNotificationObserver( AutomationProvider* automation, IPC::Message* reply_message, int num_loads) : automation_(automation->AsWeakPtr()), reply_message_(reply_message), new_window_id_(extension_misc::kUnknownWindowId), num_loads_(num_loads) { registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED, content::NotificationService::AllBrowserContextsAndSources()); registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, content::NotificationService::AllBrowserContextsAndSources()); } BrowserOpenedWithExistingProfileNotificationObserver:: ~BrowserOpenedWithExistingProfileNotificationObserver() { } void BrowserOpenedWithExistingProfileNotificationObserver::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (!automation_) { delete this; return; } if (type == chrome::NOTIFICATION_BROWSER_OPENED) { // Store the new browser ID and continue waiting for NOTIFICATION_LOAD_STOP. new_window_id_ = ExtensionTabUtil::GetWindowId( content::Source(source).ptr()); } else if (type == content::NOTIFICATION_LOAD_STOP) { // Only consider if the loaded tab is in the new window. NavigationController* controller = content::Source(source).ptr(); SessionTabHelper* session_tab_helper = SessionTabHelper::FromWebContents(controller->GetWebContents()); int window_id = session_tab_helper ? session_tab_helper->window_id().id() : -1; if (window_id == new_window_id_ && --num_loads_ == 0) { if (automation_) { AutomationJSONReply(automation_, reply_message_.release()) .SendSuccess(NULL); } delete this; } } else { NOTREACHED(); } }