Home | History | Annotate | Download | only in automation
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/automation/automation_provider_observers.h"
      6 
      7 #include <deque>
      8 #include <string>
      9 #include <vector>
     10 
     11 #include "base/basictypes.h"
     12 #include "base/bind.h"
     13 #include "base/bind_helpers.h"
     14 #include "base/callback.h"
     15 #include "base/file_util.h"
     16 #include "base/json/json_writer.h"
     17 #include "base/memory/scoped_ptr.h"
     18 #include "base/stl_util.h"
     19 #include "base/strings/string_number_conversions.h"
     20 #include "base/strings/string_util.h"
     21 #include "base/strings/stringprintf.h"
     22 #include "base/strings/utf_string_conversions.h"
     23 #include "base/threading/thread_restrictions.h"
     24 #include "base/values.h"
     25 #include "chrome/app/chrome_command_ids.h"
     26 #include "chrome/browser/automation/automation_provider.h"
     27 #include "chrome/browser/automation/automation_provider_json.h"
     28 #include "chrome/browser/bookmarks/bookmark_model.h"
     29 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
     30 #include "chrome/browser/browser_process.h"
     31 #include "chrome/browser/chrome_notification_types.h"
     32 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
     33 #include "chrome/browser/extensions/crx_installer.h"
     34 #include "chrome/browser/extensions/extension_host.h"
     35 #include "chrome/browser/extensions/extension_service.h"
     36 #include "chrome/browser/extensions/extension_system.h"
     37 #include "chrome/browser/extensions/extension_tab_util.h"
     38 #include "chrome/browser/history/history_types.h"
     39 #include "chrome/browser/history/top_sites.h"
     40 #include "chrome/browser/infobars/confirm_infobar_delegate.h"
     41 #include "chrome/browser/infobars/infobar_service.h"
     42 #include "chrome/browser/metrics/metric_event_duration_details.h"
     43 #include "chrome/browser/notifications/balloon.h"
     44 #include "chrome/browser/notifications/balloon_collection.h"
     45 #include "chrome/browser/notifications/balloon_host.h"
     46 #include "chrome/browser/notifications/balloon_notification_ui_manager.h"
     47 #include "chrome/browser/notifications/notification.h"
     48 #include "chrome/browser/password_manager/password_store_change.h"
     49 #include "chrome/browser/profiles/profile.h"
     50 #include "chrome/browser/renderer_host/chrome_render_message_filter.h"
     51 #include "chrome/browser/search_engines/template_url_service.h"
     52 #include "chrome/browser/search_engines/template_url_service_factory.h"
     53 #include "chrome/browser/sessions/session_tab_helper.h"
     54 #include "chrome/browser/sessions/tab_restore_service.h"
     55 #include "chrome/browser/sessions/tab_restore_service_factory.h"
     56 #include "chrome/browser/ui/browser.h"
     57 #include "chrome/browser/ui/browser_iterator.h"
     58 #include "chrome/browser/ui/browser_list.h"
     59 #include "chrome/browser/ui/browser_window.h"
     60 #include "chrome/browser/ui/find_bar/find_notification_details.h"
     61 #include "chrome/browser/ui/host_desktop.h"
     62 #include "chrome/browser/ui/login/login_prompt.h"
     63 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     64 #include "chrome/browser/ui/webui/ntp/app_launcher_handler.h"
     65 #include "chrome/browser/ui/webui/ntp/most_visited_handler.h"
     66 #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
     67 #include "chrome/browser/ui/webui/ntp/recently_closed_tabs_handler.h"
     68 #include "chrome/common/automation_constants.h"
     69 #include "chrome/common/automation_messages.h"
     70 #include "chrome/common/content_settings_types.h"
     71 #include "chrome/common/extensions/extension_constants.h"
     72 #include "content/public/browser/dom_operation_notification_details.h"
     73 #include "content/public/browser/navigation_controller.h"
     74 #include "content/public/browser/notification_service.h"
     75 #include "content/public/browser/render_process_host.h"
     76 #include "content/public/browser/render_view_host.h"
     77 #include "content/public/browser/web_contents.h"
     78 #include "content/public/common/process_type.h"
     79 #include "extensions/browser/process_manager.h"
     80 #include "extensions/common/extension.h"
     81 #include "extensions/common/manifest.h"
     82 #include "extensions/common/view_type.h"
     83 #include "ui/gfx/codec/png_codec.h"
     84 #include "ui/gfx/rect.h"
     85 #include "url/gurl.h"
     86 
     87 using content::BrowserThread;
     88 using content::DomOperationNotificationDetails;
     89 using content::DownloadItem;
     90 using content::DownloadManager;
     91 using content::NavigationController;
     92 using content::RenderViewHost;
     93 using content::WebContents;
     94 
     95 // Holds onto start and stop timestamps for a particular tab
     96 class InitialLoadObserver::TabTime {
     97  public:
     98   explicit TabTime(base::TimeTicks started)
     99       : load_start_time_(started) {
    100   }
    101   void set_stop_time(base::TimeTicks stopped) {
    102     load_stop_time_ = stopped;
    103   }
    104   base::TimeTicks stop_time() const {
    105     return load_stop_time_;
    106   }
    107   base::TimeTicks start_time() const {
    108     return load_start_time_;
    109   }
    110  private:
    111   base::TimeTicks load_start_time_;
    112   base::TimeTicks load_stop_time_;
    113 };
    114 
    115 InitialLoadObserver::InitialLoadObserver(size_t tab_count,
    116                                          AutomationProvider* automation)
    117     : automation_(automation->AsWeakPtr()),
    118       crashed_tab_count_(0),
    119       outstanding_tab_count_(tab_count),
    120       init_time_(base::TimeTicks::Now()) {
    121   if (outstanding_tab_count_ > 0) {
    122     registrar_.Add(this, content::NOTIFICATION_LOAD_START,
    123                    content::NotificationService::AllSources());
    124     registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
    125                    content::NotificationService::AllSources());
    126     registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
    127                    content::NotificationService::AllSources());
    128   }
    129 }
    130 
    131 InitialLoadObserver::~InitialLoadObserver() {
    132 }
    133 
    134 void InitialLoadObserver::Observe(int type,
    135                                   const content::NotificationSource& source,
    136                                   const content::NotificationDetails& details) {
    137   if (type == content::NOTIFICATION_LOAD_START) {
    138     if (outstanding_tab_count_ > loading_tabs_.size())
    139       loading_tabs_.insert(TabTimeMap::value_type(
    140           source.map_key(),
    141           TabTime(base::TimeTicks::Now())));
    142   } else if (type == content::NOTIFICATION_LOAD_STOP) {
    143     if (outstanding_tab_count_ > finished_tabs_.size()) {
    144       TabTimeMap::iterator iter = loading_tabs_.find(source.map_key());
    145       if (iter != loading_tabs_.end()) {
    146         finished_tabs_.insert(source.map_key());
    147         iter->second.set_stop_time(base::TimeTicks::Now());
    148       }
    149     }
    150   } else if (type == content::NOTIFICATION_RENDERER_PROCESS_CLOSED) {
    151     base::TerminationStatus status =
    152         content::Details<content::RenderProcessHost::RendererClosedDetails>(
    153             details)->status;
    154     switch (status) {
    155       case base::TERMINATION_STATUS_NORMAL_TERMINATION:
    156         break;
    157 
    158       case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
    159       case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
    160       case base::TERMINATION_STATUS_PROCESS_CRASHED:
    161         crashed_tab_count_++;
    162         break;
    163 
    164       case base::TERMINATION_STATUS_STILL_RUNNING:
    165         LOG(ERROR) << "Got RENDERER_PROCESS_CLOSED notification, "
    166                    << "but the process is still running. We may miss further "
    167                    << "crash notification, resulting in hangs.";
    168         break;
    169 
    170       default:
    171         LOG(ERROR) << "Unhandled termination status " << status;
    172         NOTREACHED();
    173         break;
    174     }
    175   } else {
    176     NOTREACHED();
    177   }
    178 
    179   if (finished_tabs_.size() + crashed_tab_count_ >= outstanding_tab_count_)
    180     ConditionMet();
    181 }
    182 
    183 DictionaryValue* InitialLoadObserver::GetTimingInformation() const {
    184   ListValue* items = new ListValue;
    185   for (TabTimeMap::const_iterator it = loading_tabs_.begin();
    186        it != loading_tabs_.end();
    187        ++it) {
    188     DictionaryValue* item = new DictionaryValue;
    189     base::TimeDelta delta_start = it->second.start_time() - init_time_;
    190 
    191     item->SetDouble("load_start_ms", delta_start.InMillisecondsF());
    192     if (it->second.stop_time().is_null()) {
    193       item->Set("load_stop_ms", Value::CreateNullValue());
    194     } else {
    195       base::TimeDelta delta_stop = it->second.stop_time() - init_time_;
    196       item->SetDouble("load_stop_ms", delta_stop.InMillisecondsF());
    197     }
    198     items->Append(item);
    199   }
    200   DictionaryValue* return_value = new DictionaryValue;
    201   return_value->Set("tabs", items);
    202   return return_value;
    203 }
    204 
    205 void InitialLoadObserver::ConditionMet() {
    206   registrar_.RemoveAll();
    207   if (automation_.get())
    208     automation_->OnInitialTabLoadsComplete();
    209 }
    210 
    211 NewTabUILoadObserver::NewTabUILoadObserver(AutomationProvider* automation,
    212                                            Profile* profile)
    213     : automation_(automation->AsWeakPtr()) {
    214   registrar_.Add(this, chrome::NOTIFICATION_INITIAL_NEW_TAB_UI_LOAD,
    215                  content::Source<Profile>(profile));
    216 }
    217 
    218 NewTabUILoadObserver::~NewTabUILoadObserver() {
    219 }
    220 
    221 void NewTabUILoadObserver::Observe(int type,
    222                                    const content::NotificationSource& source,
    223                                    const content::NotificationDetails& details) {
    224   if (type == chrome::NOTIFICATION_INITIAL_NEW_TAB_UI_LOAD) {
    225     content::Details<int> load_time(details);
    226     if (automation_.get()) {
    227       automation_->Send(
    228           new AutomationMsg_InitialNewTabUILoadComplete(*load_time.ptr()));
    229     }
    230   } else {
    231     NOTREACHED();
    232   }
    233 }
    234 
    235 NavigationControllerRestoredObserver::NavigationControllerRestoredObserver(
    236     AutomationProvider* automation,
    237     NavigationController* controller,
    238     IPC::Message* reply_message)
    239     : automation_(automation->AsWeakPtr()),
    240       controller_(controller),
    241       reply_message_(reply_message) {
    242   if (FinishedRestoring()) {
    243     SendDone();
    244   } else {
    245     registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
    246                    content::NotificationService::AllSources());
    247   }
    248 }
    249 
    250 NavigationControllerRestoredObserver::~NavigationControllerRestoredObserver() {
    251 }
    252 
    253 void NavigationControllerRestoredObserver::Observe(
    254     int type, const content::NotificationSource& source,
    255     const content::NotificationDetails& details) {
    256   if (FinishedRestoring()) {
    257     registrar_.RemoveAll();
    258     SendDone();
    259   }
    260 }
    261 
    262 bool NavigationControllerRestoredObserver::FinishedRestoring() {
    263   return (!controller_->NeedsReload() && !controller_->GetPendingEntry() &&
    264           !controller_->GetWebContents()->IsLoading());
    265 }
    266 
    267 void NavigationControllerRestoredObserver::SendDone() {
    268   if (automation_.get()) {
    269     AutomationJSONReply(automation_.get(), reply_message_.release())
    270         .SendSuccess(NULL);
    271   }
    272   delete this;
    273 }
    274 
    275 NavigationNotificationObserver::NavigationNotificationObserver(
    276     NavigationController* controller,
    277     AutomationProvider* automation,
    278     IPC::Message* reply_message,
    279     int number_of_navigations,
    280     bool include_current_navigation,
    281     bool use_json_interface)
    282     : automation_(automation->AsWeakPtr()),
    283       reply_message_(reply_message),
    284       controller_(controller),
    285       navigations_remaining_(number_of_navigations),
    286       navigation_started_(false),
    287       use_json_interface_(use_json_interface) {
    288   if (number_of_navigations == 0) {
    289     ConditionMet(AUTOMATION_MSG_NAVIGATION_SUCCESS);
    290     return;
    291   }
    292   DCHECK_LT(0, navigations_remaining_);
    293   content::Source<NavigationController> source(controller_);
    294   registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, source);
    295   registrar_.Add(this, content::NOTIFICATION_LOAD_START, source);
    296   registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, source);
    297   registrar_.Add(this, chrome::NOTIFICATION_AUTH_NEEDED, source);
    298   registrar_.Add(this, chrome::NOTIFICATION_AUTH_SUPPLIED, source);
    299   registrar_.Add(this, chrome::NOTIFICATION_AUTH_CANCELLED, source);
    300   registrar_.Add(this, chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN,
    301                  content::NotificationService::AllSources());
    302 
    303   if (include_current_navigation && controller->GetWebContents()->IsLoading())
    304     navigation_started_ = true;
    305 }
    306 
    307 NavigationNotificationObserver::~NavigationNotificationObserver() {
    308 }
    309 
    310 void NavigationNotificationObserver::Observe(
    311     int type, const content::NotificationSource& source,
    312     const content::NotificationDetails& details) {
    313   if (!automation_.get()) {
    314     delete this;
    315     return;
    316   }
    317 
    318   // We listen for 2 events to determine when the navigation started because:
    319   // - when this is used by the WaitForNavigation method, we might be invoked
    320   // afer the load has started (but not after the entry was committed, as
    321   // WaitForNavigation compares times of the last navigation).
    322   // - when this is used with a page requiring authentication, we will not get
    323   // a chrome::NAV_ENTRY_COMMITTED until after we authenticate, so
    324   // we need the chrome::LOAD_START.
    325   if (type == content::NOTIFICATION_NAV_ENTRY_COMMITTED ||
    326       type == content::NOTIFICATION_LOAD_START) {
    327     navigation_started_ = true;
    328   } else if (type == content::NOTIFICATION_LOAD_STOP) {
    329     if (navigation_started_) {
    330       navigation_started_ = false;
    331       if (--navigations_remaining_ == 0)
    332         ConditionMet(AUTOMATION_MSG_NAVIGATION_SUCCESS);
    333     }
    334   } else if (type == chrome::NOTIFICATION_AUTH_SUPPLIED ||
    335              type == chrome::NOTIFICATION_AUTH_CANCELLED) {
    336     // Treat this as if navigation started again, since load start/stop don't
    337     // occur while authentication is ongoing.
    338     navigation_started_ = true;
    339   } else if (type == chrome::NOTIFICATION_AUTH_NEEDED) {
    340     // Respond that authentication is needed.
    341     navigation_started_ = false;
    342     ConditionMet(AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED);
    343   } else if (type == chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN) {
    344     ConditionMet(AUTOMATION_MSG_NAVIGATION_BLOCKED_BY_MODAL_DIALOG);
    345   } else {
    346     NOTREACHED();
    347   }
    348 }
    349 
    350 void NavigationNotificationObserver::ConditionMet(
    351     AutomationMsg_NavigationResponseValues navigation_result) {
    352   if (automation_.get()) {
    353     if (use_json_interface_) {
    354       if (navigation_result == AUTOMATION_MSG_NAVIGATION_SUCCESS) {
    355         DictionaryValue dict;
    356         dict.SetInteger("result", navigation_result);
    357         AutomationJSONReply(automation_.get(), reply_message_.release())
    358             .SendSuccess(&dict);
    359       } else {
    360         AutomationJSONReply(automation_.get(), reply_message_.release())
    361             .SendError(base::StringPrintf(
    362                  "Navigation failed with error code=%d.", navigation_result));
    363       }
    364     } else {
    365       IPC::ParamTraits<int>::Write(
    366           reply_message_.get(), navigation_result);
    367       automation_->Send(reply_message_.release());
    368     }
    369   }
    370 
    371   delete this;
    372 }
    373 
    374 TabStripNotificationObserver::TabStripNotificationObserver(
    375     int notification, AutomationProvider* automation)
    376     : automation_(automation->AsWeakPtr()),
    377       notification_(notification) {
    378   registrar_.Add(this, notification_,
    379                  content::NotificationService::AllSources());
    380 }
    381 
    382 TabStripNotificationObserver::~TabStripNotificationObserver() {
    383 }
    384 
    385 void TabStripNotificationObserver::Observe(
    386     int type,
    387     const content::NotificationSource& source,
    388     const content::NotificationDetails& details) {
    389   DCHECK_EQ(notification_, type);
    390   if (type == chrome::NOTIFICATION_TAB_PARENTED) {
    391     ObserveTab(&content::Source<content::WebContents>(source)->GetController());
    392   } else if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) {
    393     ObserveTab(&content::Source<content::WebContents>(source)->GetController());
    394   } else {
    395     ObserveTab(content::Source<NavigationController>(source).ptr());
    396   }
    397   delete this;
    398 }
    399 
    400 TabAppendedNotificationObserver::TabAppendedNotificationObserver(
    401     Browser* parent,
    402     AutomationProvider* automation,
    403     IPC::Message* reply_message,
    404     bool use_json_interface)
    405     : TabStripNotificationObserver(chrome::NOTIFICATION_TAB_PARENTED,
    406                                    automation),
    407       parent_(parent),
    408       reply_message_(reply_message),
    409       use_json_interface_(use_json_interface) {
    410 }
    411 
    412 TabAppendedNotificationObserver::~TabAppendedNotificationObserver() {}
    413 
    414 void TabAppendedNotificationObserver::ObserveTab(
    415     NavigationController* controller) {
    416   if (!automation_.get() || !reply_message_.get())
    417     return;
    418 
    419   if (automation_->GetIndexForNavigationController(controller, parent_) ==
    420       TabStripModel::kNoTab) {
    421     // This tab notification doesn't belong to the parent_.
    422     return;
    423   }
    424 
    425   new NavigationNotificationObserver(controller,
    426                                      automation_.get(),
    427                                      reply_message_.release(),
    428                                      1,
    429                                      false,
    430                                      use_json_interface_);
    431 }
    432 
    433 IPC::Message* TabAppendedNotificationObserver::ReleaseReply() {
    434   return reply_message_.release();
    435 }
    436 
    437 TabClosedNotificationObserver::TabClosedNotificationObserver(
    438     AutomationProvider* automation,
    439     bool wait_until_closed,
    440     IPC::Message* reply_message,
    441     bool use_json_interface)
    442     : TabStripNotificationObserver((wait_until_closed ?
    443           static_cast<int>(content::NOTIFICATION_WEB_CONTENTS_DESTROYED) :
    444           static_cast<int>(chrome::NOTIFICATION_TAB_CLOSING)), automation),
    445       reply_message_(reply_message),
    446       use_json_interface_(use_json_interface),
    447       for_browser_command_(false) {
    448 }
    449 
    450 TabClosedNotificationObserver::~TabClosedNotificationObserver() {}
    451 
    452 void TabClosedNotificationObserver::ObserveTab(
    453     NavigationController* controller) {
    454   if (!automation_.get())
    455     return;
    456 
    457   if (use_json_interface_) {
    458     AutomationJSONReply(automation_.get(), reply_message_.release())
    459         .SendSuccess(NULL);
    460   } else {
    461     if (for_browser_command_) {
    462       AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_.get(),
    463                                                            true);
    464     } else {
    465       AutomationMsg_CloseTab::WriteReplyParams(reply_message_.get(), true);
    466     }
    467     automation_->Send(reply_message_.release());
    468   }
    469 }
    470 
    471 void TabClosedNotificationObserver::set_for_browser_command(
    472     bool for_browser_command) {
    473   for_browser_command_ = for_browser_command;
    474 }
    475 
    476 TabCountChangeObserver::TabCountChangeObserver(AutomationProvider* automation,
    477                                                Browser* browser,
    478                                                IPC::Message* reply_message,
    479                                                int target_tab_count)
    480     : automation_(automation->AsWeakPtr()),
    481       reply_message_(reply_message),
    482       tab_strip_model_(browser->tab_strip_model()),
    483       target_tab_count_(target_tab_count) {
    484   tab_strip_model_->AddObserver(this);
    485   CheckTabCount();
    486 }
    487 
    488 TabCountChangeObserver::~TabCountChangeObserver() {
    489   tab_strip_model_->RemoveObserver(this);
    490 }
    491 
    492 void TabCountChangeObserver::TabInsertedAt(WebContents* contents,
    493                                            int index,
    494                                            bool foreground) {
    495   CheckTabCount();
    496 }
    497 
    498 void TabCountChangeObserver::TabDetachedAt(WebContents* contents,
    499                                            int index) {
    500   CheckTabCount();
    501 }
    502 
    503 void TabCountChangeObserver::TabStripModelDeleted() {
    504   if (automation_.get()) {
    505     AutomationMsg_WaitForTabCountToBecome::WriteReplyParams(
    506         reply_message_.get(), false);
    507     automation_->Send(reply_message_.release());
    508   }
    509 
    510   delete this;
    511 }
    512 
    513 void TabCountChangeObserver::CheckTabCount() {
    514   if (tab_strip_model_->count() != target_tab_count_)
    515     return;
    516 
    517   if (automation_.get()) {
    518     AutomationMsg_WaitForTabCountToBecome::WriteReplyParams(
    519         reply_message_.get(), true);
    520     automation_->Send(reply_message_.release());
    521   }
    522 
    523   delete this;
    524 }
    525 
    526 bool DidExtensionViewsStopLoading(extensions::ProcessManager* manager) {
    527   extensions::ProcessManager::ViewSet all_views = manager->GetAllViews();
    528   for (extensions::ProcessManager::ViewSet::const_iterator iter =
    529            all_views.begin();
    530        iter != all_views.end(); ++iter) {
    531     if ((*iter)->IsLoading())
    532       return false;
    533   }
    534   return true;
    535 }
    536 
    537 ExtensionUninstallObserver::ExtensionUninstallObserver(
    538     AutomationProvider* automation,
    539     IPC::Message* reply_message,
    540     const std::string& id)
    541     : automation_(automation->AsWeakPtr()),
    542       reply_message_(reply_message),
    543       id_(id) {
    544   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
    545                  content::NotificationService::AllSources());
    546   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALL_NOT_ALLOWED,
    547                  content::NotificationService::AllSources());
    548 }
    549 
    550 ExtensionUninstallObserver::~ExtensionUninstallObserver() {
    551 }
    552 
    553 void ExtensionUninstallObserver::Observe(
    554     int type,
    555     const content::NotificationSource& source,
    556     const content::NotificationDetails& details) {
    557   if (!automation_.get()) {
    558     delete this;
    559     return;
    560   }
    561 
    562   switch (type) {
    563     case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: {
    564       if (id_ == content::Details<extensions::Extension>(details).ptr()->id()) {
    565         scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
    566         return_value->SetBoolean("success", true);
    567         AutomationJSONReply(automation_.get(), reply_message_.release())
    568             .SendSuccess(return_value.get());
    569         delete this;
    570         return;
    571       }
    572       break;
    573     }
    574 
    575     case chrome::NOTIFICATION_EXTENSION_UNINSTALL_NOT_ALLOWED: {
    576       const extensions::Extension* extension =
    577           content::Details<extensions::Extension>(details).ptr();
    578       if (id_ == extension->id()) {
    579         scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
    580         return_value->SetBoolean("success", false);
    581         AutomationJSONReply(automation_.get(), reply_message_.release())
    582             .SendSuccess(return_value.get());
    583         delete this;
    584         return;
    585       }
    586       break;
    587     }
    588 
    589     default:
    590       NOTREACHED();
    591   }
    592 }
    593 
    594 ExtensionReadyNotificationObserver::ExtensionReadyNotificationObserver(
    595     extensions::ProcessManager* manager, ExtensionService* service,
    596     AutomationProvider* automation, IPC::Message* reply_message)
    597     : manager_(manager),
    598       service_(service),
    599       automation_(automation->AsWeakPtr()),
    600       reply_message_(reply_message),
    601       extension_(NULL) {
    602   Init();
    603 }
    604 
    605 ExtensionReadyNotificationObserver::~ExtensionReadyNotificationObserver() {
    606 }
    607 
    608 void ExtensionReadyNotificationObserver::Init() {
    609   registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
    610                  content::NotificationService::AllSources());
    611   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
    612                  content::NotificationService::AllSources());
    613   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOAD_ERROR,
    614                  content::NotificationService::AllSources());
    615   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR,
    616                  content::NotificationService::AllSources());
    617   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED,
    618                  content::NotificationService::AllSources());
    619 }
    620 
    621 void ExtensionReadyNotificationObserver::Observe(
    622     int type, const content::NotificationSource& source,
    623     const content::NotificationDetails& details) {
    624   if (!automation_.get()) {
    625     delete this;
    626     return;
    627   }
    628 
    629   switch (type) {
    630     case content::NOTIFICATION_LOAD_STOP:
    631       // Only continue on with this method if our extension has been loaded
    632       // and all the extension views have stopped loading.
    633       if (!extension_ || !DidExtensionViewsStopLoading(manager_))
    634         return;
    635       break;
    636     case chrome::NOTIFICATION_EXTENSION_LOADED: {
    637       const extensions::Extension* loaded_extension =
    638           content::Details<const extensions::Extension>(details).ptr();
    639       // Only track an internal or unpacked extension load.
    640       extensions::Manifest::Location location = loaded_extension->location();
    641       if (location != extensions::Manifest::INTERNAL &&
    642           !extensions::Manifest::IsUnpackedLocation(location))
    643         return;
    644       extension_ = loaded_extension;
    645       if (!DidExtensionViewsStopLoading(manager_))
    646         return;
    647       // For some reason, the background extension view is not yet
    648       // created at this point so just checking whether all extension views
    649       // are loaded is not sufficient. If background page is not ready,
    650       // we wait for NOTIFICATION_LOAD_STOP.
    651       if (!service_->IsBackgroundPageReady(extension_))
    652         return;
    653       break;
    654     }
    655     case chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR:
    656     case chrome::NOTIFICATION_EXTENSION_LOAD_ERROR:
    657     case chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED:
    658       break;
    659     default:
    660       NOTREACHED();
    661       break;
    662   }
    663 
    664   AutomationJSONReply reply(automation_.get(), reply_message_.release());
    665   if (extension_) {
    666     DictionaryValue dict;
    667     dict.SetString("id", extension_->id());
    668     reply.SendSuccess(&dict);
    669   } else {
    670     reply.SendError("Extension could not be installed");
    671   }
    672   delete this;
    673 }
    674 
    675 ExtensionUnloadNotificationObserver::ExtensionUnloadNotificationObserver()
    676     : did_receive_unload_notification_(false) {
    677   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
    678                  content::NotificationService::AllSources());
    679 }
    680 
    681 ExtensionUnloadNotificationObserver::~ExtensionUnloadNotificationObserver() {
    682 }
    683 
    684 void ExtensionUnloadNotificationObserver::Observe(
    685     int type, const content::NotificationSource& source,
    686     const content::NotificationDetails& details) {
    687   if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
    688     did_receive_unload_notification_ = true;
    689   } else {
    690     NOTREACHED();
    691   }
    692 }
    693 
    694 ExtensionsUpdatedObserver::ExtensionsUpdatedObserver(
    695     extensions::ProcessManager* manager, AutomationProvider* automation,
    696     IPC::Message* reply_message)
    697     : manager_(manager), automation_(automation->AsWeakPtr()),
    698       reply_message_(reply_message), updater_finished_(false) {
    699   registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
    700                  content::NotificationService::AllSources());
    701 }
    702 
    703 ExtensionsUpdatedObserver::~ExtensionsUpdatedObserver() {
    704 }
    705 
    706 void ExtensionsUpdatedObserver::Observe(
    707     int type, const content::NotificationSource& source,
    708     const content::NotificationDetails& details) {
    709   if (!automation_.get()) {
    710     delete this;
    711     return;
    712   }
    713 
    714   DCHECK(type == content::NOTIFICATION_LOAD_STOP);
    715   MaybeReply();
    716 }
    717 
    718 void ExtensionsUpdatedObserver::UpdateCheckFinished() {
    719   if (!automation_.get()) {
    720     delete this;
    721     return;
    722   }
    723 
    724   // Extension updater has completed updating all extensions.
    725   updater_finished_ = true;
    726   MaybeReply();
    727 }
    728 
    729 void ExtensionsUpdatedObserver::MaybeReply() {
    730   // Send the reply if (1) the extension updater has finished updating all
    731   // extensions; and (2) all extension views have stopped loading.
    732   if (updater_finished_ && DidExtensionViewsStopLoading(manager_)) {
    733     AutomationJSONReply reply(automation_.get(), reply_message_.release());
    734     reply.SendSuccess(NULL);
    735     delete this;
    736   }
    737 }
    738 
    739 BrowserOpenedNotificationObserver::BrowserOpenedNotificationObserver(
    740     AutomationProvider* automation,
    741     IPC::Message* reply_message,
    742     bool use_json_interface)
    743     : automation_(automation->AsWeakPtr()),
    744       reply_message_(reply_message),
    745       new_window_id_(extension_misc::kUnknownWindowId),
    746       use_json_interface_(use_json_interface),
    747       for_browser_command_(false) {
    748   registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED,
    749                  content::NotificationService::AllBrowserContextsAndSources());
    750   registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
    751                  content::NotificationService::AllBrowserContextsAndSources());
    752 }
    753 
    754 BrowserOpenedNotificationObserver::~BrowserOpenedNotificationObserver() {
    755 }
    756 
    757 void BrowserOpenedNotificationObserver::Observe(
    758     int type,
    759     const content::NotificationSource& source,
    760     const content::NotificationDetails& details) {
    761   if (!automation_.get()) {
    762     delete this;
    763     return;
    764   }
    765 
    766   if (type == chrome::NOTIFICATION_BROWSER_OPENED) {
    767     // Store the new browser ID and continue waiting for a new tab within it
    768     // to stop loading.
    769     new_window_id_ = extensions::ExtensionTabUtil::GetWindowId(
    770         content::Source<Browser>(source).ptr());
    771   } else {
    772     DCHECK_EQ(content::NOTIFICATION_LOAD_STOP, type);
    773     // Only send the result if the loaded tab is in the new window.
    774     NavigationController* controller =
    775         content::Source<NavigationController>(source).ptr();
    776     SessionTabHelper* session_tab_helper =
    777         SessionTabHelper::FromWebContents(controller->GetWebContents());
    778     int window_id = session_tab_helper ? session_tab_helper->window_id().id()
    779                                        : -1;
    780     if (window_id == new_window_id_) {
    781       if (use_json_interface_) {
    782         AutomationJSONReply(automation_.get(), reply_message_.release())
    783             .SendSuccess(NULL);
    784       } else {
    785         if (for_browser_command_) {
    786           AutomationMsg_WindowExecuteCommand::WriteReplyParams(
    787               reply_message_.get(), true);
    788         }
    789         automation_->Send(reply_message_.release());
    790       }
    791       delete this;
    792       return;
    793     }
    794   }
    795 }
    796 
    797 void BrowserOpenedNotificationObserver::set_for_browser_command(
    798     bool for_browser_command) {
    799   for_browser_command_ = for_browser_command;
    800 }
    801 
    802 BrowserClosedNotificationObserver::BrowserClosedNotificationObserver(
    803     Browser* browser,
    804     AutomationProvider* automation,
    805     IPC::Message* reply_message,
    806     bool use_json_interface)
    807     : automation_(automation->AsWeakPtr()),
    808       reply_message_(reply_message),
    809       use_json_interface_(use_json_interface),
    810       for_browser_command_(false) {
    811   registrar_.Add(this, chrome::NOTIFICATION_BROWSER_CLOSED,
    812                  content::Source<Browser>(browser));
    813 }
    814 
    815 BrowserClosedNotificationObserver::~BrowserClosedNotificationObserver() {}
    816 
    817 void BrowserClosedNotificationObserver::Observe(
    818     int type, const content::NotificationSource& source,
    819     const content::NotificationDetails& details) {
    820   DCHECK_EQ(chrome::NOTIFICATION_BROWSER_CLOSED, type);
    821 
    822   if (!automation_.get()) {
    823     delete this;
    824     return;
    825   }
    826 
    827   // The automation layer doesn't support non-native desktops.
    828   int browser_count = static_cast<int>(BrowserList::GetInstance(
    829                           chrome::HOST_DESKTOP_TYPE_NATIVE)->size());
    830   // We get the notification before the browser is removed from the BrowserList.
    831   bool app_closing = browser_count == 1;
    832 
    833   if (use_json_interface_) {
    834     AutomationJSONReply(automation_.get(), reply_message_.release())
    835         .SendSuccess(NULL);
    836   } else {
    837     if (for_browser_command_) {
    838       AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_.get(),
    839                                                            true);
    840     } else {
    841       AutomationMsg_CloseBrowser::WriteReplyParams(reply_message_.get(), true,
    842                                                    app_closing);
    843     }
    844     automation_->Send(reply_message_.release());
    845   }
    846   delete this;
    847 }
    848 
    849 void BrowserClosedNotificationObserver::set_for_browser_command(
    850     bool for_browser_command) {
    851   for_browser_command_ = for_browser_command;
    852 }
    853 
    854 BrowserCountChangeNotificationObserver::BrowserCountChangeNotificationObserver(
    855     int target_count,
    856     AutomationProvider* automation,
    857     IPC::Message* reply_message)
    858     : target_count_(target_count),
    859       automation_(automation->AsWeakPtr()),
    860       reply_message_(reply_message) {
    861   registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED,
    862                  content::NotificationService::AllBrowserContextsAndSources());
    863   registrar_.Add(this, chrome::NOTIFICATION_BROWSER_CLOSED,
    864                  content::NotificationService::AllBrowserContextsAndSources());
    865 }
    866 
    867 BrowserCountChangeNotificationObserver::
    868     ~BrowserCountChangeNotificationObserver() {}
    869 
    870 void BrowserCountChangeNotificationObserver::Observe(
    871     int type,
    872     const content::NotificationSource& source,
    873     const content::NotificationDetails& details) {
    874   DCHECK(type == chrome::NOTIFICATION_BROWSER_OPENED ||
    875          type == chrome::NOTIFICATION_BROWSER_CLOSED);
    876 
    877   // The automation layer doesn't support non-native desktops.
    878   int current_count = static_cast<int>(BrowserList::GetInstance(
    879                           chrome::HOST_DESKTOP_TYPE_NATIVE)->size());
    880   if (type == chrome::NOTIFICATION_BROWSER_CLOSED) {
    881     // At the time of the notification the browser being closed is not removed
    882     // from the list. The real count is one less than the reported count.
    883     DCHECK_LT(0, current_count);
    884     current_count--;
    885   }
    886 
    887   if (!automation_.get()) {
    888     delete this;
    889     return;
    890   }
    891 
    892   if (current_count == target_count_) {
    893     AutomationMsg_WaitForBrowserWindowCountToBecome::WriteReplyParams(
    894         reply_message_.get(), true);
    895     automation_->Send(reply_message_.release());
    896     delete this;
    897   }
    898 }
    899 
    900 namespace {
    901 
    902 // Define mapping from command to notification
    903 struct CommandNotification {
    904   int command;
    905   int notification_type;
    906 };
    907 
    908 const struct CommandNotification command_notifications[] = {
    909   {IDC_DUPLICATE_TAB, chrome::NOTIFICATION_TAB_PARENTED},
    910 
    911   // Returns as soon as the restored tab is created. To further wait until
    912   // the content page is loaded, use WaitForTabToBeRestored.
    913   {IDC_RESTORE_TAB, chrome::NOTIFICATION_TAB_PARENTED},
    914 
    915   // For the following commands, we need to wait for a new tab to be created,
    916   // load to finish, and title to change.
    917   {IDC_MANAGE_EXTENSIONS, content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED},
    918   {IDC_OPTIONS, content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED},
    919   {IDC_PRINT, content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED},
    920   {IDC_SHOW_DOWNLOADS, content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED},
    921   {IDC_SHOW_HISTORY, content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED},
    922 };
    923 
    924 }  // namespace
    925 
    926 ExecuteBrowserCommandObserver::~ExecuteBrowserCommandObserver() {
    927 }
    928 
    929 // static
    930 bool ExecuteBrowserCommandObserver::CreateAndRegisterObserver(
    931     AutomationProvider* automation,
    932     Browser* browser,
    933     int command,
    934     IPC::Message* reply_message,
    935     bool use_json_interface) {
    936   bool result = true;
    937   switch (command) {
    938     case IDC_NEW_TAB: {
    939       new NewTabObserver(automation, reply_message, use_json_interface);
    940       break;
    941     }
    942     case IDC_NEW_WINDOW:
    943     case IDC_NEW_INCOGNITO_WINDOW: {
    944       BrowserOpenedNotificationObserver* observer =
    945           new BrowserOpenedNotificationObserver(automation, reply_message,
    946                                                 use_json_interface);
    947       observer->set_for_browser_command(true);
    948       break;
    949     }
    950     case IDC_CLOSE_WINDOW: {
    951       BrowserClosedNotificationObserver* observer =
    952           new BrowserClosedNotificationObserver(browser, automation,
    953                                                 reply_message,
    954                                                 use_json_interface);
    955       observer->set_for_browser_command(true);
    956       break;
    957     }
    958     case IDC_CLOSE_TAB: {
    959       TabClosedNotificationObserver* observer =
    960           new TabClosedNotificationObserver(automation, true, reply_message,
    961                                             use_json_interface);
    962       observer->set_for_browser_command(true);
    963       break;
    964     }
    965     case IDC_BACK:
    966     case IDC_FORWARD:
    967     case IDC_RELOAD: {
    968       new NavigationNotificationObserver(
    969           &browser->tab_strip_model()->GetActiveWebContents()->GetController(),
    970           automation, reply_message, 1, false, use_json_interface);
    971       break;
    972     }
    973     default: {
    974       ExecuteBrowserCommandObserver* observer =
    975           new ExecuteBrowserCommandObserver(automation, reply_message,
    976                                             use_json_interface);
    977       if (!observer->Register(command)) {
    978         observer->ReleaseReply();
    979         delete observer;
    980         result = false;
    981       }
    982       break;
    983     }
    984   }
    985   return result;
    986 }
    987 
    988 void ExecuteBrowserCommandObserver::Observe(
    989     int type, const content::NotificationSource& source,
    990     const content::NotificationDetails& details) {
    991   if (type == notification_type_) {
    992     if (automation_.get()) {
    993       if (use_json_interface_) {
    994         AutomationJSONReply(automation_.get(), reply_message_.release())
    995             .SendSuccess(NULL);
    996       } else {
    997         AutomationMsg_WindowExecuteCommand::WriteReplyParams(
    998             reply_message_.get(), true);
    999         automation_->Send(reply_message_.release());
   1000       }
   1001     }
   1002     delete this;
   1003   } else {
   1004     NOTREACHED();
   1005   }
   1006 }
   1007 
   1008 ExecuteBrowserCommandObserver::ExecuteBrowserCommandObserver(
   1009     AutomationProvider* automation,
   1010     IPC::Message* reply_message,
   1011     bool use_json_interface)
   1012     : automation_(automation->AsWeakPtr()),
   1013       notification_type_(content::NOTIFICATION_ALL),
   1014       reply_message_(reply_message),
   1015       use_json_interface_(use_json_interface) {
   1016 }
   1017 
   1018 bool ExecuteBrowserCommandObserver::Register(int command) {
   1019   if (!Getint(command, &notification_type_))
   1020     return false;
   1021   registrar_.Add(this, notification_type_,
   1022                  content::NotificationService::AllSources());
   1023   return true;
   1024 }
   1025 
   1026 bool ExecuteBrowserCommandObserver::Getint(
   1027     int command, int* type) {
   1028   if (!type)
   1029     return false;
   1030   bool found = false;
   1031   for (unsigned int i = 0; i < arraysize(command_notifications); i++) {
   1032     if (command_notifications[i].command == command) {
   1033       *type = command_notifications[i].notification_type;
   1034       found = true;
   1035       break;
   1036     }
   1037   }
   1038   return found;
   1039 }
   1040 
   1041 IPC::Message* ExecuteBrowserCommandObserver::ReleaseReply() {
   1042   return reply_message_.release();
   1043 }
   1044 
   1045 FindInPageNotificationObserver::FindInPageNotificationObserver(
   1046     AutomationProvider* automation, WebContents* parent_tab,
   1047     bool reply_with_json, IPC::Message* reply_message)
   1048     : automation_(automation->AsWeakPtr()),
   1049       active_match_ordinal_(-1),
   1050       reply_with_json_(reply_with_json),
   1051       reply_message_(reply_message) {
   1052   registrar_.Add(this, chrome::NOTIFICATION_FIND_RESULT_AVAILABLE,
   1053                  content::Source<WebContents>(parent_tab));
   1054 }
   1055 
   1056 FindInPageNotificationObserver::~FindInPageNotificationObserver() {
   1057 }
   1058 
   1059 void FindInPageNotificationObserver::Observe(
   1060     int type, const content::NotificationSource& source,
   1061     const content::NotificationDetails& details) {
   1062   content::Details<FindNotificationDetails> find_details(details);
   1063   if (!(find_details->final_update() && reply_message_ != NULL)) {
   1064     DVLOG(1) << "Ignoring, since we only care about the final message";
   1065     return;
   1066   }
   1067 
   1068   if (!automation_.get()) {
   1069     delete this;
   1070     return;
   1071   }
   1072 
   1073   // We get multiple responses and one of those will contain the ordinal.
   1074   // This message comes to us before the final update is sent.
   1075   if (find_details->request_id() == kFindInPageRequestId) {
   1076     if (reply_with_json_) {
   1077       scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
   1078       return_value->SetInteger("match_count",
   1079           find_details->number_of_matches());
   1080       gfx::Rect rect = find_details->selection_rect();
   1081       // If MatchCount is > 0, then rect should not be Empty.
   1082       // We dont guard it here because we want to let the test
   1083       // code catch this invalid case if needed.
   1084       if (!rect.IsEmpty()) {
   1085         return_value->SetInteger("match_left", rect.x());
   1086         return_value->SetInteger("match_top", rect.y());
   1087         return_value->SetInteger("match_right", rect.right());
   1088         return_value->SetInteger("match_bottom", rect.bottom());
   1089       }
   1090       AutomationJSONReply(automation_.get(), reply_message_.release())
   1091           .SendSuccess(return_value.get());
   1092       delete this;
   1093     } else {
   1094       if (find_details->active_match_ordinal() > -1) {
   1095         active_match_ordinal_ = find_details->active_match_ordinal();
   1096         AutomationMsg_Find::WriteReplyParams(reply_message_.get(),
   1097             active_match_ordinal_, find_details->number_of_matches());
   1098         automation_->Send(reply_message_.release());
   1099       }
   1100     }
   1101   }
   1102 }
   1103 
   1104 // static
   1105 const int FindInPageNotificationObserver::kFindInPageRequestId = -1;
   1106 
   1107 DomOperationObserver::DomOperationObserver(int automation_id)
   1108     : automation_id_(automation_id) {
   1109   registrar_.Add(this, content::NOTIFICATION_DOM_OPERATION_RESPONSE,
   1110                  content::NotificationService::AllSources());
   1111   registrar_.Add(this, chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN,
   1112                  content::NotificationService::AllSources());
   1113   registrar_.Add(this, chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
   1114                  content::NotificationService::AllSources());
   1115 }
   1116 
   1117 DomOperationObserver::~DomOperationObserver() {}
   1118 
   1119 void DomOperationObserver::Observe(
   1120     int type, const content::NotificationSource& source,
   1121     const content::NotificationDetails& details) {
   1122   if (type == content::NOTIFICATION_DOM_OPERATION_RESPONSE) {
   1123     content::Details<DomOperationNotificationDetails> dom_op_details(details);
   1124     if (dom_op_details->automation_id == automation_id_)
   1125       OnDomOperationCompleted(dom_op_details->json);
   1126   } else if (type == chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN) {
   1127     OnJavascriptBlocked();
   1128   } else {
   1129     DCHECK_EQ(chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED, type);
   1130     WebContents* web_contents = content::Source<WebContents>(source).ptr();
   1131     if (web_contents) {
   1132       TabSpecificContentSettings* tab_content_settings =
   1133           TabSpecificContentSettings::FromWebContents(web_contents);
   1134       if (tab_content_settings &&
   1135           tab_content_settings->IsContentBlocked(
   1136               CONTENT_SETTINGS_TYPE_JAVASCRIPT))
   1137         OnJavascriptBlocked();
   1138     }
   1139   }
   1140 }
   1141 
   1142 DomOperationMessageSender::DomOperationMessageSender(
   1143     AutomationProvider* automation,
   1144     IPC::Message* reply_message,
   1145     bool use_json_interface)
   1146     : DomOperationObserver(0),
   1147       automation_(automation->AsWeakPtr()),
   1148       reply_message_(reply_message),
   1149       use_json_interface_(use_json_interface) {
   1150 }
   1151 
   1152 DomOperationMessageSender::~DomOperationMessageSender() {}
   1153 
   1154 void DomOperationMessageSender::OnDomOperationCompleted(
   1155     const std::string& json) {
   1156   if (automation_.get()) {
   1157     if (use_json_interface_) {
   1158       DictionaryValue dict;
   1159       dict.SetString("result", json);
   1160       AutomationJSONReply(automation_.get(), reply_message_.release())
   1161           .SendSuccess(&dict);
   1162     } else {
   1163       AutomationMsg_DomOperation::WriteReplyParams(reply_message_.get(), json);
   1164       automation_->Send(reply_message_.release());
   1165     }
   1166   }
   1167   delete this;
   1168 }
   1169 
   1170 void DomOperationMessageSender::OnJavascriptBlocked() {
   1171   if (automation_.get() && use_json_interface_) {
   1172     AutomationJSONReply(automation_.get(), reply_message_.release())
   1173         .SendError("Javascript execution was blocked");
   1174     delete this;
   1175   }
   1176 }
   1177 
   1178 MetricEventDurationObserver::MetricEventDurationObserver() {
   1179   registrar_.Add(this, chrome::NOTIFICATION_METRIC_EVENT_DURATION,
   1180                  content::NotificationService::AllSources());
   1181 }
   1182 
   1183 MetricEventDurationObserver::~MetricEventDurationObserver() {}
   1184 
   1185 int MetricEventDurationObserver::GetEventDurationMs(
   1186     const std::string& event_name) {
   1187   EventDurationMap::const_iterator it = durations_.find(event_name);
   1188   if (it == durations_.end())
   1189     return -1;
   1190   return it->second;
   1191 }
   1192 
   1193 void MetricEventDurationObserver::Observe(
   1194     int type,
   1195     const content::NotificationSource& source,
   1196     const content::NotificationDetails& details) {
   1197   if (type != chrome::NOTIFICATION_METRIC_EVENT_DURATION) {
   1198     NOTREACHED();
   1199     return;
   1200   }
   1201   MetricEventDurationDetails* metric_event_duration =
   1202       content::Details<MetricEventDurationDetails>(details).ptr();
   1203   durations_[metric_event_duration->event_name] =
   1204       metric_event_duration->duration_ms;
   1205 }
   1206 
   1207 InfoBarCountObserver::InfoBarCountObserver(AutomationProvider* automation,
   1208                                            IPC::Message* reply_message,
   1209                                            WebContents* web_contents,
   1210                                            size_t target_count)
   1211     : automation_(automation->AsWeakPtr()),
   1212       reply_message_(reply_message),
   1213       web_contents_(web_contents),
   1214       target_count_(target_count) {
   1215   content::Source<InfoBarService> source(
   1216       InfoBarService::FromWebContents(web_contents));
   1217   registrar_.Add(this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED,
   1218                  source);
   1219   registrar_.Add(this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED,
   1220                  source);
   1221   CheckCount();
   1222 }
   1223 
   1224 InfoBarCountObserver::~InfoBarCountObserver() {}
   1225 
   1226 void InfoBarCountObserver::Observe(
   1227     int type,
   1228     const content::NotificationSource& source,
   1229     const content::NotificationDetails& details) {
   1230   DCHECK(type == chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED ||
   1231          type == chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED);
   1232   CheckCount();
   1233 }
   1234 
   1235 void InfoBarCountObserver::CheckCount() {
   1236   if (InfoBarService::FromWebContents(web_contents_)->infobar_count() !=
   1237       target_count_)
   1238     return;
   1239 
   1240   if (automation_.get()) {
   1241     AutomationMsg_WaitForInfoBarCount::WriteReplyParams(reply_message_.get(),
   1242                                                         true);
   1243     automation_->Send(reply_message_.release());
   1244   }
   1245   delete this;
   1246 }
   1247 
   1248 AutomationProviderBookmarkModelObserver::
   1249 AutomationProviderBookmarkModelObserver(
   1250     AutomationProvider* provider,
   1251     IPC::Message* reply_message,
   1252     BookmarkModel* model,
   1253     bool use_json_interface)
   1254     : automation_provider_(provider->AsWeakPtr()),
   1255       reply_message_(reply_message),
   1256       model_(model),
   1257       use_json_interface_(use_json_interface) {
   1258   model_->AddObserver(this);
   1259 }
   1260 
   1261 AutomationProviderBookmarkModelObserver::
   1262     ~AutomationProviderBookmarkModelObserver() {
   1263   model_->RemoveObserver(this);
   1264 }
   1265 
   1266 void AutomationProviderBookmarkModelObserver::Loaded(BookmarkModel* model,
   1267                                                      bool ids_reassigned) {
   1268   ReplyAndDelete(true);
   1269 }
   1270 
   1271 void AutomationProviderBookmarkModelObserver::BookmarkModelBeingDeleted(
   1272     BookmarkModel* model) {
   1273   ReplyAndDelete(false);
   1274 }
   1275 
   1276 IPC::Message* AutomationProviderBookmarkModelObserver::ReleaseReply() {
   1277   return reply_message_.release();
   1278 }
   1279 
   1280 void AutomationProviderBookmarkModelObserver::ReplyAndDelete(bool success) {
   1281   if (automation_provider_.get()) {
   1282     if (use_json_interface_) {
   1283       AutomationJSONReply(automation_provider_.get(), reply_message_.release())
   1284           .SendSuccess(NULL);
   1285     } else {
   1286       AutomationMsg_WaitForBookmarkModelToLoad::WriteReplyParams(
   1287           reply_message_.get(), success);
   1288       automation_provider_->Send(reply_message_.release());
   1289     }
   1290   }
   1291   delete this;
   1292 }
   1293 
   1294 AutomationProviderDownloadUpdatedObserver::
   1295 AutomationProviderDownloadUpdatedObserver(
   1296     AutomationProvider* provider,
   1297     IPC::Message* reply_message,
   1298     bool wait_for_open,
   1299     bool incognito)
   1300     : provider_(provider->AsWeakPtr()),
   1301       reply_message_(reply_message),
   1302       wait_for_open_(wait_for_open),
   1303       incognito_(incognito) {
   1304 }
   1305 
   1306 AutomationProviderDownloadUpdatedObserver::
   1307     ~AutomationProviderDownloadUpdatedObserver() {}
   1308 
   1309 void AutomationProviderDownloadUpdatedObserver::OnDownloadUpdated(
   1310     DownloadItem* download) {
   1311   // If this observer is watching for open, only send the reply if the download
   1312   // has been auto-opened.
   1313   if (wait_for_open_ && !download->GetAutoOpened())
   1314     return;
   1315 
   1316   download->RemoveObserver(this);
   1317 
   1318   if (provider_.get()) {
   1319     scoped_ptr<DictionaryValue> return_value(
   1320         provider_->GetDictionaryFromDownloadItem(download, incognito_));
   1321     AutomationJSONReply(provider_.get(), reply_message_.release())
   1322         .SendSuccess(return_value.get());
   1323   }
   1324   delete this;
   1325 }
   1326 
   1327 void AutomationProviderDownloadUpdatedObserver::OnDownloadOpened(
   1328     DownloadItem* download) {
   1329   download->RemoveObserver(this);
   1330 
   1331   if (provider_.get()) {
   1332     scoped_ptr<DictionaryValue> return_value(
   1333         provider_->GetDictionaryFromDownloadItem(download, incognito_));
   1334     AutomationJSONReply(provider_.get(), reply_message_.release())
   1335         .SendSuccess(return_value.get());
   1336   }
   1337   delete this;
   1338 }
   1339 
   1340 AutomationProviderDownloadModelChangedObserver::
   1341 AutomationProviderDownloadModelChangedObserver(
   1342     AutomationProvider* provider,
   1343     IPC::Message* reply_message,
   1344     DownloadManager* download_manager)
   1345     : provider_(provider->AsWeakPtr()),
   1346       reply_message_(reply_message),
   1347       notifier_(download_manager, this) {
   1348 }
   1349 
   1350 AutomationProviderDownloadModelChangedObserver::
   1351     ~AutomationProviderDownloadModelChangedObserver() {}
   1352 
   1353 void AutomationProviderDownloadModelChangedObserver::ModelChanged() {
   1354   if (provider_.get())
   1355     AutomationJSONReply(provider_.get(), reply_message_.release())
   1356         .SendSuccess(NULL);
   1357   delete this;
   1358 }
   1359 
   1360 void AutomationProviderDownloadModelChangedObserver::OnDownloadCreated(
   1361     DownloadManager* manager, DownloadItem* item) {
   1362   ModelChanged();
   1363 }
   1364 
   1365 void AutomationProviderDownloadModelChangedObserver::OnDownloadRemoved(
   1366     DownloadManager* manager, DownloadItem* item) {
   1367   ModelChanged();
   1368 }
   1369 
   1370 AllDownloadsCompleteObserver::AllDownloadsCompleteObserver(
   1371     AutomationProvider* provider,
   1372     IPC::Message* reply_message,
   1373     DownloadManager* download_manager,
   1374     ListValue* pre_download_ids)
   1375     : provider_(provider->AsWeakPtr()),
   1376       reply_message_(reply_message),
   1377       download_manager_(download_manager) {
   1378   for (ListValue::iterator it = pre_download_ids->begin();
   1379        it != pre_download_ids->end(); ++it) {
   1380     int val = 0;
   1381     if ((*it)->GetAsInteger(&val)) {
   1382       pre_download_ids_.insert(val);
   1383     } else {
   1384       AutomationJSONReply(provider_.get(), reply_message_.release())
   1385           .SendError("Cannot convert ID of prior download to integer.");
   1386       delete this;
   1387       return;
   1388     }
   1389   }
   1390   download_manager_->AddObserver(this);
   1391   DownloadManager::DownloadVector all_items;
   1392   download_manager->GetAllDownloads(&all_items);
   1393   for (DownloadManager::DownloadVector::const_iterator
   1394        it = all_items.begin(); it != all_items.end(); ++it) {
   1395     OnDownloadCreated(download_manager_, *it);
   1396   }
   1397   ReplyIfNecessary();
   1398 }
   1399 
   1400 AllDownloadsCompleteObserver::~AllDownloadsCompleteObserver() {
   1401   if (download_manager_) {
   1402     download_manager_->RemoveObserver(this);
   1403     download_manager_ = NULL;
   1404   }
   1405   for (std::set<DownloadItem*>::const_iterator it = pending_downloads_.begin();
   1406        it != pending_downloads_.end(); ++it) {
   1407     (*it)->RemoveObserver(this);
   1408   }
   1409   pending_downloads_.clear();
   1410 }
   1411 
   1412 void AllDownloadsCompleteObserver::ManagerGoingDown(DownloadManager* manager) {
   1413   DCHECK_EQ(manager, download_manager_);
   1414   download_manager_->RemoveObserver(this);
   1415   download_manager_ = NULL;
   1416 }
   1417 
   1418 void AllDownloadsCompleteObserver::OnDownloadCreated(
   1419     DownloadManager* manager, DownloadItem* item) {
   1420   // This method is also called in the c-tor for previously existing items.
   1421   if (pre_download_ids_.find(item->GetId()) == pre_download_ids_.end() &&
   1422       item->GetState() == DownloadItem::IN_PROGRESS) {
   1423     item->AddObserver(this);
   1424     pending_downloads_.insert(item);
   1425   }
   1426 }
   1427 
   1428 void AllDownloadsCompleteObserver::OnDownloadUpdated(DownloadItem* download) {
   1429   // If the current download's status has changed to a final state (not state
   1430   // "in progress"), remove it from the pending list.
   1431   if (download->GetState() != DownloadItem::IN_PROGRESS) {
   1432     download->RemoveObserver(this);
   1433     pending_downloads_.erase(download);
   1434     ReplyIfNecessary();
   1435   }
   1436 }
   1437 
   1438 void AllDownloadsCompleteObserver::ReplyIfNecessary() {
   1439   if (!pending_downloads_.empty())
   1440     return;
   1441 
   1442   download_manager_->RemoveObserver(this);
   1443   if (provider_.get())
   1444     AutomationJSONReply(provider_.get(), reply_message_.release())
   1445         .SendSuccess(NULL);
   1446   delete this;
   1447 }
   1448 
   1449 AutomationProviderSearchEngineObserver::AutomationProviderSearchEngineObserver(
   1450     AutomationProvider* provider,
   1451     Profile* profile,
   1452     IPC::Message* reply_message)
   1453     : provider_(provider->AsWeakPtr()),
   1454       profile_(profile),
   1455       reply_message_(reply_message) {
   1456 }
   1457 
   1458 AutomationProviderSearchEngineObserver::
   1459     ~AutomationProviderSearchEngineObserver() {}
   1460 
   1461 void AutomationProviderSearchEngineObserver::OnTemplateURLServiceChanged() {
   1462   if (provider_.get()) {
   1463     TemplateURLService* url_service =
   1464         TemplateURLServiceFactory::GetForProfile(profile_);
   1465     url_service->RemoveObserver(this);
   1466     AutomationJSONReply(provider_.get(), reply_message_.release())
   1467         .SendSuccess(NULL);
   1468   }
   1469   delete this;
   1470 }
   1471 
   1472 AutomationProviderHistoryObserver::AutomationProviderHistoryObserver(
   1473     AutomationProvider* provider,
   1474     IPC::Message* reply_message)
   1475     : provider_(provider->AsWeakPtr()),
   1476       reply_message_(reply_message) {
   1477 }
   1478 
   1479 AutomationProviderHistoryObserver::~AutomationProviderHistoryObserver() {}
   1480 
   1481 void AutomationProviderHistoryObserver::HistoryQueryComplete(
   1482     HistoryService::Handle request_handle,
   1483     history::QueryResults* results) {
   1484   if (!provider_.get()) {
   1485     delete this;
   1486     return;
   1487   }
   1488 
   1489   scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
   1490 
   1491   ListValue* history_list = new ListValue;
   1492   for (size_t i = 0; i < results->size(); ++i) {
   1493     DictionaryValue* page_value = new DictionaryValue;
   1494     history::URLResult const &page = (*results)[i];
   1495     page_value->SetString("title", page.title());
   1496     page_value->SetString("url", page.url().spec());
   1497     page_value->SetDouble("time",
   1498                           static_cast<double>(page.visit_time().ToDoubleT()));
   1499     page_value->SetString("snippet", page.snippet().text());
   1500     page_value->SetBoolean(
   1501         "starred",
   1502         BookmarkModelFactory::GetForProfile(
   1503             provider_->profile())->IsBookmarked(page.url()));
   1504     history_list->Append(page_value);
   1505   }
   1506 
   1507   return_value->Set("history", history_list);
   1508   // Return history info.
   1509   AutomationJSONReply reply(provider_.get(), reply_message_.release());
   1510   reply.SendSuccess(return_value.get());
   1511   delete this;
   1512 }
   1513 
   1514 AutomationProviderImportSettingsObserver::
   1515 AutomationProviderImportSettingsObserver(
   1516     AutomationProvider* provider,
   1517     IPC::Message* reply_message)
   1518     : provider_(provider->AsWeakPtr()),
   1519       reply_message_(reply_message) {
   1520 }
   1521 
   1522 AutomationProviderImportSettingsObserver::
   1523     ~AutomationProviderImportSettingsObserver() {}
   1524 
   1525 void AutomationProviderImportSettingsObserver::ImportStarted() {
   1526 }
   1527 
   1528 void AutomationProviderImportSettingsObserver::ImportItemStarted(
   1529     importer::ImportItem item) {
   1530 }
   1531 
   1532 void AutomationProviderImportSettingsObserver::ImportItemEnded(
   1533     importer::ImportItem item) {
   1534 }
   1535 
   1536 void AutomationProviderImportSettingsObserver::ImportEnded() {
   1537   if (provider_.get())
   1538     AutomationJSONReply(provider_.get(), reply_message_.release())
   1539         .SendSuccess(NULL);
   1540   delete this;
   1541 }
   1542 
   1543 AutomationProviderGetPasswordsObserver::AutomationProviderGetPasswordsObserver(
   1544     AutomationProvider* provider,
   1545     IPC::Message* reply_message)
   1546     : provider_(provider->AsWeakPtr()),
   1547       reply_message_(reply_message) {
   1548 }
   1549 
   1550 AutomationProviderGetPasswordsObserver::
   1551     ~AutomationProviderGetPasswordsObserver() {}
   1552 
   1553 void AutomationProviderGetPasswordsObserver::OnPasswordStoreRequestDone(
   1554     CancelableRequestProvider::Handle handle,
   1555     const std::vector<autofill::PasswordForm*>& result) {
   1556   if (!provider_.get()) {
   1557     delete this;
   1558     return;
   1559   }
   1560 
   1561   scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
   1562 
   1563   ListValue* passwords = new ListValue;
   1564   for (std::vector<autofill::PasswordForm*>::const_iterator it =
   1565            result.begin(); it != result.end(); ++it) {
   1566     DictionaryValue* password_val = new DictionaryValue;
   1567     autofill::PasswordForm* password_form = *it;
   1568     password_val->SetString("username_value", password_form->username_value);
   1569     password_val->SetString("password_value", password_form->password_value);
   1570     password_val->SetString("signon_realm", password_form->signon_realm);
   1571     password_val->SetDouble(
   1572         "time", static_cast<double>(password_form->date_created.ToDoubleT()));
   1573     password_val->SetString("origin_url", password_form->origin.spec());
   1574     password_val->SetString("username_element",
   1575                             password_form->username_element);
   1576     password_val->SetString("password_element",
   1577                             password_form->password_element);
   1578     password_val->SetString("submit_element", password_form->submit_element);
   1579     password_val->SetString("action_target", password_form->action.spec());
   1580     password_val->SetBoolean("blacklist", password_form->blacklisted_by_user);
   1581     passwords->Append(password_val);
   1582   }
   1583 
   1584   return_value->Set("passwords", passwords);
   1585   AutomationJSONReply(provider_.get(), reply_message_.release())
   1586       .SendSuccess(return_value.get());
   1587   delete this;
   1588 }
   1589 
   1590 void AutomationProviderGetPasswordsObserver::OnGetPasswordStoreResults(
   1591     const std::vector<autofill::PasswordForm*>& results) {
   1592   // TODO(kaiwang): Implement when I refactor
   1593   // PasswordManager::GetAutofillableLogins.
   1594   NOTIMPLEMENTED();
   1595 }
   1596 
   1597 PasswordStoreLoginsChangedObserver::PasswordStoreLoginsChangedObserver(
   1598     AutomationProvider* automation,
   1599     IPC::Message* reply_message,
   1600     PasswordStoreChange::Type expected_type,
   1601     const std::string& result_key)
   1602     : automation_(automation->AsWeakPtr()),
   1603       reply_message_(reply_message),
   1604       expected_type_(expected_type),
   1605       result_key_(result_key),
   1606       done_event_(false, false) {
   1607   AddRef();
   1608 }
   1609 
   1610 PasswordStoreLoginsChangedObserver::~PasswordStoreLoginsChangedObserver() {
   1611   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   1612 }
   1613 
   1614 void PasswordStoreLoginsChangedObserver::Init() {
   1615   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   1616   BrowserThread::PostTask(
   1617       BrowserThread::DB,
   1618       FROM_HERE,
   1619       base::Bind(&PasswordStoreLoginsChangedObserver::RegisterObserversTask,
   1620                  this));
   1621   done_event_.Wait();
   1622 }
   1623 
   1624 void PasswordStoreLoginsChangedObserver::RegisterObserversTask() {
   1625   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
   1626   registrar_.reset(new content::NotificationRegistrar);
   1627   registrar_->Add(this, chrome::NOTIFICATION_LOGINS_CHANGED,
   1628                   content::NotificationService::AllSources());
   1629   done_event_.Signal();
   1630 }
   1631 
   1632 void PasswordStoreLoginsChangedObserver::Observe(
   1633     int type,
   1634     const content::NotificationSource& source,
   1635     const content::NotificationDetails& details) {
   1636   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
   1637   DCHECK(type == chrome::NOTIFICATION_LOGINS_CHANGED);
   1638   registrar_.reset();  // Must be done from the DB thread.
   1639   PasswordStoreChangeList* change_details =
   1640       content::Details<PasswordStoreChangeList>(details).ptr();
   1641   if (change_details->size() != 1 ||
   1642       change_details->front().type() != expected_type_) {
   1643     // Notify the UI thread that there's an error.
   1644     std::string error = "Unexpected password store login change details.";
   1645     BrowserThread::PostTask(
   1646         BrowserThread::UI,
   1647         FROM_HERE,
   1648         base::Bind(&PasswordStoreLoginsChangedObserver::IndicateError, this,
   1649                    error));
   1650     return;
   1651   }
   1652 
   1653   // Notify the UI thread that we're done listening.
   1654   BrowserThread::PostTask(
   1655       BrowserThread::UI,
   1656       FROM_HERE,
   1657       base::Bind(&PasswordStoreLoginsChangedObserver::IndicateDone, this));
   1658 }
   1659 
   1660 void PasswordStoreLoginsChangedObserver::IndicateDone() {
   1661   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   1662   if (automation_.get()) {
   1663     if (result_key_.empty()) {
   1664       AutomationJSONReply(automation_.get(), reply_message_.release())
   1665           .SendSuccess(NULL);
   1666     } else {
   1667       scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
   1668       return_value->SetBoolean(result_key_, true);
   1669       AutomationJSONReply(automation_.get(), reply_message_.release())
   1670           .SendSuccess(return_value.get());
   1671     }
   1672   }
   1673   Release();
   1674 }
   1675 
   1676 void PasswordStoreLoginsChangedObserver::IndicateError(
   1677     const std::string& error) {
   1678   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   1679   if (automation_.get())
   1680     AutomationJSONReply(automation_.get(), reply_message_.release())
   1681         .SendError(error);
   1682   Release();
   1683 }
   1684 
   1685 OmniboxAcceptNotificationObserver::OmniboxAcceptNotificationObserver(
   1686     NavigationController* controller,
   1687     AutomationProvider* automation,
   1688     IPC::Message* reply_message)
   1689     : automation_(automation->AsWeakPtr()),
   1690       reply_message_(reply_message),
   1691       controller_(controller) {
   1692   content::Source<NavigationController> source(controller_);
   1693   registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, source);
   1694   // Pages requiring auth don't send LOAD_STOP.
   1695   registrar_.Add(this, chrome::NOTIFICATION_AUTH_NEEDED, source);
   1696 }
   1697 
   1698 OmniboxAcceptNotificationObserver::~OmniboxAcceptNotificationObserver() {
   1699 }
   1700 
   1701 void OmniboxAcceptNotificationObserver::Observe(
   1702     int type,
   1703     const content::NotificationSource& source,
   1704     const content::NotificationDetails& details) {
   1705   if (type == content::NOTIFICATION_LOAD_STOP ||
   1706       type == chrome::NOTIFICATION_AUTH_NEEDED) {
   1707     if (automation_.get()) {
   1708       AutomationJSONReply(automation_.get(), reply_message_.release())
   1709           .SendSuccess(NULL);
   1710     }
   1711     delete this;
   1712   } else {
   1713     NOTREACHED();
   1714   }
   1715 }
   1716 
   1717 SavePackageNotificationObserver::SavePackageNotificationObserver(
   1718     content::DownloadManager* download_manager,
   1719     AutomationProvider* automation,
   1720     IPC::Message* reply_message)
   1721     : download_manager_(download_manager),
   1722       automation_(automation->AsWeakPtr()),
   1723       reply_message_(reply_message) {
   1724   download_manager_->AddObserver(this);
   1725 }
   1726 
   1727 SavePackageNotificationObserver::~SavePackageNotificationObserver() {
   1728   download_manager_->RemoveObserver(this);
   1729 }
   1730 
   1731 void SavePackageNotificationObserver::OnSavePackageSuccessfullyFinished(
   1732     content::DownloadManager* manager, content::DownloadItem* item) {
   1733   if (automation_.get()) {
   1734     AutomationJSONReply(automation_.get(), reply_message_.release())
   1735         .SendSuccess(NULL);
   1736   }
   1737   delete this;
   1738 }
   1739 
   1740 void SavePackageNotificationObserver::ManagerGoingDown(
   1741     content::DownloadManager* manager) {
   1742   delete this;
   1743 }
   1744 
   1745 namespace {
   1746 
   1747 // Returns a vector of dictionaries containing information about installed apps,
   1748 // as identified from a given list of extensions.  The caller takes ownership
   1749 // of the created vector.
   1750 std::vector<DictionaryValue*>* GetAppInfoFromExtensions(
   1751     const ExtensionSet* extensions,
   1752     ExtensionService* ext_service) {
   1753   std::vector<DictionaryValue*>* apps_list =
   1754       new std::vector<DictionaryValue*>();
   1755   for (ExtensionSet::const_iterator ext = extensions->begin();
   1756        ext != extensions->end(); ++ext) {
   1757     // Only return information about extensions that are actually apps.
   1758     if ((*ext)->is_app()) {
   1759       DictionaryValue* app_info = new DictionaryValue();
   1760       AppLauncherHandler::CreateAppInfo(ext->get(), ext_service, app_info);
   1761       app_info->SetBoolean(
   1762           "is_component_extension",
   1763           (*ext)->location() == extensions::Manifest::COMPONENT);
   1764 
   1765       // Convert the launch_type integer into a more descriptive string.
   1766       int launch_type;
   1767       const char* kLaunchType = "launch_type";
   1768       if (!app_info->GetInteger(kLaunchType, &launch_type)) {
   1769         NOTREACHED() << "Can't get integer from key " << kLaunchType;
   1770         continue;
   1771       }
   1772       if (launch_type == extensions::LAUNCH_TYPE_PINNED) {
   1773         app_info->SetString(kLaunchType, "pinned");
   1774       } else if (launch_type == extensions::LAUNCH_TYPE_REGULAR) {
   1775         app_info->SetString(kLaunchType, "regular");
   1776       } else if (launch_type == extensions::LAUNCH_TYPE_FULLSCREEN) {
   1777         app_info->SetString(kLaunchType, "fullscreen");
   1778       } else if (launch_type == extensions::LAUNCH_TYPE_WINDOW) {
   1779         app_info->SetString(kLaunchType, "window");
   1780       } else {
   1781         app_info->SetString(kLaunchType, "unknown");
   1782       }
   1783 
   1784       apps_list->push_back(app_info);
   1785     }
   1786   }
   1787   return apps_list;
   1788 }
   1789 
   1790 }  // namespace
   1791 
   1792 NTPInfoObserver::NTPInfoObserver(AutomationProvider* automation,
   1793                                  IPC::Message* reply_message)
   1794     : automation_(automation->AsWeakPtr()),
   1795       reply_message_(reply_message),
   1796       request_(0),
   1797       ntp_info_(new DictionaryValue) {
   1798   top_sites_ = automation_->profile()->GetTopSites();
   1799   if (!top_sites_) {
   1800     AutomationJSONReply(automation_.get(), reply_message_.release())
   1801         .SendError("Profile does not have service for querying the top sites.");
   1802     return;
   1803   }
   1804   TabRestoreService* service =
   1805       TabRestoreServiceFactory::GetForProfile(automation_->profile());
   1806   if (!service) {
   1807     AutomationJSONReply(automation_.get(), reply_message_.release())
   1808         .SendError("No TabRestoreService.");
   1809     return;
   1810   }
   1811 
   1812   // Collect information about the apps in the new tab page.
   1813   ExtensionService* ext_service = extensions::ExtensionSystem::Get(
   1814       automation_->profile())->extension_service();
   1815   if (!ext_service) {
   1816     AutomationJSONReply(automation_.get(), reply_message_.release())
   1817         .SendError("No ExtensionService.");
   1818     return;
   1819   }
   1820   // Process enabled extensions.
   1821   ListValue* apps_list = new ListValue();
   1822   const ExtensionSet* extensions = ext_service->extensions();
   1823   std::vector<DictionaryValue*>* enabled_apps = GetAppInfoFromExtensions(
   1824       extensions, ext_service);
   1825   for (std::vector<DictionaryValue*>::const_iterator app =
   1826        enabled_apps->begin(); app != enabled_apps->end(); ++app) {
   1827     (*app)->SetBoolean("is_disabled", false);
   1828     apps_list->Append(*app);
   1829   }
   1830   delete enabled_apps;
   1831   // Process disabled extensions.
   1832   const ExtensionSet* disabled_extensions = ext_service->disabled_extensions();
   1833   std::vector<DictionaryValue*>* disabled_apps = GetAppInfoFromExtensions(
   1834       disabled_extensions, ext_service);
   1835   for (std::vector<DictionaryValue*>::const_iterator app =
   1836        disabled_apps->begin(); app != disabled_apps->end(); ++app) {
   1837     (*app)->SetBoolean("is_disabled", true);
   1838     apps_list->Append(*app);
   1839   }
   1840   delete disabled_apps;
   1841   // Process terminated extensions.
   1842   const ExtensionSet* terminated_extensions =
   1843       ext_service->terminated_extensions();
   1844   std::vector<DictionaryValue*>* terminated_apps = GetAppInfoFromExtensions(
   1845       terminated_extensions, ext_service);
   1846   for (std::vector<DictionaryValue*>::const_iterator app =
   1847        terminated_apps->begin(); app != terminated_apps->end(); ++app) {
   1848     (*app)->SetBoolean("is_disabled", true);
   1849     apps_list->Append(*app);
   1850   }
   1851   delete terminated_apps;
   1852   ntp_info_->Set("apps", apps_list);
   1853 
   1854   // Get the info that would be displayed in the recently closed section.
   1855   ListValue* recently_closed_list = new ListValue;
   1856   RecentlyClosedTabsHandler::CreateRecentlyClosedValues(service->entries(),
   1857                                                         recently_closed_list);
   1858   ntp_info_->Set("recently_closed", recently_closed_list);
   1859 
   1860   // Add default site URLs.
   1861   ListValue* default_sites_list = new ListValue;
   1862   history::MostVisitedURLList urls = top_sites_->GetPrepopulatePages();
   1863   for (size_t i = 0; i < urls.size(); ++i) {
   1864     default_sites_list->Append(Value::CreateStringValue(
   1865         urls[i].url.possibly_invalid_spec()));
   1866   }
   1867   ntp_info_->Set("default_sites", default_sites_list);
   1868 
   1869   registrar_.Add(this, chrome::NOTIFICATION_TOP_SITES_UPDATED,
   1870                  content::Source<history::TopSites>(top_sites_));
   1871   if (top_sites_->loaded()) {
   1872     OnTopSitesLoaded();
   1873   } else {
   1874     registrar_.Add(this, chrome::NOTIFICATION_TOP_SITES_LOADED,
   1875                    content::Source<Profile>(automation_->profile()));
   1876   }
   1877 }
   1878 
   1879 NTPInfoObserver::~NTPInfoObserver() {}
   1880 
   1881 void NTPInfoObserver::Observe(int type,
   1882                               const content::NotificationSource& source,
   1883                               const content::NotificationDetails& details) {
   1884   if (type == chrome::NOTIFICATION_TOP_SITES_LOADED) {
   1885     OnTopSitesLoaded();
   1886   } else if (type == chrome::NOTIFICATION_TOP_SITES_UPDATED) {
   1887     content::Details<CancelableRequestProvider::Handle> request_details(
   1888           details);
   1889     if (request_ == *request_details.ptr()) {
   1890       top_sites_->GetMostVisitedURLs(
   1891           base::Bind(&NTPInfoObserver::OnTopSitesReceived,
   1892                      base::Unretained(this)), false);
   1893     }
   1894   }
   1895 }
   1896 
   1897 void NTPInfoObserver::OnTopSitesLoaded() {
   1898   request_ = top_sites_->StartQueryForMostVisited();
   1899 }
   1900 
   1901 void NTPInfoObserver::OnTopSitesReceived(
   1902     const history::MostVisitedURLList& visited_list) {
   1903   if (!automation_.get()) {
   1904     delete this;
   1905     return;
   1906   }
   1907 
   1908   ListValue* list_value = new ListValue;
   1909   for (size_t i = 0; i < visited_list.size(); ++i) {
   1910     const history::MostVisitedURL& visited = visited_list[i];
   1911     if (visited.url.spec().empty())
   1912       break;  // This is the signal that there are no more real visited sites.
   1913     DictionaryValue* dict = new DictionaryValue;
   1914     dict->SetString("url", visited.url.spec());
   1915     dict->SetString("title", visited.title);
   1916     list_value->Append(dict);
   1917   }
   1918   ntp_info_->Set("most_visited", list_value);
   1919   AutomationJSONReply(automation_.get(), reply_message_.release())
   1920       .SendSuccess(ntp_info_.get());
   1921   delete this;
   1922 }
   1923 
   1924 AppLaunchObserver::AppLaunchObserver(
   1925     NavigationController* controller,
   1926     AutomationProvider* automation,
   1927     IPC::Message* reply_message,
   1928     extensions::LaunchContainer launch_container)
   1929     : controller_(controller),
   1930       automation_(automation->AsWeakPtr()),
   1931       reply_message_(reply_message),
   1932       launch_container_(launch_container),
   1933       new_window_id_(extension_misc::kUnknownWindowId) {
   1934   if (launch_container_ == extensions::LAUNCH_CONTAINER_TAB) {
   1935     // Need to wait for the currently-active tab to reload.
   1936     content::Source<NavigationController> source(controller_);
   1937     registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, source);
   1938   } else {
   1939     // Need to wait for a new tab in a new window to load.
   1940     registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
   1941                    content::NotificationService::AllSources());
   1942     registrar_.Add(this, chrome::NOTIFICATION_BROWSER_WINDOW_READY,
   1943                    content::NotificationService::AllSources());
   1944   }
   1945 }
   1946 
   1947 AppLaunchObserver::~AppLaunchObserver() {}
   1948 
   1949 void AppLaunchObserver::Observe(int type,
   1950                                 const content::NotificationSource& source,
   1951                                 const content::NotificationDetails& details) {
   1952   if (type == chrome::NOTIFICATION_BROWSER_WINDOW_READY) {
   1953     new_window_id_ = extensions::ExtensionTabUtil::GetWindowId(
   1954         content::Source<Browser>(source).ptr());
   1955     return;
   1956   }
   1957 
   1958   DCHECK_EQ(content::NOTIFICATION_LOAD_STOP, type);
   1959   SessionTabHelper* session_tab_helper = SessionTabHelper::FromWebContents(
   1960       content::Source<NavigationController>(source)->GetWebContents());
   1961   if ((launch_container_ == extensions::LAUNCH_CONTAINER_TAB) ||
   1962       (session_tab_helper &&
   1963           (session_tab_helper->window_id().id() == new_window_id_))) {
   1964     if (automation_.get()) {
   1965       AutomationJSONReply(automation_.get(), reply_message_.release())
   1966           .SendSuccess(NULL);
   1967     }
   1968     delete this;
   1969   }
   1970 }
   1971 
   1972 namespace {
   1973 
   1974 // Returns whether all active notifications have an associated process ID.
   1975 bool AreActiveNotificationProcessesReady() {
   1976   BalloonNotificationUIManager* manager =
   1977       BalloonNotificationUIManager::GetInstanceForTesting();
   1978   const BalloonCollection::Balloons& balloons =
   1979       manager->balloon_collection()->GetActiveBalloons();
   1980   BalloonCollection::Balloons::const_iterator iter;
   1981   for (iter = balloons.begin(); iter != balloons.end(); ++iter) {
   1982     BalloonHost* balloon_host = (*iter)->balloon_view()->GetHost();
   1983     if (balloon_host && !balloon_host->IsRenderViewReady())
   1984       return false;
   1985   }
   1986   return true;
   1987 }
   1988 
   1989 }  // namespace
   1990 
   1991 GetAllNotificationsObserver::GetAllNotificationsObserver(
   1992     AutomationProvider* automation,
   1993     IPC::Message* reply_message)
   1994     : automation_(automation->AsWeakPtr()),
   1995       reply_message_(reply_message) {
   1996   if (AreActiveNotificationProcessesReady()) {
   1997     SendMessage();
   1998   } else {
   1999     registrar_.Add(this, chrome::NOTIFICATION_NOTIFY_BALLOON_CONNECTED,
   2000                    content::NotificationService::AllSources());
   2001   }
   2002 }
   2003 
   2004 GetAllNotificationsObserver::~GetAllNotificationsObserver() {}
   2005 
   2006 void GetAllNotificationsObserver::Observe(
   2007     int type,
   2008     const content::NotificationSource& source,
   2009     const content::NotificationDetails& details) {
   2010   if (!automation_.get()) {
   2011     delete this;
   2012     return;
   2013   }
   2014   if (AreActiveNotificationProcessesReady())
   2015     SendMessage();
   2016 }
   2017 
   2018 base::DictionaryValue* GetAllNotificationsObserver::NotificationToJson(
   2019     const Notification* note) {
   2020   DictionaryValue* dict = new base::DictionaryValue();
   2021   dict->SetString("content_url", note->content_url().spec());
   2022   dict->SetString("origin_url", note->origin_url().spec());
   2023   dict->SetString("display_source", note->display_source());
   2024   dict->SetString("id", note->notification_id());
   2025   return dict;
   2026 }
   2027 
   2028 void GetAllNotificationsObserver::SendMessage() {
   2029   BalloonNotificationUIManager* manager =
   2030       BalloonNotificationUIManager::GetInstanceForTesting();
   2031   const BalloonCollection::Balloons& balloons =
   2032       manager->balloon_collection()->GetActiveBalloons();
   2033   DictionaryValue return_value;
   2034   ListValue* list = new ListValue;
   2035   return_value.Set("notifications", list);
   2036   BalloonCollection::Balloons::const_iterator balloon_iter;
   2037   for (balloon_iter = balloons.begin(); balloon_iter != balloons.end();
   2038        ++balloon_iter) {
   2039     base::DictionaryValue* note = NotificationToJson(
   2040         &(*balloon_iter)->notification());
   2041     BalloonHost* balloon_host = (*balloon_iter)->balloon_view()->GetHost();
   2042     if (balloon_host) {
   2043       int pid = base::GetProcId(balloon_host->web_contents()->
   2044                                 GetRenderViewHost()->GetProcess()->GetHandle());
   2045       note->SetInteger("pid", pid);
   2046     }
   2047     list->Append(note);
   2048   }
   2049   std::vector<const Notification*> queued_notes;
   2050   manager->GetQueuedNotificationsForTesting(&queued_notes);
   2051   std::vector<const Notification*>::const_iterator queued_iter;
   2052   for (queued_iter = queued_notes.begin(); queued_iter != queued_notes.end();
   2053        ++queued_iter) {
   2054     list->Append(NotificationToJson(*queued_iter));
   2055   }
   2056   AutomationJSONReply(automation_.get(), reply_message_.release())
   2057       .SendSuccess(&return_value);
   2058   delete this;
   2059 }
   2060 
   2061 NewNotificationBalloonObserver::NewNotificationBalloonObserver(
   2062     AutomationProvider* provider,
   2063     IPC::Message* reply_message)
   2064     : automation_(provider->AsWeakPtr()),
   2065       reply_message_(reply_message) {
   2066   registrar_.Add(this, chrome::NOTIFICATION_NOTIFY_BALLOON_CONNECTED,
   2067                  content::NotificationService::AllSources());
   2068 }
   2069 
   2070 NewNotificationBalloonObserver::~NewNotificationBalloonObserver() { }
   2071 
   2072 void NewNotificationBalloonObserver::Observe(
   2073     int type,
   2074     const content::NotificationSource& source,
   2075     const content::NotificationDetails& details) {
   2076   if (automation_.get()) {
   2077     AutomationJSONReply(automation_.get(), reply_message_.release())
   2078         .SendSuccess(NULL);
   2079   }
   2080   delete this;
   2081 }
   2082 
   2083 OnNotificationBalloonCountObserver::OnNotificationBalloonCountObserver(
   2084     AutomationProvider* provider,
   2085     IPC::Message* reply_message,
   2086     int count)
   2087     : automation_(provider->AsWeakPtr()),
   2088       reply_message_(reply_message),
   2089       collection_(BalloonNotificationUIManager::GetInstanceForTesting()->
   2090           balloon_collection()),
   2091       count_(count) {
   2092   registrar_.Add(this, chrome::NOTIFICATION_NOTIFY_BALLOON_CONNECTED,
   2093                  content::NotificationService::AllSources());
   2094   collection_->set_on_collection_changed_callback(
   2095       base::Bind(&OnNotificationBalloonCountObserver::CheckBalloonCount,
   2096                  base::Unretained(this)));
   2097   CheckBalloonCount();
   2098 }
   2099 
   2100 OnNotificationBalloonCountObserver::~OnNotificationBalloonCountObserver() {
   2101 }
   2102 
   2103 void OnNotificationBalloonCountObserver::Observe(
   2104     int type,
   2105     const content::NotificationSource& source,
   2106     const content::NotificationDetails& details) {
   2107   CheckBalloonCount();
   2108 }
   2109 
   2110 void OnNotificationBalloonCountObserver::CheckBalloonCount() {
   2111   bool balloon_count_met = AreActiveNotificationProcessesReady() &&
   2112       static_cast<int>(collection_->GetActiveBalloons().size()) == count_;
   2113 
   2114   if (balloon_count_met && automation_.get()) {
   2115     AutomationJSONReply(automation_.get(), reply_message_.release())
   2116         .SendSuccess(NULL);
   2117   }
   2118 
   2119   if (balloon_count_met || !automation_.get()) {
   2120     collection_->set_on_collection_changed_callback(base::Closure());
   2121     delete this;
   2122   }
   2123 }
   2124 
   2125 RendererProcessClosedObserver::RendererProcessClosedObserver(
   2126     AutomationProvider* automation,
   2127     IPC::Message* reply_message)
   2128     : automation_(automation->AsWeakPtr()),
   2129       reply_message_(reply_message) {
   2130   registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
   2131                  content::NotificationService::AllSources());
   2132 }
   2133 
   2134 RendererProcessClosedObserver::~RendererProcessClosedObserver() {}
   2135 
   2136 void RendererProcessClosedObserver::Observe(
   2137     int type,
   2138     const content::NotificationSource& source,
   2139     const content::NotificationDetails& details) {
   2140   if (automation_.get()) {
   2141     AutomationJSONReply(automation_.get(), reply_message_.release())
   2142         .SendSuccess(NULL);
   2143   }
   2144   delete this;
   2145 }
   2146 
   2147 InputEventAckNotificationObserver::InputEventAckNotificationObserver(
   2148     AutomationProvider* automation,
   2149     IPC::Message* reply_message,
   2150     int event_type,
   2151     int count)
   2152     : automation_(automation->AsWeakPtr()),
   2153       reply_message_(reply_message),
   2154       event_type_(event_type),
   2155       count_(count) {
   2156   DCHECK(1 <= count);
   2157   registrar_.Add(
   2158       this,
   2159       content::NOTIFICATION_RENDER_WIDGET_HOST_DID_RECEIVE_INPUT_EVENT_ACK,
   2160       content::NotificationService::AllSources());
   2161   registrar_.Add(
   2162       this,
   2163       chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN,
   2164       content::NotificationService::AllSources());
   2165 }
   2166 
   2167 InputEventAckNotificationObserver::~InputEventAckNotificationObserver() {}
   2168 
   2169 void InputEventAckNotificationObserver::Observe(
   2170     int type,
   2171     const content::NotificationSource& source,
   2172     const content::NotificationDetails& details) {
   2173   if (type == chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN) {
   2174     AutomationJSONReply(automation_.get(), reply_message_.release())
   2175         .SendSuccess(NULL);
   2176     delete this;
   2177     return;
   2178   }
   2179 
   2180   content::Details<int> request_details(details);
   2181   // If the event type matches for |count_| times, replies with a JSON message.
   2182   if (event_type_ == *request_details.ptr()) {
   2183     if (--count_ == 0 && automation_.get()) {
   2184       AutomationJSONReply(automation_.get(), reply_message_.release())
   2185           .SendSuccess(NULL);
   2186       delete this;
   2187     }
   2188   } else {
   2189     LOG(WARNING) << "Ignoring unexpected event type: "
   2190                  << *request_details.ptr() << " (expected: " << event_type_
   2191                  << ")";
   2192   }
   2193 }
   2194 
   2195 NewTabObserver::NewTabObserver(AutomationProvider* automation,
   2196                                IPC::Message* reply_message,
   2197                                bool use_json_interface)
   2198     : automation_(automation->AsWeakPtr()),
   2199       reply_message_(reply_message),
   2200       use_json_interface_(use_json_interface) {
   2201   // Use TAB_PARENTED to detect the new tab.
   2202   registrar_.Add(this,
   2203                  chrome::NOTIFICATION_TAB_PARENTED,
   2204                  content::NotificationService::AllSources());
   2205 }
   2206 
   2207 void NewTabObserver::Observe(int type,
   2208                              const content::NotificationSource& source,
   2209                              const content::NotificationDetails& details) {
   2210   DCHECK_EQ(chrome::NOTIFICATION_TAB_PARENTED, type);
   2211   NavigationController* controller =
   2212       &(content::Source<content::WebContents>(source).ptr()->GetController());
   2213   if (automation_.get()) {
   2214     // TODO(phajdan.jr): Clean up this hack. We write the correct return type
   2215     // here, but don't send the message. NavigationNotificationObserver
   2216     // will wait properly for the load to finish, and send the message,
   2217     // but it will also append its own return value at the end of the reply.
   2218     if (!use_json_interface_)
   2219       AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_.get(),
   2220                                                            true);
   2221     new NavigationNotificationObserver(controller,
   2222                                        automation_.get(),
   2223                                        reply_message_.release(),
   2224                                        1,
   2225                                        false,
   2226                                        use_json_interface_);
   2227   }
   2228   delete this;
   2229 }
   2230 
   2231 NewTabObserver::~NewTabObserver() {
   2232 }
   2233 
   2234 WaitForProcessLauncherThreadToGoIdleObserver::
   2235 WaitForProcessLauncherThreadToGoIdleObserver(
   2236     AutomationProvider* automation, IPC::Message* reply_message)
   2237     : automation_(automation->AsWeakPtr()),
   2238       reply_message_(reply_message) {
   2239   // Balanced in RunOnUIThread.
   2240   AddRef();
   2241   BrowserThread::PostTask(
   2242       BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
   2243       base::Bind(
   2244           &WaitForProcessLauncherThreadToGoIdleObserver::
   2245               RunOnProcessLauncherThread,
   2246           this));
   2247 }
   2248 
   2249 WaitForProcessLauncherThreadToGoIdleObserver::
   2250     ~WaitForProcessLauncherThreadToGoIdleObserver() {
   2251 }
   2252 
   2253 void WaitForProcessLauncherThreadToGoIdleObserver::
   2254 RunOnProcessLauncherThread() {
   2255   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER));
   2256   BrowserThread::PostTask(
   2257       BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
   2258       base::Bind(
   2259           &WaitForProcessLauncherThreadToGoIdleObserver::
   2260               RunOnProcessLauncherThread2,
   2261           this));
   2262 }
   2263 
   2264 void WaitForProcessLauncherThreadToGoIdleObserver::
   2265 RunOnProcessLauncherThread2() {
   2266   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER));
   2267   BrowserThread::PostTask(
   2268       BrowserThread::UI, FROM_HERE,
   2269       base::Bind(&WaitForProcessLauncherThreadToGoIdleObserver::RunOnUIThread,
   2270                  this));
   2271 }
   2272 
   2273 void WaitForProcessLauncherThreadToGoIdleObserver::RunOnUIThread() {
   2274   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   2275   if (automation_.get())
   2276     automation_->Send(reply_message_.release());
   2277   Release();
   2278 }
   2279 
   2280 DragTargetDropAckNotificationObserver::DragTargetDropAckNotificationObserver(
   2281     AutomationProvider* automation,
   2282     IPC::Message* reply_message)
   2283     : automation_(automation->AsWeakPtr()),
   2284       reply_message_(reply_message) {
   2285   registrar_.Add(
   2286       this,
   2287       content::NOTIFICATION_RENDER_VIEW_HOST_DID_RECEIVE_DRAG_TARGET_DROP_ACK,
   2288       content::NotificationService::AllSources());
   2289   registrar_.Add(
   2290       this,
   2291       chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN,
   2292       content::NotificationService::AllSources());
   2293 }
   2294 
   2295 DragTargetDropAckNotificationObserver::
   2296     ~DragTargetDropAckNotificationObserver() {}
   2297 
   2298 void DragTargetDropAckNotificationObserver::Observe(
   2299     int type,
   2300     const content::NotificationSource& source,
   2301     const content::NotificationDetails& details) {
   2302   if (automation_.get()) {
   2303     AutomationJSONReply(automation_.get(), reply_message_.release())
   2304         .SendSuccess(NULL);
   2305   }
   2306   delete this;
   2307 }
   2308 
   2309 ProcessInfoObserver::ProcessInfoObserver(
   2310     AutomationProvider* automation,
   2311     IPC::Message* reply_message)
   2312     : automation_(automation->AsWeakPtr()),
   2313       reply_message_(reply_message) {}
   2314 
   2315 ProcessInfoObserver::~ProcessInfoObserver() {}
   2316 
   2317 void ProcessInfoObserver::OnDetailsAvailable() {
   2318   scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
   2319   ListValue* browser_proc_list = new ListValue();
   2320   const std::vector<ProcessData>& all_processes = processes();
   2321   for (size_t index = 0; index < all_processes.size(); ++index) {
   2322     DictionaryValue* browser_data = new DictionaryValue();
   2323     browser_data->SetString("name", all_processes[index].name);
   2324     browser_data->SetString("process_name", all_processes[index].process_name);
   2325 
   2326     ListValue* proc_list = new ListValue();
   2327     for (ProcessMemoryInformationList::const_iterator iterator =
   2328              all_processes[index].processes.begin();
   2329          iterator != all_processes[index].processes.end(); ++iterator) {
   2330       DictionaryValue* proc_data = new DictionaryValue();
   2331 
   2332       proc_data->SetInteger("pid", iterator->pid);
   2333 
   2334       // Working set (resident) memory usage, in KBytes.
   2335       DictionaryValue* working_set = new DictionaryValue();
   2336       working_set->SetInteger("priv", iterator->working_set.priv);
   2337       working_set->SetInteger("shareable", iterator->working_set.shareable);
   2338       working_set->SetInteger("shared", iterator->working_set.shared);
   2339       proc_data->Set("working_set_mem", working_set);
   2340 
   2341       // Committed (resident + paged) memory usage, in KBytes.
   2342       DictionaryValue* committed = new DictionaryValue();
   2343       committed->SetInteger("priv", iterator->committed.priv);
   2344       committed->SetInteger("mapped", iterator->committed.mapped);
   2345       committed->SetInteger("image", iterator->committed.image);
   2346       proc_data->Set("committed_mem", committed);
   2347 
   2348       proc_data->SetString("version", iterator->version);
   2349       proc_data->SetString("product_name", iterator->product_name);
   2350       proc_data->SetInteger("num_processes", iterator->num_processes);
   2351       proc_data->SetBoolean("is_diagnostics", iterator->is_diagnostics);
   2352 
   2353       // Process type, if this is a child process of Chrome (e.g., 'plugin').
   2354       std::string process_type = "Unknown";
   2355       // The following condition avoids a DCHECK in debug builds when the
   2356       // process type passed to |GetTypeNameInEnglish| is unknown.
   2357       if (iterator->process_type != content::PROCESS_TYPE_UNKNOWN) {
   2358         process_type =
   2359             content::GetProcessTypeNameInEnglish(iterator->process_type);
   2360       }
   2361       proc_data->SetString("child_process_type", process_type);
   2362 
   2363       // Renderer type, if this is a renderer process.
   2364       std::string renderer_type = "Unknown";
   2365       if (iterator->renderer_type !=
   2366           ProcessMemoryInformation::RENDERER_UNKNOWN) {
   2367         renderer_type = ProcessMemoryInformation::GetRendererTypeNameInEnglish(
   2368             iterator->renderer_type);
   2369       }
   2370       proc_data->SetString("renderer_type", renderer_type);
   2371 
   2372       // Titles associated with this process.
   2373       ListValue* titles = new ListValue();
   2374       for (size_t title_index = 0; title_index < iterator->titles.size();
   2375            ++title_index)
   2376         titles->Append(Value::CreateStringValue(iterator->titles[title_index]));
   2377       proc_data->Set("titles", titles);
   2378 
   2379       proc_list->Append(proc_data);
   2380     }
   2381     browser_data->Set("processes", proc_list);
   2382 
   2383     browser_proc_list->Append(browser_data);
   2384   }
   2385   return_value->Set("browsers", browser_proc_list);
   2386 
   2387   if (automation_.get()) {
   2388     AutomationJSONReply(automation_.get(), reply_message_.release())
   2389         .SendSuccess(return_value.get());
   2390   }
   2391 }
   2392 
   2393 V8HeapStatsObserver::V8HeapStatsObserver(
   2394     AutomationProvider* automation,
   2395     IPC::Message* reply_message,
   2396     base::ProcessId renderer_id)
   2397     : automation_(automation->AsWeakPtr()),
   2398       reply_message_(reply_message),
   2399       renderer_id_(renderer_id) {
   2400   registrar_.Add(
   2401       this,
   2402       chrome::NOTIFICATION_RENDERER_V8_HEAP_STATS_COMPUTED,
   2403       content::NotificationService::AllSources());
   2404 }
   2405 
   2406 V8HeapStatsObserver::~V8HeapStatsObserver() {}
   2407 
   2408 void V8HeapStatsObserver::Observe(
   2409     int type,
   2410     const content::NotificationSource& source,
   2411     const content::NotificationDetails& details) {
   2412   DCHECK(type == chrome::NOTIFICATION_RENDERER_V8_HEAP_STATS_COMPUTED);
   2413 
   2414   base::ProcessId updated_renderer_id =
   2415       *(content::Source<base::ProcessId>(source).ptr());
   2416   // Only return information for the renderer ID we're interested in.
   2417   if (renderer_id_ != updated_renderer_id)
   2418     return;
   2419 
   2420   ChromeRenderMessageFilter::V8HeapStatsDetails* v8_heap_details =
   2421       content::Details<ChromeRenderMessageFilter::V8HeapStatsDetails>(details)
   2422           .ptr();
   2423   scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
   2424   return_value->SetInteger("renderer_id", updated_renderer_id);
   2425   return_value->SetInteger("v8_memory_allocated",
   2426                            v8_heap_details->v8_memory_allocated());
   2427   return_value->SetInteger("v8_memory_used",
   2428                            v8_heap_details->v8_memory_used());
   2429 
   2430   if (automation_.get()) {
   2431     AutomationJSONReply(automation_.get(), reply_message_.release())
   2432         .SendSuccess(return_value.get());
   2433   }
   2434   delete this;
   2435 }
   2436 
   2437 FPSObserver::FPSObserver(
   2438     AutomationProvider* automation,
   2439     IPC::Message* reply_message,
   2440     base::ProcessId renderer_id,
   2441     int routing_id)
   2442     : automation_(automation->AsWeakPtr()),
   2443       reply_message_(reply_message),
   2444       renderer_id_(renderer_id),
   2445       routing_id_(routing_id) {
   2446   registrar_.Add(
   2447       this,
   2448       chrome::NOTIFICATION_RENDERER_FPS_COMPUTED,
   2449       content::NotificationService::AllSources());
   2450 }
   2451 
   2452 FPSObserver::~FPSObserver() {}
   2453 
   2454 void FPSObserver::Observe(
   2455     int type,
   2456     const content::NotificationSource& source,
   2457     const content::NotificationDetails& details) {
   2458   DCHECK(type == chrome::NOTIFICATION_RENDERER_FPS_COMPUTED);
   2459 
   2460   base::ProcessId updated_renderer_id =
   2461       *(content::Source<base::ProcessId>(source).ptr());
   2462   // Only return information for the renderer ID we're interested in.
   2463   if (renderer_id_ != updated_renderer_id)
   2464     return;
   2465 
   2466   ChromeRenderMessageFilter::FPSDetails* fps_details =
   2467       content::Details<ChromeRenderMessageFilter::FPSDetails>(details).ptr();
   2468   // Only return information for the routing id of the host render view we're
   2469   // interested in.
   2470   if (routing_id_ != fps_details->routing_id())
   2471     return;
   2472 
   2473   scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
   2474   return_value->SetInteger("renderer_id", updated_renderer_id);
   2475   return_value->SetInteger("routing_id", fps_details->routing_id());
   2476   return_value->SetDouble("fps", fps_details->fps());
   2477   if (automation_.get()) {
   2478     AutomationJSONReply(automation_.get(), reply_message_.release())
   2479         .SendSuccess(return_value.get());
   2480   }
   2481   delete this;
   2482 }
   2483 
   2484 BrowserOpenedWithNewProfileNotificationObserver::
   2485     BrowserOpenedWithNewProfileNotificationObserver(
   2486         AutomationProvider* automation,
   2487         IPC::Message* reply_message)
   2488         : automation_(automation->AsWeakPtr()),
   2489           reply_message_(reply_message),
   2490           new_window_id_(extension_misc::kUnknownWindowId) {
   2491   registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CREATED,
   2492                  content::NotificationService::AllBrowserContextsAndSources());
   2493   registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED,
   2494                  content::NotificationService::AllBrowserContextsAndSources());
   2495   registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
   2496                  content::NotificationService::AllBrowserContextsAndSources());
   2497 }
   2498 
   2499 BrowserOpenedWithNewProfileNotificationObserver::
   2500     ~BrowserOpenedWithNewProfileNotificationObserver() {
   2501 }
   2502 
   2503 void BrowserOpenedWithNewProfileNotificationObserver::Observe(
   2504     int type,
   2505     const content::NotificationSource& source,
   2506     const content::NotificationDetails& details) {
   2507   if (!automation_.get()) {
   2508     delete this;
   2509     return;
   2510   }
   2511 
   2512   if (type == chrome::NOTIFICATION_PROFILE_CREATED) {
   2513     // As part of multi-profile creation, a new browser window will
   2514     // automatically be opened.
   2515     Profile* profile = content::Source<Profile>(source).ptr();
   2516     if (!profile) {
   2517       AutomationJSONReply(automation_.get(), reply_message_.release())
   2518           .SendError("Profile could not be created.");
   2519       return;
   2520     }
   2521   } else if (type == chrome::NOTIFICATION_BROWSER_OPENED) {
   2522     // Store the new browser ID and continue waiting for a new tab within it
   2523     // to stop loading.
   2524     new_window_id_ = extensions::ExtensionTabUtil::GetWindowId(
   2525         content::Source<Browser>(source).ptr());
   2526   } else {
   2527     DCHECK_EQ(content::NOTIFICATION_LOAD_STOP, type);
   2528     // Only send the result if the loaded tab is in the new window.
   2529     NavigationController* controller =
   2530         content::Source<NavigationController>(source).ptr();
   2531     SessionTabHelper* session_tab_helper =
   2532         SessionTabHelper::FromWebContents(controller->GetWebContents());
   2533     int window_id = session_tab_helper ? session_tab_helper->window_id().id()
   2534                                        : -1;
   2535     if (window_id == new_window_id_) {
   2536       if (automation_.get()) {
   2537         AutomationJSONReply(automation_.get(), reply_message_.release())
   2538             .SendSuccess(NULL);
   2539       }
   2540       delete this;
   2541     }
   2542   }
   2543 }
   2544 
   2545 ExtensionPopupObserver::ExtensionPopupObserver(
   2546     AutomationProvider* automation,
   2547     IPC::Message* reply_message,
   2548     const std::string& extension_id)
   2549     : automation_(automation->AsWeakPtr()),
   2550       reply_message_(reply_message),
   2551       extension_id_(extension_id) {
   2552   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING,
   2553                  content::NotificationService::AllSources());
   2554 }
   2555 
   2556 ExtensionPopupObserver::~ExtensionPopupObserver() {
   2557 }
   2558 
   2559 void ExtensionPopupObserver::Observe(
   2560     int type,
   2561     const content::NotificationSource& source,
   2562     const content::NotificationDetails& details) {
   2563   if (!automation_.get()) {
   2564     delete this;
   2565     return;
   2566   }
   2567 
   2568   extensions::ExtensionHost* host =
   2569       content::Details<extensions::ExtensionHost>(details).ptr();
   2570   if (host->extension_id() == extension_id_ &&
   2571       host->extension_host_type() == extensions::VIEW_TYPE_EXTENSION_POPUP) {
   2572     AutomationJSONReply(automation_.get(), reply_message_.release())
   2573         .SendSuccess(NULL);
   2574     delete this;
   2575   }
   2576 }
   2577 
   2578 #if defined(OS_LINUX)
   2579 WindowMaximizedObserver::WindowMaximizedObserver(
   2580     AutomationProvider* automation,
   2581     IPC::Message* reply_message)
   2582     : automation_(automation->AsWeakPtr()),
   2583       reply_message_(reply_message) {
   2584   registrar_.Add(this, chrome::NOTIFICATION_BROWSER_WINDOW_MAXIMIZED,
   2585                  content::NotificationService::AllSources());
   2586 }
   2587 
   2588 WindowMaximizedObserver::~WindowMaximizedObserver() {}
   2589 
   2590 void WindowMaximizedObserver::Observe(
   2591     int type,
   2592     const content::NotificationSource& source,
   2593     const content::NotificationDetails& details) {
   2594   DCHECK_EQ(chrome::NOTIFICATION_BROWSER_WINDOW_MAXIMIZED, type);
   2595 
   2596   if (automation_.get()) {
   2597     AutomationJSONReply(automation_.get(), reply_message_.release())
   2598         .SendSuccess(NULL);
   2599   }
   2600   delete this;
   2601 }
   2602 #endif  // defined(OS_LINUX)
   2603 
   2604 BrowserOpenedWithExistingProfileNotificationObserver::
   2605     BrowserOpenedWithExistingProfileNotificationObserver(
   2606         AutomationProvider* automation,
   2607         IPC::Message* reply_message,
   2608         int num_loads)
   2609         : automation_(automation->AsWeakPtr()),
   2610           reply_message_(reply_message),
   2611           new_window_id_(extension_misc::kUnknownWindowId),
   2612           num_loads_(num_loads) {
   2613   registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED,
   2614                  content::NotificationService::AllBrowserContextsAndSources());
   2615   registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
   2616                  content::NotificationService::AllBrowserContextsAndSources());
   2617 }
   2618 
   2619 BrowserOpenedWithExistingProfileNotificationObserver::
   2620     ~BrowserOpenedWithExistingProfileNotificationObserver() {
   2621 }
   2622 
   2623 void BrowserOpenedWithExistingProfileNotificationObserver::Observe(
   2624     int type,
   2625     const content::NotificationSource& source,
   2626     const content::NotificationDetails& details) {
   2627   if (!automation_.get()) {
   2628     delete this;
   2629     return;
   2630   }
   2631 
   2632   if (type == chrome::NOTIFICATION_BROWSER_OPENED) {
   2633     // Store the new browser ID and continue waiting for NOTIFICATION_LOAD_STOP.
   2634     new_window_id_ = extensions::ExtensionTabUtil::GetWindowId(
   2635         content::Source<Browser>(source).ptr());
   2636   } else if (type == content::NOTIFICATION_LOAD_STOP) {
   2637     // Only consider if the loaded tab is in the new window.
   2638     NavigationController* controller =
   2639         content::Source<NavigationController>(source).ptr();
   2640     SessionTabHelper* session_tab_helper =
   2641         SessionTabHelper::FromWebContents(controller->GetWebContents());
   2642     int window_id = session_tab_helper ? session_tab_helper->window_id().id()
   2643                                        : -1;
   2644     if (window_id == new_window_id_ && --num_loads_ == 0) {
   2645       if (automation_.get()) {
   2646         AutomationJSONReply(automation_.get(), reply_message_.release())
   2647             .SendSuccess(NULL);
   2648       }
   2649       delete this;
   2650     }
   2651   } else {
   2652     NOTREACHED();
   2653   }
   2654 }
   2655