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