Home | History | Annotate | Download | only in automation
      1 // Copyright (c) 2011 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/callback.h"
     13 #include "base/file_util.h"
     14 #include "base/json/json_writer.h"
     15 #include "base/memory/scoped_ptr.h"
     16 #include "base/string_util.h"
     17 #include "base/stringprintf.h"
     18 #include "base/threading/thread_restrictions.h"
     19 #include "base/utf_string_conversions.h"
     20 #include "base/values.h"
     21 #include "chrome/app/chrome_command_ids.h"
     22 #include "chrome/browser/automation/automation_provider.h"
     23 #include "chrome/browser/automation/automation_provider_json.h"
     24 #include "chrome/browser/bookmarks/bookmark_model.h"
     25 #include "chrome/browser/browser_process.h"
     26 #include "chrome/browser/dom_operation_notification_details.h"
     27 #include "chrome/browser/download/download_item.h"
     28 #include "chrome/browser/download/save_package.h"
     29 #include "chrome/browser/extensions/crx_installer.h"
     30 #include "chrome/browser/extensions/extension_host.h"
     31 #include "chrome/browser/extensions/extension_process_manager.h"
     32 #include "chrome/browser/extensions/extension_tabs_module.h"
     33 #include "chrome/browser/extensions/extension_updater.h"
     34 #include "chrome/browser/history/top_sites.h"
     35 #include "chrome/browser/metrics/metric_event_duration_details.h"
     36 #include "chrome/browser/notifications/balloon.h"
     37 #include "chrome/browser/notifications/balloon_collection.h"
     38 #include "chrome/browser/notifications/balloon_host.h"
     39 #include "chrome/browser/notifications/notification.h"
     40 #include "chrome/browser/notifications/notification_ui_manager.h"
     41 #include "chrome/browser/printing/print_job.h"
     42 #include "chrome/browser/profiles/profile.h"
     43 #include "chrome/browser/search_engines/template_url_model.h"
     44 #include "chrome/browser/sessions/tab_restore_service.h"
     45 #include "chrome/browser/tab_contents/thumbnail_generator.h"
     46 #include "chrome/browser/translate/page_translated_details.h"
     47 #include "chrome/browser/translate/translate_infobar_delegate.h"
     48 #include "chrome/browser/translate/translate_tab_helper.h"
     49 #include "chrome/browser/ui/browser.h"
     50 #include "chrome/browser/ui/browser_list.h"
     51 #include "chrome/browser/ui/find_bar/find_notification_details.h"
     52 #include "chrome/browser/ui/login/login_prompt.h"
     53 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
     54 #include "chrome/browser/ui/webui/app_launcher_handler.h"
     55 #include "chrome/browser/ui/webui/most_visited_handler.h"
     56 #include "chrome/browser/ui/webui/new_tab_ui.h"
     57 #include "chrome/common/automation_messages.h"
     58 #include "chrome/common/extensions/extension.h"
     59 #include "content/browser/renderer_host/render_process_host.h"
     60 #include "content/browser/renderer_host/render_view_host.h"
     61 #include "content/browser/tab_contents/navigation_controller.h"
     62 #include "content/browser/tab_contents/tab_contents.h"
     63 #include "content/common/notification_service.h"
     64 #include "googleurl/src/gurl.h"
     65 #include "ui/gfx/codec/png_codec.h"
     66 #include "ui/gfx/rect.h"
     67 
     68 // Holds onto start and stop timestamps for a particular tab
     69 class InitialLoadObserver::TabTime {
     70  public:
     71   explicit TabTime(base::TimeTicks started)
     72       : load_start_time_(started) {
     73   }
     74   void set_stop_time(base::TimeTicks stopped) {
     75     load_stop_time_ = stopped;
     76   }
     77   base::TimeTicks stop_time() const {
     78     return load_stop_time_;
     79   }
     80   base::TimeTicks start_time() const {
     81     return load_start_time_;
     82   }
     83  private:
     84   base::TimeTicks load_start_time_;
     85   base::TimeTicks load_stop_time_;
     86 };
     87 
     88 InitialLoadObserver::InitialLoadObserver(size_t tab_count,
     89                                          AutomationProvider* automation)
     90     : automation_(automation->AsWeakPtr()),
     91       outstanding_tab_count_(tab_count),
     92       init_time_(base::TimeTicks::Now()) {
     93   if (outstanding_tab_count_ > 0) {
     94     registrar_.Add(this, NotificationType::LOAD_START,
     95                    NotificationService::AllSources());
     96     registrar_.Add(this, NotificationType::LOAD_STOP,
     97                    NotificationService::AllSources());
     98   }
     99 }
    100 
    101 InitialLoadObserver::~InitialLoadObserver() {
    102 }
    103 
    104 void InitialLoadObserver::Observe(NotificationType type,
    105                                   const NotificationSource& source,
    106                                   const NotificationDetails& details) {
    107   if (type == NotificationType::LOAD_START) {
    108     if (outstanding_tab_count_ > loading_tabs_.size())
    109       loading_tabs_.insert(TabTimeMap::value_type(
    110           source.map_key(),
    111           TabTime(base::TimeTicks::Now())));
    112   } else if (type == NotificationType::LOAD_STOP) {
    113     if (outstanding_tab_count_ > finished_tabs_.size()) {
    114       TabTimeMap::iterator iter = loading_tabs_.find(source.map_key());
    115       if (iter != loading_tabs_.end()) {
    116         finished_tabs_.insert(source.map_key());
    117         iter->second.set_stop_time(base::TimeTicks::Now());
    118       }
    119       if (outstanding_tab_count_ == finished_tabs_.size())
    120         ConditionMet();
    121     }
    122   } else {
    123     NOTREACHED();
    124   }
    125 }
    126 
    127 DictionaryValue* InitialLoadObserver::GetTimingInformation() const {
    128   ListValue* items = new ListValue;
    129   for (TabTimeMap::const_iterator it = loading_tabs_.begin();
    130        it != loading_tabs_.end();
    131        ++it) {
    132     DictionaryValue* item = new DictionaryValue;
    133     base::TimeDelta delta_start = it->second.start_time() - init_time_;
    134 
    135     item->SetDouble("load_start_ms", delta_start.InMillisecondsF());
    136     if (it->second.stop_time().is_null()) {
    137       item->Set("load_stop_ms", Value::CreateNullValue());
    138     } else {
    139       base::TimeDelta delta_stop = it->second.stop_time() - init_time_;
    140       item->SetDouble("load_stop_ms", delta_stop.InMillisecondsF());
    141     }
    142     items->Append(item);
    143   }
    144   DictionaryValue* return_value = new DictionaryValue;
    145   return_value->Set("tabs", items);
    146   return return_value;
    147 }
    148 
    149 void InitialLoadObserver::ConditionMet() {
    150   registrar_.RemoveAll();
    151   if (automation_)
    152     automation_->OnInitialTabLoadsComplete();
    153 }
    154 
    155 NewTabUILoadObserver::NewTabUILoadObserver(AutomationProvider* automation)
    156     : automation_(automation->AsWeakPtr()) {
    157   registrar_.Add(this, NotificationType::INITIAL_NEW_TAB_UI_LOAD,
    158                  NotificationService::AllSources());
    159 }
    160 
    161 NewTabUILoadObserver::~NewTabUILoadObserver() {
    162 }
    163 
    164 void NewTabUILoadObserver::Observe(NotificationType type,
    165                                    const NotificationSource& source,
    166                                    const NotificationDetails& details) {
    167   if (type == NotificationType::INITIAL_NEW_TAB_UI_LOAD) {
    168     Details<int> load_time(details);
    169     if (automation_) {
    170       automation_->Send(
    171           new AutomationMsg_InitialNewTabUILoadComplete(*load_time.ptr()));
    172     }
    173   } else {
    174     NOTREACHED();
    175   }
    176 }
    177 
    178 NavigationControllerRestoredObserver::NavigationControllerRestoredObserver(
    179     AutomationProvider* automation,
    180     NavigationController* controller,
    181     IPC::Message* reply_message)
    182     : automation_(automation->AsWeakPtr()),
    183       controller_(controller),
    184       reply_message_(reply_message) {
    185   if (FinishedRestoring()) {
    186     SendDone();
    187   } else {
    188     registrar_.Add(this, NotificationType::LOAD_STOP,
    189                    NotificationService::AllSources());
    190   }
    191 }
    192 
    193 NavigationControllerRestoredObserver::~NavigationControllerRestoredObserver() {
    194 }
    195 
    196 void NavigationControllerRestoredObserver::Observe(
    197     NotificationType type, const NotificationSource& source,
    198     const NotificationDetails& details) {
    199   if (FinishedRestoring()) {
    200     SendDone();
    201     registrar_.RemoveAll();
    202   }
    203 }
    204 
    205 bool NavigationControllerRestoredObserver::FinishedRestoring() {
    206   return (!controller_->needs_reload() && !controller_->pending_entry() &&
    207           !controller_->tab_contents()->is_loading());
    208 }
    209 
    210 void NavigationControllerRestoredObserver::SendDone() {
    211   if (!automation_)
    212     return;
    213 
    214   AutomationMsg_WaitForTabToBeRestored::WriteReplyParams(reply_message_.get(),
    215                                                          true);
    216   automation_->Send(reply_message_.release());
    217 }
    218 
    219 NavigationNotificationObserver::NavigationNotificationObserver(
    220     NavigationController* controller,
    221     AutomationProvider* automation,
    222     IPC::Message* reply_message,
    223     int number_of_navigations,
    224     bool include_current_navigation,
    225     bool use_json_interface)
    226     : automation_(automation->AsWeakPtr()),
    227       reply_message_(reply_message),
    228       controller_(controller),
    229       navigations_remaining_(number_of_navigations),
    230       navigation_started_(false),
    231       use_json_interface_(use_json_interface) {
    232   DCHECK_LT(0, navigations_remaining_);
    233   Source<NavigationController> source(controller_);
    234   registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED, source);
    235   registrar_.Add(this, NotificationType::LOAD_START, source);
    236   registrar_.Add(this, NotificationType::LOAD_STOP, source);
    237   registrar_.Add(this, NotificationType::AUTH_NEEDED, source);
    238   registrar_.Add(this, NotificationType::AUTH_SUPPLIED, source);
    239   registrar_.Add(this, NotificationType::AUTH_CANCELLED, source);
    240 
    241   if (include_current_navigation && controller->tab_contents()->is_loading())
    242     navigation_started_ = true;
    243 }
    244 
    245 NavigationNotificationObserver::~NavigationNotificationObserver() {
    246 }
    247 
    248 void NavigationNotificationObserver::Observe(
    249     NotificationType type, const NotificationSource& source,
    250     const NotificationDetails& details) {
    251   if (!automation_) {
    252     delete this;
    253     return;
    254   }
    255 
    256   // We listen for 2 events to determine when the navigation started because:
    257   // - when this is used by the WaitForNavigation method, we might be invoked
    258   // afer the load has started (but not after the entry was committed, as
    259   // WaitForNavigation compares times of the last navigation).
    260   // - when this is used with a page requiring authentication, we will not get
    261   // a NotificationType::NAV_ENTRY_COMMITTED until after we authenticate, so
    262   // we need the NotificationType::LOAD_START.
    263   if (type == NotificationType::NAV_ENTRY_COMMITTED ||
    264       type == NotificationType::LOAD_START) {
    265     navigation_started_ = true;
    266   } else if (type == NotificationType::LOAD_STOP) {
    267     if (navigation_started_) {
    268       navigation_started_ = false;
    269       if (--navigations_remaining_ == 0)
    270         ConditionMet(AUTOMATION_MSG_NAVIGATION_SUCCESS);
    271     }
    272   } else if (type == NotificationType::AUTH_SUPPLIED ||
    273              type == NotificationType::AUTH_CANCELLED) {
    274     // The LoginHandler for this tab is no longer valid.
    275     automation_->RemoveLoginHandler(controller_);
    276 
    277     // Treat this as if navigation started again, since load start/stop don't
    278     // occur while authentication is ongoing.
    279     navigation_started_ = true;
    280   } else if (type == NotificationType::AUTH_NEEDED) {
    281     // Remember the login handler that wants authentication.
    282     // We do this in all cases (not just when navigation_started_ == true) so
    283     // tests can still wait for auth dialogs outside of navigation.
    284     LoginHandler* handler =
    285         Details<LoginNotificationDetails>(details)->handler();
    286     automation_->AddLoginHandler(controller_, handler);
    287 
    288     // Respond that authentication is needed.
    289     navigation_started_ = false;
    290     ConditionMet(AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED);
    291   } else {
    292     NOTREACHED();
    293   }
    294 }
    295 
    296 void NavigationNotificationObserver::ConditionMet(
    297     AutomationMsg_NavigationResponseValues navigation_result) {
    298   if (automation_) {
    299     if (use_json_interface_) {
    300       DictionaryValue dict;
    301       dict.SetInteger("result", navigation_result);
    302       AutomationJSONReply(automation_, reply_message_.release())
    303           .SendSuccess(&dict);
    304     } else {
    305       IPC::ParamTraits<AutomationMsg_NavigationResponseValues>::Write(
    306           reply_message_.get(), navigation_result);
    307       automation_->Send(reply_message_.release());
    308     }
    309   }
    310 
    311   delete this;
    312 }
    313 
    314 TabStripNotificationObserver::TabStripNotificationObserver(
    315     NotificationType notification, AutomationProvider* automation)
    316     : automation_(automation->AsWeakPtr()),
    317       notification_(notification) {
    318   registrar_.Add(this, notification_, NotificationService::AllSources());
    319 }
    320 
    321 TabStripNotificationObserver::~TabStripNotificationObserver() {
    322 }
    323 
    324 void TabStripNotificationObserver::Observe(NotificationType type,
    325                                            const NotificationSource& source,
    326                                            const NotificationDetails& details) {
    327   if (type == notification_) {
    328     ObserveTab(Source<NavigationController>(source).ptr());
    329     delete this;
    330   } else {
    331     NOTREACHED();
    332   }
    333 }
    334 
    335 TabAppendedNotificationObserver::TabAppendedNotificationObserver(
    336     Browser* parent, AutomationProvider* automation,
    337     IPC::Message* reply_message)
    338     : TabStripNotificationObserver(NotificationType::TAB_PARENTED, automation),
    339       parent_(parent),
    340       reply_message_(reply_message) {
    341 }
    342 
    343 TabAppendedNotificationObserver::~TabAppendedNotificationObserver() {}
    344 
    345 void TabAppendedNotificationObserver::ObserveTab(
    346     NavigationController* controller) {
    347   if (!automation_)
    348     return;
    349 
    350   if (automation_->GetIndexForNavigationController(controller, parent_) ==
    351       TabStripModel::kNoTab) {
    352     // This tab notification doesn't belong to the parent_.
    353     return;
    354   }
    355 
    356   new NavigationNotificationObserver(controller, automation_,
    357                                      reply_message_.release(),
    358                                      1, false, false);
    359 }
    360 
    361 TabClosedNotificationObserver::TabClosedNotificationObserver(
    362     AutomationProvider* automation, bool wait_until_closed,
    363     IPC::Message* reply_message)
    364     : TabStripNotificationObserver(wait_until_closed ?
    365           NotificationType::TAB_CLOSED : NotificationType::TAB_CLOSING,
    366           automation),
    367       reply_message_(reply_message),
    368       for_browser_command_(false) {
    369 }
    370 
    371 TabClosedNotificationObserver::~TabClosedNotificationObserver() {}
    372 
    373 void TabClosedNotificationObserver::ObserveTab(
    374     NavigationController* controller) {
    375   if (!automation_)
    376     return;
    377 
    378   if (for_browser_command_) {
    379     AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_.get(),
    380                                                          true);
    381   } else {
    382     AutomationMsg_CloseTab::WriteReplyParams(reply_message_.get(), true);
    383   }
    384   automation_->Send(reply_message_.release());
    385 }
    386 
    387 void TabClosedNotificationObserver::set_for_browser_command(
    388     bool for_browser_command) {
    389   for_browser_command_ = for_browser_command;
    390 }
    391 
    392 TabCountChangeObserver::TabCountChangeObserver(AutomationProvider* automation,
    393                                                Browser* browser,
    394                                                IPC::Message* reply_message,
    395                                                int target_tab_count)
    396     : automation_(automation->AsWeakPtr()),
    397       reply_message_(reply_message),
    398       tab_strip_model_(browser->tabstrip_model()),
    399       target_tab_count_(target_tab_count) {
    400   tab_strip_model_->AddObserver(this);
    401   CheckTabCount();
    402 }
    403 
    404 TabCountChangeObserver::~TabCountChangeObserver() {
    405   tab_strip_model_->RemoveObserver(this);
    406 }
    407 
    408 void TabCountChangeObserver::TabInsertedAt(TabContentsWrapper* contents,
    409                                            int index,
    410                                            bool foreground) {
    411   CheckTabCount();
    412 }
    413 
    414 void TabCountChangeObserver::TabDetachedAt(TabContentsWrapper* contents,
    415                                            int index) {
    416   CheckTabCount();
    417 }
    418 
    419 void TabCountChangeObserver::TabStripModelDeleted() {
    420   if (automation_) {
    421     AutomationMsg_WaitForTabCountToBecome::WriteReplyParams(
    422         reply_message_.get(), false);
    423     automation_->Send(reply_message_.release());
    424   }
    425 
    426   delete this;
    427 }
    428 
    429 void TabCountChangeObserver::CheckTabCount() {
    430   if (tab_strip_model_->count() != target_tab_count_)
    431     return;
    432 
    433   if (automation_) {
    434     AutomationMsg_WaitForTabCountToBecome::WriteReplyParams(
    435         reply_message_.get(), true);
    436     automation_->Send(reply_message_.release());
    437   }
    438 
    439   delete this;
    440 }
    441 
    442 bool DidExtensionHostsStopLoading(ExtensionProcessManager* manager) {
    443   for (ExtensionProcessManager::const_iterator iter = manager->begin();
    444        iter != manager->end(); ++iter) {
    445     if (!(*iter)->did_stop_loading())
    446       return false;
    447   }
    448   return true;
    449 }
    450 
    451 ExtensionInstallNotificationObserver::ExtensionInstallNotificationObserver(
    452     AutomationProvider* automation, int id, IPC::Message* reply_message)
    453     : automation_(automation->AsWeakPtr()),
    454       id_(id),
    455       reply_message_(reply_message) {
    456   registrar_.Add(this, NotificationType::EXTENSION_LOADED,
    457                  NotificationService::AllSources());
    458   registrar_.Add(this, NotificationType::EXTENSION_INSTALL_ERROR,
    459                  NotificationService::AllSources());
    460   registrar_.Add(this, NotificationType::EXTENSION_UPDATE_DISABLED,
    461                  NotificationService::AllSources());
    462 }
    463 
    464 ExtensionInstallNotificationObserver::~ExtensionInstallNotificationObserver() {
    465 }
    466 
    467 void ExtensionInstallNotificationObserver::Observe(
    468     NotificationType type, const NotificationSource& source,
    469     const NotificationDetails& details) {
    470   switch (type.value) {
    471     case NotificationType::EXTENSION_LOADED:
    472       SendResponse(AUTOMATION_MSG_EXTENSION_INSTALL_SUCCEEDED);
    473       break;
    474     case NotificationType::EXTENSION_INSTALL_ERROR:
    475     case NotificationType::EXTENSION_UPDATE_DISABLED:
    476       SendResponse(AUTOMATION_MSG_EXTENSION_INSTALL_FAILED);
    477       break;
    478     default:
    479       NOTREACHED();
    480       break;
    481   }
    482 
    483   delete this;
    484 }
    485 
    486 void ExtensionInstallNotificationObserver::SendResponse(
    487     AutomationMsg_ExtensionResponseValues response) {
    488   if (!automation_ || !reply_message_.get()) {
    489     delete this;
    490     return;
    491   }
    492 
    493   switch (id_) {
    494     case AutomationMsg_InstallExtension::ID:
    495       AutomationMsg_InstallExtension::WriteReplyParams(reply_message_.get(),
    496                                                        response);
    497       break;
    498     default:
    499       NOTREACHED();
    500       break;
    501   }
    502 
    503   automation_->Send(reply_message_.release());
    504 }
    505 
    506 ExtensionUninstallObserver::ExtensionUninstallObserver(
    507     AutomationProvider* automation,
    508     IPC::Message* reply_message,
    509     const std::string& id)
    510     : automation_(automation->AsWeakPtr()),
    511       reply_message_(reply_message),
    512       id_(id) {
    513   registrar_.Add(this, NotificationType::EXTENSION_UNINSTALLED,
    514                  NotificationService::AllSources());
    515   registrar_.Add(this, NotificationType::EXTENSION_UNINSTALL_NOT_ALLOWED,
    516                  NotificationService::AllSources());
    517 }
    518 
    519 ExtensionUninstallObserver::~ExtensionUninstallObserver() {
    520 }
    521 
    522 void ExtensionUninstallObserver::Observe(
    523     NotificationType type,
    524     const NotificationSource& source,
    525     const NotificationDetails& details) {
    526   if (!automation_) {
    527     delete this;
    528     return;
    529   }
    530 
    531   switch (type.value) {
    532     case NotificationType::EXTENSION_UNINSTALLED: {
    533       UninstalledExtensionInfo* info =
    534           Details<UninstalledExtensionInfo>(details).ptr();
    535       if (id_ == info->extension_id) {
    536         scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
    537         return_value->SetBoolean("success", true);
    538         AutomationJSONReply(automation_, reply_message_.release())
    539             .SendSuccess(return_value.get());
    540         delete this;
    541         return;
    542       }
    543       break;
    544     }
    545 
    546     case NotificationType::EXTENSION_UNINSTALL_NOT_ALLOWED: {
    547       const Extension* extension = Details<Extension>(details).ptr();
    548       if (id_ == extension->id()) {
    549         scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
    550         return_value->SetBoolean("success", false);
    551         AutomationJSONReply(automation_, reply_message_.release())
    552             .SendSuccess(return_value.get());
    553         delete this;
    554         return;
    555       }
    556       break;
    557     }
    558 
    559     default:
    560       NOTREACHED();
    561   }
    562 }
    563 
    564 ExtensionReadyNotificationObserver::ExtensionReadyNotificationObserver(
    565     ExtensionProcessManager* manager, AutomationProvider* automation, int id,
    566     IPC::Message* reply_message)
    567     : manager_(manager),
    568       automation_(automation->AsWeakPtr()),
    569       id_(id),
    570       reply_message_(reply_message),
    571       extension_(NULL) {
    572   registrar_.Add(this, NotificationType::EXTENSION_HOST_DID_STOP_LOADING,
    573                  NotificationService::AllSources());
    574   registrar_.Add(this, NotificationType::EXTENSION_LOADED,
    575                  NotificationService::AllSources());
    576   registrar_.Add(this, NotificationType::EXTENSION_INSTALL_ERROR,
    577                  NotificationService::AllSources());
    578   registrar_.Add(this, NotificationType::EXTENSION_UPDATE_DISABLED,
    579                  NotificationService::AllSources());
    580 }
    581 
    582 ExtensionReadyNotificationObserver::~ExtensionReadyNotificationObserver() {
    583 }
    584 
    585 void ExtensionReadyNotificationObserver::Observe(
    586     NotificationType type, const NotificationSource& source,
    587     const NotificationDetails& details) {
    588   if (!automation_) {
    589     delete this;
    590     return;
    591   }
    592 
    593   bool success = false;
    594   switch (type.value) {
    595     case NotificationType::EXTENSION_HOST_DID_STOP_LOADING:
    596       // Only continue on with this method if our extension has been loaded
    597       // and all the extension hosts have stopped loading.
    598       if (!extension_ || !DidExtensionHostsStopLoading(manager_))
    599         return;
    600       success = true;
    601       break;
    602     case NotificationType::EXTENSION_LOADED:
    603       extension_ = Details<const Extension>(details).ptr();
    604       if (!DidExtensionHostsStopLoading(manager_))
    605         return;
    606       success = true;
    607       break;
    608     case NotificationType::EXTENSION_INSTALL_ERROR:
    609     case NotificationType::EXTENSION_UPDATE_DISABLED:
    610       success = false;
    611       break;
    612     default:
    613       NOTREACHED();
    614       break;
    615   }
    616 
    617   if (id_ == AutomationMsg_InstallExtensionAndGetHandle::ID) {
    618     // A handle of zero indicates an error.
    619     int extension_handle = 0;
    620     if (extension_)
    621       extension_handle = automation_->AddExtension(extension_);
    622     AutomationMsg_InstallExtensionAndGetHandle::WriteReplyParams(
    623         reply_message_.get(), extension_handle);
    624   } else if (id_ == AutomationMsg_EnableExtension::ID) {
    625     AutomationMsg_EnableExtension::WriteReplyParams(reply_message_.get(), true);
    626   } else {
    627     NOTREACHED();
    628     LOG(ERROR) << "Cannot write reply params for unknown message id.";
    629   }
    630 
    631   automation_->Send(reply_message_.release());
    632   delete this;
    633 }
    634 
    635 ExtensionUnloadNotificationObserver::ExtensionUnloadNotificationObserver()
    636     : did_receive_unload_notification_(false) {
    637   registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
    638                  NotificationService::AllSources());
    639 }
    640 
    641 ExtensionUnloadNotificationObserver::~ExtensionUnloadNotificationObserver() {
    642 }
    643 
    644 void ExtensionUnloadNotificationObserver::Observe(
    645     NotificationType type, const NotificationSource& source,
    646     const NotificationDetails& details) {
    647   if (type.value == NotificationType::EXTENSION_UNLOADED) {
    648     did_receive_unload_notification_ = true;
    649   } else {
    650     NOTREACHED();
    651   }
    652 }
    653 
    654 ExtensionsUpdatedObserver::ExtensionsUpdatedObserver(
    655     ExtensionProcessManager* manager, AutomationProvider* automation,
    656     IPC::Message* reply_message)
    657     : manager_(manager), automation_(automation->AsWeakPtr()),
    658       reply_message_(reply_message), updater_finished_(false) {
    659   registrar_.Add(this, NotificationType::EXTENSION_HOST_DID_STOP_LOADING,
    660                  NotificationService::AllSources());
    661   registrar_.Add(this, NotificationType::EXTENSION_INSTALL_ERROR,
    662                  NotificationService::AllSources());
    663   registrar_.Add(this, NotificationType::EXTENSION_INSTALL_NOT_ALLOWED,
    664                  NotificationService::AllSources());
    665   registrar_.Add(this, NotificationType::EXTENSION_LOADED,
    666                  NotificationService::AllSources());
    667   registrar_.Add(this, NotificationType::EXTENSION_UPDATE_DISABLED,
    668                  NotificationService::AllSources());
    669   registrar_.Add(this, NotificationType::EXTENSION_UPDATE_FOUND,
    670                  NotificationService::AllSources());
    671   registrar_.Add(this, NotificationType::EXTENSION_UPDATING_FINISHED,
    672                  NotificationService::AllSources());
    673 }
    674 
    675 ExtensionsUpdatedObserver::~ExtensionsUpdatedObserver() {
    676 }
    677 
    678 void ExtensionsUpdatedObserver::Observe(
    679     NotificationType type, const NotificationSource& source,
    680     const NotificationDetails& details) {
    681   if (!automation_) {
    682     delete this;
    683     return;
    684   }
    685 
    686   // We expect the following sequence of events.  First, the ExtensionUpdater
    687   // service notifies of each extension that needs to be updated.  Once the
    688   // ExtensionUpdater has finished searching for extensions to update, it
    689   // notifies that it is finished.  Meanwhile, the extensions are updated
    690   // asynchronously: either they will be updated and loaded, or else they will
    691   // not load due to (1) not being allowed; (2) having updating disabled; or
    692   // (3) encountering an error.  Finally, notifications are also sent whenever
    693   // an extension host stops loading.  Updating is not considered complete if
    694   // any extension hosts are still loading.
    695   switch (type.value) {
    696     case NotificationType::EXTENSION_UPDATE_FOUND:
    697       // Extension updater has identified an extension that needs to be updated.
    698       in_progress_updates_.insert(*(Details<const std::string>(details).ptr()));
    699       break;
    700 
    701     case NotificationType::EXTENSION_UPDATING_FINISHED:
    702       // Extension updater has completed notifying all extensions to update
    703       // themselves.
    704       updater_finished_ = true;
    705       break;
    706 
    707     case NotificationType::EXTENSION_LOADED:
    708     case NotificationType::EXTENSION_INSTALL_NOT_ALLOWED:
    709     case NotificationType::EXTENSION_UPDATE_DISABLED: {
    710       // An extension has either completed update installation and is now
    711       // loaded, or else the install has been skipped because it is
    712       // either not allowed or else has been disabled.
    713       const Extension* extension = Details<Extension>(details).ptr();
    714       in_progress_updates_.erase(extension->id());
    715       break;
    716     }
    717 
    718     case NotificationType::EXTENSION_INSTALL_ERROR: {
    719       // An extension had an error on update installation.
    720       CrxInstaller* installer = Source<CrxInstaller>(source).ptr();
    721       in_progress_updates_.erase(installer->expected_id());
    722       break;
    723     }
    724 
    725     case NotificationType::EXTENSION_HOST_DID_STOP_LOADING:
    726       // Break out to the conditional check below to see if all extension hosts
    727       // have stopped loading.
    728       break;
    729 
    730     default:
    731       NOTREACHED();
    732       break;
    733   }
    734 
    735   // Send the reply if (1) the extension updater has finished notifying all
    736   // extensions to update themselves; (2) all extensions that need to be updated
    737   // have completed installation and are now loaded; and (3) all extension hosts
    738   // have stopped loading.
    739   if (updater_finished_ && in_progress_updates_.empty() &&
    740       DidExtensionHostsStopLoading(manager_)) {
    741     AutomationJSONReply reply(automation_, reply_message_.release());
    742     reply.SendSuccess(NULL);
    743     delete this;
    744   }
    745 }
    746 
    747 ExtensionTestResultNotificationObserver::
    748     ExtensionTestResultNotificationObserver(AutomationProvider* automation)
    749         : automation_(automation->AsWeakPtr()) {
    750   registrar_.Add(this, NotificationType::EXTENSION_TEST_PASSED,
    751                  NotificationService::AllSources());
    752   registrar_.Add(this, NotificationType::EXTENSION_TEST_FAILED,
    753                  NotificationService::AllSources());
    754 }
    755 
    756 ExtensionTestResultNotificationObserver::
    757     ~ExtensionTestResultNotificationObserver() {
    758 }
    759 
    760 void ExtensionTestResultNotificationObserver::Observe(
    761     NotificationType type, const NotificationSource& source,
    762     const NotificationDetails& details) {
    763   switch (type.value) {
    764     case NotificationType::EXTENSION_TEST_PASSED:
    765       results_.push_back(true);
    766       messages_.push_back("");
    767       break;
    768 
    769     case NotificationType::EXTENSION_TEST_FAILED:
    770       results_.push_back(false);
    771       messages_.push_back(*(Details<std::string>(details).ptr()));
    772       break;
    773 
    774     default:
    775       NOTREACHED();
    776   }
    777   // There may be a reply message waiting for this event, so check.
    778   MaybeSendResult();
    779 }
    780 
    781 void ExtensionTestResultNotificationObserver::MaybeSendResult() {
    782   if (!automation_)
    783     return;
    784 
    785   if (!results_.empty()) {
    786     // This release method should return the automation's current
    787     // reply message, or NULL if there is no current one. If it is not
    788     // NULL, we are stating that we will handle this reply message.
    789     IPC::Message* reply_message = automation_->reply_message_release();
    790     // Send the result back if we have a reply message.
    791     if (reply_message) {
    792       AutomationMsg_WaitForExtensionTestResult::WriteReplyParams(
    793           reply_message, results_.front(), messages_.front());
    794       results_.pop_front();
    795       messages_.pop_front();
    796       automation_->Send(reply_message);
    797     }
    798   }
    799 }
    800 
    801 BrowserOpenedNotificationObserver::BrowserOpenedNotificationObserver(
    802     AutomationProvider* automation,
    803     IPC::Message* reply_message)
    804     : automation_(automation->AsWeakPtr()),
    805       reply_message_(reply_message),
    806       new_window_id_(extension_misc::kUnknownWindowId),
    807       for_browser_command_(false) {
    808   registrar_.Add(this, NotificationType::BROWSER_OPENED,
    809                  NotificationService::AllSources());
    810   registrar_.Add(this, NotificationType::LOAD_STOP,
    811                  NotificationService::AllSources());
    812 }
    813 
    814 BrowserOpenedNotificationObserver::~BrowserOpenedNotificationObserver() {
    815 }
    816 
    817 void BrowserOpenedNotificationObserver::Observe(
    818     NotificationType type, const NotificationSource& source,
    819     const NotificationDetails& details) {
    820   if (!automation_) {
    821     delete this;
    822     return;
    823   }
    824 
    825   if (type.value == NotificationType::BROWSER_OPENED) {
    826     // Store the new browser ID and continue waiting for a new tab within it
    827     // to stop loading.
    828     new_window_id_ = ExtensionTabUtil::GetWindowId(
    829         Source<Browser>(source).ptr());
    830   } else if (type.value == NotificationType::LOAD_STOP) {
    831     // Only send the result if the loaded tab is in the new window.
    832     int window_id = Source<NavigationController>(source)->window_id().id();
    833     if (window_id == new_window_id_) {
    834       if (for_browser_command_) {
    835         AutomationMsg_WindowExecuteCommand::WriteReplyParams(
    836             reply_message_.get(), true);
    837       }
    838       automation_->Send(reply_message_.release());
    839       delete this;
    840       return;
    841     }
    842   } else {
    843     NOTREACHED();
    844   }
    845 }
    846 
    847 void BrowserOpenedNotificationObserver::set_for_browser_command(
    848     bool for_browser_command) {
    849   for_browser_command_ = for_browser_command;
    850 }
    851 
    852 BrowserClosedNotificationObserver::BrowserClosedNotificationObserver(
    853     Browser* browser,
    854     AutomationProvider* automation,
    855     IPC::Message* reply_message)
    856     : automation_(automation->AsWeakPtr()),
    857       reply_message_(reply_message),
    858       for_browser_command_(false) {
    859   registrar_.Add(this, NotificationType::BROWSER_CLOSED,
    860                  Source<Browser>(browser));
    861 }
    862 
    863 BrowserClosedNotificationObserver::~BrowserClosedNotificationObserver() {}
    864 
    865 void BrowserClosedNotificationObserver::Observe(
    866     NotificationType type, const NotificationSource& source,
    867     const NotificationDetails& details) {
    868   DCHECK(type == NotificationType::BROWSER_CLOSED);
    869 
    870   if (!automation_) {
    871     delete this;
    872     return;
    873   }
    874 
    875   Details<bool> close_app(details);
    876 
    877   if (for_browser_command_) {
    878     AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_.get(),
    879                                                          true);
    880   } else {
    881     AutomationMsg_CloseBrowser::WriteReplyParams(reply_message_.get(), true,
    882                                                  *(close_app.ptr()));
    883   }
    884   automation_->Send(reply_message_.release());
    885   delete this;
    886 }
    887 
    888 void BrowserClosedNotificationObserver::set_for_browser_command(
    889     bool for_browser_command) {
    890   for_browser_command_ = for_browser_command;
    891 }
    892 
    893 BrowserCountChangeNotificationObserver::BrowserCountChangeNotificationObserver(
    894     int target_count,
    895     AutomationProvider* automation,
    896     IPC::Message* reply_message)
    897     : target_count_(target_count),
    898       automation_(automation->AsWeakPtr()),
    899       reply_message_(reply_message) {
    900   registrar_.Add(this, NotificationType::BROWSER_OPENED,
    901                  NotificationService::AllSources());
    902   registrar_.Add(this, NotificationType::BROWSER_CLOSED,
    903                  NotificationService::AllSources());
    904 }
    905 
    906 BrowserCountChangeNotificationObserver::
    907     ~BrowserCountChangeNotificationObserver() {}
    908 
    909 void BrowserCountChangeNotificationObserver::Observe(
    910     NotificationType type,
    911     const NotificationSource& source,
    912     const NotificationDetails& details) {
    913   DCHECK(type == NotificationType::BROWSER_OPENED ||
    914          type == NotificationType::BROWSER_CLOSED);
    915   int current_count = static_cast<int>(BrowserList::size());
    916   if (type == NotificationType::BROWSER_CLOSED) {
    917     // At the time of the notification the browser being closed is not removed
    918     // from the list. The real count is one less than the reported count.
    919     DCHECK_LT(0, current_count);
    920     current_count--;
    921   }
    922 
    923   if (!automation_) {
    924     delete this;
    925     return;
    926   }
    927 
    928   if (current_count == target_count_) {
    929     AutomationMsg_WaitForBrowserWindowCountToBecome::WriteReplyParams(
    930         reply_message_.get(), true);
    931     automation_->Send(reply_message_.release());
    932     delete this;
    933   }
    934 }
    935 
    936 AppModalDialogShownObserver::AppModalDialogShownObserver(
    937     AutomationProvider* automation, IPC::Message* reply_message)
    938     : automation_(automation->AsWeakPtr()),
    939       reply_message_(reply_message) {
    940   registrar_.Add(this, NotificationType::APP_MODAL_DIALOG_SHOWN,
    941                  NotificationService::AllSources());
    942 }
    943 
    944 AppModalDialogShownObserver::~AppModalDialogShownObserver() {
    945 }
    946 
    947 void AppModalDialogShownObserver::Observe(
    948     NotificationType type, const NotificationSource& source,
    949     const NotificationDetails& details) {
    950   DCHECK(type == NotificationType::APP_MODAL_DIALOG_SHOWN);
    951 
    952   if (automation_) {
    953     AutomationMsg_WaitForAppModalDialogToBeShown::WriteReplyParams(
    954         reply_message_.get(), true);
    955     automation_->Send(reply_message_.release());
    956   }
    957   delete this;
    958 }
    959 
    960 namespace {
    961 
    962 // Define mapping from command to notification
    963 struct CommandNotification {
    964   int command;
    965   NotificationType::Type notification_type;
    966 };
    967 
    968 const struct CommandNotification command_notifications[] = {
    969   {IDC_DUPLICATE_TAB, NotificationType::TAB_PARENTED},
    970 
    971   // Returns as soon as the restored tab is created. To further wait until
    972   // the content page is loaded, use WaitForTabToBeRestored.
    973   {IDC_RESTORE_TAB, NotificationType::TAB_PARENTED},
    974 
    975   // For the following commands, we need to wait for a new tab to be created,
    976   // load to finish, and title to change.
    977   {IDC_MANAGE_EXTENSIONS, NotificationType::TAB_CONTENTS_TITLE_UPDATED},
    978   {IDC_OPTIONS, NotificationType::TAB_CONTENTS_TITLE_UPDATED},
    979   {IDC_PRINT, NotificationType::TAB_CONTENTS_TITLE_UPDATED},
    980   {IDC_SHOW_DOWNLOADS, NotificationType::TAB_CONTENTS_TITLE_UPDATED},
    981   {IDC_SHOW_HISTORY, NotificationType::TAB_CONTENTS_TITLE_UPDATED},
    982 };
    983 
    984 }  // namespace
    985 
    986 ExecuteBrowserCommandObserver::~ExecuteBrowserCommandObserver() {
    987 }
    988 
    989 // static
    990 bool ExecuteBrowserCommandObserver::CreateAndRegisterObserver(
    991     AutomationProvider* automation, Browser* browser, int command,
    992     IPC::Message* reply_message) {
    993   bool result = true;
    994   switch (command) {
    995     case IDC_NEW_TAB: {
    996       new NewTabObserver(automation, reply_message);
    997       break;
    998     }
    999     case IDC_NEW_WINDOW:
   1000     case IDC_NEW_INCOGNITO_WINDOW: {
   1001       BrowserOpenedNotificationObserver* observer =
   1002           new BrowserOpenedNotificationObserver(automation, reply_message);
   1003       observer->set_for_browser_command(true);
   1004       break;
   1005     }
   1006     case IDC_CLOSE_WINDOW: {
   1007       BrowserClosedNotificationObserver* observer =
   1008           new BrowserClosedNotificationObserver(browser, automation,
   1009                                                 reply_message);
   1010       observer->set_for_browser_command(true);
   1011       break;
   1012     }
   1013     case IDC_CLOSE_TAB: {
   1014       TabClosedNotificationObserver* observer =
   1015           new TabClosedNotificationObserver(automation, true, reply_message);
   1016       observer->set_for_browser_command(true);
   1017       break;
   1018     }
   1019     case IDC_BACK:
   1020     case IDC_FORWARD:
   1021     case IDC_RELOAD: {
   1022       new NavigationNotificationObserver(
   1023           &browser->GetSelectedTabContents()->controller(),
   1024           automation, reply_message, 1, false, false);
   1025       break;
   1026     }
   1027     default: {
   1028       ExecuteBrowserCommandObserver* observer =
   1029           new ExecuteBrowserCommandObserver(automation, reply_message);
   1030       if (!observer->Register(command)) {
   1031         delete observer;
   1032         result = false;
   1033       }
   1034       break;
   1035     }
   1036   }
   1037   return result;
   1038 }
   1039 
   1040 void ExecuteBrowserCommandObserver::Observe(
   1041     NotificationType type, const NotificationSource& source,
   1042     const NotificationDetails& details) {
   1043   if (type == notification_type_) {
   1044     if (automation_) {
   1045       AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_.get(),
   1046                                                            true);
   1047       automation_->Send(reply_message_.release());
   1048     }
   1049     delete this;
   1050   } else {
   1051     NOTREACHED();
   1052   }
   1053 }
   1054 
   1055 ExecuteBrowserCommandObserver::ExecuteBrowserCommandObserver(
   1056     AutomationProvider* automation, IPC::Message* reply_message)
   1057     : automation_(automation->AsWeakPtr()),
   1058       notification_type_(NotificationType::ALL),
   1059       reply_message_(reply_message) {
   1060 }
   1061 
   1062 bool ExecuteBrowserCommandObserver::Register(int command) {
   1063   if (!GetNotificationType(command, &notification_type_))
   1064     return false;
   1065   registrar_.Add(this, notification_type_, NotificationService::AllSources());
   1066   return true;
   1067 }
   1068 
   1069 bool ExecuteBrowserCommandObserver::GetNotificationType(
   1070     int command, NotificationType::Type* type) {
   1071   if (!type)
   1072     return false;
   1073   bool found = false;
   1074   for (unsigned int i = 0; i < arraysize(command_notifications); i++) {
   1075     if (command_notifications[i].command == command) {
   1076       *type = command_notifications[i].notification_type;
   1077       found = true;
   1078       break;
   1079     }
   1080   }
   1081   return found;
   1082 }
   1083 
   1084 FindInPageNotificationObserver::FindInPageNotificationObserver(
   1085     AutomationProvider* automation, TabContents* parent_tab,
   1086     bool reply_with_json, IPC::Message* reply_message)
   1087     : automation_(automation->AsWeakPtr()),
   1088       active_match_ordinal_(-1),
   1089       reply_with_json_(reply_with_json),
   1090       reply_message_(reply_message) {
   1091   registrar_.Add(this, NotificationType::FIND_RESULT_AVAILABLE,
   1092                  Source<TabContents>(parent_tab));
   1093 }
   1094 
   1095 FindInPageNotificationObserver::~FindInPageNotificationObserver() {
   1096 }
   1097 
   1098 void FindInPageNotificationObserver::Observe(
   1099     NotificationType type, const NotificationSource& source,
   1100     const NotificationDetails& details) {
   1101   Details<FindNotificationDetails> find_details(details);
   1102   if (!(find_details->final_update() && reply_message_ != NULL)) {
   1103     DVLOG(1) << "Ignoring, since we only care about the final message";
   1104     return;
   1105   }
   1106 
   1107   if (!automation_) {
   1108     delete this;
   1109     return;
   1110   }
   1111 
   1112   // We get multiple responses and one of those will contain the ordinal.
   1113   // This message comes to us before the final update is sent.
   1114   if (find_details->request_id() == kFindInPageRequestId) {
   1115     if (reply_with_json_) {
   1116       scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
   1117       return_value->SetInteger("match_count",
   1118           find_details->number_of_matches());
   1119       gfx::Rect rect = find_details->selection_rect();
   1120       // If MatchCount is > 0, then rect should not be Empty.
   1121       // We dont guard it here because we want to let the test
   1122       // code catch this invalid case if needed.
   1123       if (!rect.IsEmpty()) {
   1124         return_value->SetInteger("match_left", rect.x());
   1125         return_value->SetInteger("match_top", rect.y());
   1126         return_value->SetInteger("match_right", rect.right());
   1127         return_value->SetInteger("match_bottom", rect.bottom());
   1128       }
   1129       AutomationJSONReply(automation_, reply_message_.release())
   1130           .SendSuccess(return_value.get());
   1131       delete this;
   1132     } else {
   1133       if (find_details->active_match_ordinal() > -1) {
   1134         active_match_ordinal_ = find_details->active_match_ordinal();
   1135         AutomationMsg_Find::WriteReplyParams(reply_message_.get(),
   1136             active_match_ordinal_, find_details->number_of_matches());
   1137         automation_->Send(reply_message_.release());
   1138       }
   1139     }
   1140   }
   1141 }
   1142 
   1143 // static
   1144 const int FindInPageNotificationObserver::kFindInPageRequestId = -1;
   1145 
   1146 DomOperationObserver::DomOperationObserver() {
   1147   registrar_.Add(this, NotificationType::DOM_OPERATION_RESPONSE,
   1148                  NotificationService::AllSources());
   1149 }
   1150 
   1151 DomOperationObserver::~DomOperationObserver() {}
   1152 
   1153 void DomOperationObserver::Observe(
   1154     NotificationType type, const NotificationSource& source,
   1155     const NotificationDetails& details) {
   1156   if (NotificationType::DOM_OPERATION_RESPONSE == type) {
   1157     Details<DomOperationNotificationDetails> dom_op_details(details);
   1158     OnDomOperationCompleted(dom_op_details->json());
   1159   }
   1160 }
   1161 
   1162 DomOperationMessageSender::DomOperationMessageSender(
   1163     AutomationProvider* automation,
   1164     IPC::Message* reply_message,
   1165     bool use_json_interface)
   1166     : automation_(automation->AsWeakPtr()),
   1167       reply_message_(reply_message),
   1168       use_json_interface_(use_json_interface) {
   1169 }
   1170 
   1171 DomOperationMessageSender::~DomOperationMessageSender() {}
   1172 
   1173 void DomOperationMessageSender::OnDomOperationCompleted(
   1174     const std::string& json) {
   1175   if (automation_) {
   1176     if (use_json_interface_) {
   1177       DictionaryValue dict;
   1178       dict.SetString("result", json);
   1179       AutomationJSONReply(automation_, reply_message_.release())
   1180           .SendSuccess(&dict);
   1181     } else {
   1182       AutomationMsg_DomOperation::WriteReplyParams(reply_message_.get(), json);
   1183       automation_->Send(reply_message_.release());
   1184     }
   1185   }
   1186   delete this;
   1187 }
   1188 
   1189 DocumentPrintedNotificationObserver::DocumentPrintedNotificationObserver(
   1190     AutomationProvider* automation, IPC::Message* reply_message)
   1191     : automation_(automation->AsWeakPtr()),
   1192       success_(false),
   1193       reply_message_(reply_message) {
   1194   registrar_.Add(this, NotificationType::PRINT_JOB_EVENT,
   1195                  NotificationService::AllSources());
   1196 }
   1197 
   1198 DocumentPrintedNotificationObserver::~DocumentPrintedNotificationObserver() {
   1199   if (automation_) {
   1200     AutomationMsg_PrintNow::WriteReplyParams(reply_message_.get(), success_);
   1201     automation_->Send(reply_message_.release());
   1202   }
   1203 }
   1204 
   1205 void DocumentPrintedNotificationObserver::Observe(
   1206     NotificationType type, const NotificationSource& source,
   1207     const NotificationDetails& details) {
   1208   using namespace printing;
   1209   DCHECK(type == NotificationType::PRINT_JOB_EVENT);
   1210   switch (Details<JobEventDetails>(details)->type()) {
   1211     case JobEventDetails::JOB_DONE: {
   1212       // Succeeded.
   1213       success_ = true;
   1214       delete this;
   1215       break;
   1216     }
   1217     case JobEventDetails::USER_INIT_CANCELED:
   1218     case JobEventDetails::FAILED: {
   1219       // Failed.
   1220       delete this;
   1221       break;
   1222     }
   1223     case JobEventDetails::NEW_DOC:
   1224     case JobEventDetails::USER_INIT_DONE:
   1225     case JobEventDetails::DEFAULT_INIT_DONE:
   1226     case JobEventDetails::NEW_PAGE:
   1227     case JobEventDetails::PAGE_DONE:
   1228     case JobEventDetails::DOC_DONE:
   1229     case JobEventDetails::ALL_PAGES_REQUESTED: {
   1230       // Don't care.
   1231       break;
   1232     }
   1233     default: {
   1234       NOTREACHED();
   1235       break;
   1236     }
   1237   }
   1238 }
   1239 
   1240 MetricEventDurationObserver::MetricEventDurationObserver() {
   1241   registrar_.Add(this, NotificationType::METRIC_EVENT_DURATION,
   1242                  NotificationService::AllSources());
   1243 }
   1244 
   1245 MetricEventDurationObserver::~MetricEventDurationObserver() {}
   1246 
   1247 int MetricEventDurationObserver::GetEventDurationMs(
   1248     const std::string& event_name) {
   1249   EventDurationMap::const_iterator it = durations_.find(event_name);
   1250   if (it == durations_.end())
   1251     return -1;
   1252   return it->second;
   1253 }
   1254 
   1255 void MetricEventDurationObserver::Observe(NotificationType type,
   1256     const NotificationSource& source, const NotificationDetails& details) {
   1257   if (type != NotificationType::METRIC_EVENT_DURATION) {
   1258     NOTREACHED();
   1259     return;
   1260   }
   1261   MetricEventDurationDetails* metric_event_duration =
   1262       Details<MetricEventDurationDetails>(details).ptr();
   1263   durations_[metric_event_duration->event_name] =
   1264       metric_event_duration->duration_ms;
   1265 }
   1266 
   1267 PageTranslatedObserver::PageTranslatedObserver(AutomationProvider* automation,
   1268                                                IPC::Message* reply_message,
   1269                                                TabContents* tab_contents)
   1270   : automation_(automation->AsWeakPtr()),
   1271     reply_message_(reply_message) {
   1272   registrar_.Add(this, NotificationType::PAGE_TRANSLATED,
   1273                  Source<TabContents>(tab_contents));
   1274 }
   1275 
   1276 PageTranslatedObserver::~PageTranslatedObserver() {}
   1277 
   1278 void PageTranslatedObserver::Observe(NotificationType type,
   1279                                      const NotificationSource& source,
   1280                                      const NotificationDetails& details) {
   1281   if (!automation_) {
   1282     delete this;
   1283     return;
   1284   }
   1285 
   1286   DCHECK(type == NotificationType::PAGE_TRANSLATED);
   1287   AutomationJSONReply reply(automation_, reply_message_.release());
   1288 
   1289   PageTranslatedDetails* translated_details =
   1290       Details<PageTranslatedDetails>(details).ptr();
   1291   scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
   1292   return_value->SetBoolean(
   1293       "translation_success",
   1294       translated_details->error_type == TranslateErrors::NONE);
   1295   reply.SendSuccess(return_value.get());
   1296   delete this;
   1297 }
   1298 
   1299 TabLanguageDeterminedObserver::TabLanguageDeterminedObserver(
   1300     AutomationProvider* automation, IPC::Message* reply_message,
   1301     TabContents* tab_contents, TranslateInfoBarDelegate* translate_bar)
   1302     : automation_(automation->AsWeakPtr()),
   1303       reply_message_(reply_message),
   1304       tab_contents_(tab_contents),
   1305       translate_bar_(translate_bar) {
   1306   registrar_.Add(this, NotificationType::TAB_LANGUAGE_DETERMINED,
   1307                  Source<TabContents>(tab_contents));
   1308 }
   1309 
   1310 TabLanguageDeterminedObserver::~TabLanguageDeterminedObserver() {}
   1311 
   1312 void TabLanguageDeterminedObserver::Observe(
   1313     NotificationType type, const NotificationSource& source,
   1314     const NotificationDetails& details) {
   1315   DCHECK(type == NotificationType::TAB_LANGUAGE_DETERMINED);
   1316 
   1317   if (!automation_) {
   1318     delete this;
   1319     return;
   1320   }
   1321 
   1322   TranslateTabHelper* helper = TabContentsWrapper::GetCurrentWrapperForContents(
   1323       tab_contents_)->translate_tab_helper();
   1324   scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
   1325   return_value->SetBoolean("page_translated",
   1326                            helper->language_state().IsPageTranslated());
   1327   return_value->SetBoolean(
   1328       "can_translate_page", TranslatePrefs::CanTranslate(
   1329           automation_->profile()->GetPrefs(),
   1330           helper->language_state().original_language(),
   1331           tab_contents_->GetURL()));
   1332   return_value->SetString("original_language",
   1333                           helper->language_state().original_language());
   1334   if (translate_bar_) {
   1335     DictionaryValue* bar_info = new DictionaryValue;
   1336     std::map<TranslateInfoBarDelegate::Type, std::string> type_to_string;
   1337     type_to_string[TranslateInfoBarDelegate::BEFORE_TRANSLATE] =
   1338         "BEFORE_TRANSLATE";
   1339     type_to_string[TranslateInfoBarDelegate::TRANSLATING] =
   1340         "TRANSLATING";
   1341     type_to_string[TranslateInfoBarDelegate::AFTER_TRANSLATE] =
   1342         "AFTER_TRANSLATE";
   1343     type_to_string[TranslateInfoBarDelegate::TRANSLATION_ERROR] =
   1344         "TRANSLATION_ERROR";
   1345 
   1346     bar_info->SetBoolean("always_translate_lang_button_showing",
   1347                          translate_bar_->ShouldShowAlwaysTranslateButton());
   1348     bar_info->SetBoolean("never_translate_lang_button_showing",
   1349                          translate_bar_->ShouldShowNeverTranslateButton());
   1350     bar_info->SetString("bar_state", type_to_string[translate_bar_->type()]);
   1351     bar_info->SetString("target_lang_code",
   1352                         translate_bar_->GetTargetLanguageCode());
   1353     bar_info->SetString("original_lang_code",
   1354                         translate_bar_->GetOriginalLanguageCode());
   1355     return_value->Set("translate_bar", bar_info);
   1356   }
   1357   AutomationJSONReply(automation_, reply_message_.release())
   1358       .SendSuccess(return_value.get());
   1359   delete this;
   1360 }
   1361 
   1362 InfoBarCountObserver::InfoBarCountObserver(AutomationProvider* automation,
   1363                                            IPC::Message* reply_message,
   1364                                            TabContents* tab_contents,
   1365                                            size_t target_count)
   1366     : automation_(automation->AsWeakPtr()),
   1367       reply_message_(reply_message),
   1368       tab_contents_(tab_contents),
   1369       target_count_(target_count) {
   1370   Source<TabContents> source(tab_contents);
   1371   registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_ADDED, source);
   1372   registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_REMOVED, source);
   1373   CheckCount();
   1374 }
   1375 
   1376 InfoBarCountObserver::~InfoBarCountObserver() {}
   1377 
   1378 void InfoBarCountObserver::Observe(NotificationType type,
   1379                                    const NotificationSource& source,
   1380                                    const NotificationDetails& details) {
   1381   DCHECK(type == NotificationType::TAB_CONTENTS_INFOBAR_ADDED ||
   1382          type == NotificationType::TAB_CONTENTS_INFOBAR_REMOVED);
   1383   CheckCount();
   1384 }
   1385 
   1386 void InfoBarCountObserver::CheckCount() {
   1387   if (tab_contents_->infobar_count() != target_count_)
   1388     return;
   1389 
   1390   if (automation_) {
   1391     AutomationMsg_WaitForInfoBarCount::WriteReplyParams(reply_message_.get(),
   1392                                                         true);
   1393     automation_->Send(reply_message_.release());
   1394   }
   1395   delete this;
   1396 }
   1397 
   1398 AutomationProviderBookmarkModelObserver::
   1399 AutomationProviderBookmarkModelObserver(
   1400     AutomationProvider* provider,
   1401     IPC::Message* reply_message,
   1402     BookmarkModel* model)
   1403     : automation_provider_(provider->AsWeakPtr()),
   1404       reply_message_(reply_message),
   1405       model_(model) {
   1406   model_->AddObserver(this);
   1407 }
   1408 
   1409 AutomationProviderBookmarkModelObserver::
   1410     ~AutomationProviderBookmarkModelObserver() {
   1411   model_->RemoveObserver(this);
   1412 }
   1413 
   1414 void AutomationProviderBookmarkModelObserver::Loaded(BookmarkModel* model) {
   1415   ReplyAndDelete(true);
   1416 }
   1417 
   1418 void AutomationProviderBookmarkModelObserver::BookmarkModelBeingDeleted(
   1419     BookmarkModel* model) {
   1420   ReplyAndDelete(false);
   1421 }
   1422 
   1423 void AutomationProviderBookmarkModelObserver::ReplyAndDelete(bool success) {
   1424   if (automation_provider_) {
   1425     AutomationMsg_WaitForBookmarkModelToLoad::WriteReplyParams(
   1426         reply_message_.get(), success);
   1427     automation_provider_->Send(reply_message_.release());
   1428   }
   1429   delete this;
   1430 }
   1431 
   1432 AutomationProviderDownloadItemObserver::AutomationProviderDownloadItemObserver(
   1433     AutomationProvider* provider,
   1434     IPC::Message* reply_message,
   1435     int downloads)
   1436     : provider_(provider->AsWeakPtr()),
   1437       reply_message_(reply_message),
   1438       downloads_(downloads),
   1439       interrupted_(false) {
   1440 }
   1441 
   1442 AutomationProviderDownloadItemObserver::
   1443     ~AutomationProviderDownloadItemObserver() {}
   1444 
   1445 void AutomationProviderDownloadItemObserver::OnDownloadUpdated(
   1446     DownloadItem* download) {
   1447   interrupted_ |= download->IsInterrupted();
   1448   // If any download was interrupted, on the next update each outstanding
   1449   // download is cancelled.
   1450   if (interrupted_) {
   1451     // |Cancel()| does nothing if |download| is already interrupted.
   1452     download->Cancel(true);
   1453     RemoveAndCleanupOnLastEntry(download);
   1454   }
   1455 
   1456   if (download->IsComplete())
   1457     RemoveAndCleanupOnLastEntry(download);
   1458 }
   1459 
   1460 // We don't want to send multiple messages, as the behavior is undefined.
   1461 // Set |interrupted_| on error, and on the last download completed/
   1462 // interrupted, send either an error or a success message.
   1463 void AutomationProviderDownloadItemObserver::RemoveAndCleanupOnLastEntry(
   1464     DownloadItem* download) {
   1465   // Forget about the download.
   1466   download->RemoveObserver(this);
   1467   if (--downloads_ == 0) {
   1468     if (provider_) {
   1469       if (interrupted_) {
   1470         AutomationJSONReply(provider_, reply_message_.release()).SendError(
   1471             "Download Interrupted");
   1472       } else {
   1473         AutomationJSONReply(provider_, reply_message_.release()).SendSuccess(
   1474             NULL);
   1475       }
   1476     }
   1477     delete this;
   1478   }
   1479 }
   1480 
   1481 void AutomationProviderDownloadItemObserver::OnDownloadOpened(
   1482     DownloadItem* download) {
   1483 }
   1484 
   1485 AutomationProviderDownloadUpdatedObserver::
   1486 AutomationProviderDownloadUpdatedObserver(
   1487     AutomationProvider* provider,
   1488     IPC::Message* reply_message,
   1489     bool wait_for_open)
   1490     : provider_(provider->AsWeakPtr()),
   1491       reply_message_(reply_message),
   1492       wait_for_open_(wait_for_open) {
   1493 }
   1494 
   1495 AutomationProviderDownloadUpdatedObserver::
   1496     ~AutomationProviderDownloadUpdatedObserver() {}
   1497 
   1498 void AutomationProviderDownloadUpdatedObserver::OnDownloadUpdated(
   1499     DownloadItem* download) {
   1500   // If this observer is watching for open, only send the reply if the download
   1501   // has been auto-opened.
   1502   if (wait_for_open_ && !download->auto_opened())
   1503     return;
   1504 
   1505   download->RemoveObserver(this);
   1506   scoped_ptr<DictionaryValue> return_value(
   1507       provider_->GetDictionaryFromDownloadItem(download));
   1508 
   1509   if (provider_) {
   1510     AutomationJSONReply(provider_, reply_message_.release()).SendSuccess(
   1511         return_value.get());
   1512   }
   1513   delete this;
   1514 }
   1515 
   1516 void AutomationProviderDownloadUpdatedObserver::OnDownloadOpened(
   1517     DownloadItem* download) {
   1518   download->RemoveObserver(this);
   1519   scoped_ptr<DictionaryValue> return_value(
   1520       provider_->GetDictionaryFromDownloadItem(download));
   1521 
   1522   if (provider_) {
   1523     AutomationJSONReply(provider_, reply_message_.release()).SendSuccess(
   1524         return_value.get());
   1525   }
   1526   delete this;
   1527 }
   1528 
   1529 AutomationProviderDownloadModelChangedObserver::
   1530 AutomationProviderDownloadModelChangedObserver(
   1531     AutomationProvider* provider,
   1532     IPC::Message* reply_message,
   1533     DownloadManager* download_manager)
   1534     : provider_(provider->AsWeakPtr()),
   1535       reply_message_(reply_message),
   1536       download_manager_(download_manager) {
   1537 }
   1538 
   1539 AutomationProviderDownloadModelChangedObserver::
   1540     ~AutomationProviderDownloadModelChangedObserver() {}
   1541 
   1542 void AutomationProviderDownloadModelChangedObserver::ModelChanged() {
   1543   download_manager_->RemoveObserver(this);
   1544 
   1545   if (provider_)
   1546     AutomationJSONReply(provider_, reply_message_.release()).SendSuccess(NULL);
   1547   delete this;
   1548 }
   1549 
   1550 AutomationProviderSearchEngineObserver::AutomationProviderSearchEngineObserver(
   1551     AutomationProvider* provider,
   1552     IPC::Message* reply_message)
   1553     : provider_(provider->AsWeakPtr()),
   1554       reply_message_(reply_message) {
   1555 }
   1556 
   1557 AutomationProviderSearchEngineObserver::
   1558     ~AutomationProviderSearchEngineObserver() {}
   1559 
   1560 void AutomationProviderSearchEngineObserver::OnTemplateURLModelChanged() {
   1561   TemplateURLModel* url_model = provider_->profile()->GetTemplateURLModel();
   1562   url_model->RemoveObserver(this);
   1563 
   1564   if (provider_)
   1565     AutomationJSONReply(provider_, reply_message_.release()).SendSuccess(NULL);
   1566   delete this;
   1567 }
   1568 
   1569 AutomationProviderHistoryObserver::AutomationProviderHistoryObserver(
   1570     AutomationProvider* provider,
   1571     IPC::Message* reply_message)
   1572     : provider_(provider->AsWeakPtr()),
   1573       reply_message_(reply_message) {
   1574 }
   1575 
   1576 AutomationProviderHistoryObserver::~AutomationProviderHistoryObserver() {}
   1577 
   1578 void AutomationProviderHistoryObserver::HistoryQueryComplete(
   1579     HistoryService::Handle request_handle,
   1580     history::QueryResults* results) {
   1581   if (!provider_) {
   1582     delete this;
   1583     return;
   1584   }
   1585 
   1586   scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
   1587 
   1588   ListValue* history_list = new ListValue;
   1589   for (size_t i = 0; i < results->size(); ++i) {
   1590     DictionaryValue* page_value = new DictionaryValue;
   1591     history::URLResult const &page = (*results)[i];
   1592     page_value->SetString("title", page.title());
   1593     page_value->SetString("url", page.url().spec());
   1594     page_value->SetDouble("time",
   1595                           static_cast<double>(page.visit_time().ToDoubleT()));
   1596     page_value->SetString("snippet", page.snippet().text());
   1597     page_value->SetBoolean(
   1598         "starred",
   1599         provider_->profile()->GetBookmarkModel()->IsBookmarked(page.url()));
   1600     history_list->Append(page_value);
   1601   }
   1602 
   1603   return_value->Set("history", history_list);
   1604   // Return history info.
   1605   AutomationJSONReply reply(provider_, reply_message_.release());
   1606   reply.SendSuccess(return_value.get());
   1607   delete this;
   1608 }
   1609 
   1610 AutomationProviderImportSettingsObserver::
   1611 AutomationProviderImportSettingsObserver(
   1612     AutomationProvider* provider,
   1613     IPC::Message* reply_message)
   1614     : provider_(provider->AsWeakPtr()),
   1615       reply_message_(reply_message) {
   1616 }
   1617 
   1618 AutomationProviderImportSettingsObserver::
   1619     ~AutomationProviderImportSettingsObserver() {}
   1620 
   1621 void AutomationProviderImportSettingsObserver::ImportStarted() {
   1622 }
   1623 
   1624 void AutomationProviderImportSettingsObserver::ImportItemStarted(
   1625     importer::ImportItem item) {
   1626 }
   1627 
   1628 void AutomationProviderImportSettingsObserver::ImportItemEnded(
   1629     importer::ImportItem item) {
   1630 }
   1631 
   1632 void AutomationProviderImportSettingsObserver::ImportEnded() {
   1633   if (provider_)
   1634     AutomationJSONReply(provider_, reply_message_.release()).SendSuccess(NULL);
   1635   delete this;
   1636 }
   1637 
   1638 AutomationProviderGetPasswordsObserver::AutomationProviderGetPasswordsObserver(
   1639     AutomationProvider* provider,
   1640     IPC::Message* reply_message)
   1641     : provider_(provider->AsWeakPtr()),
   1642       reply_message_(reply_message) {
   1643 }
   1644 
   1645 AutomationProviderGetPasswordsObserver::
   1646     ~AutomationProviderGetPasswordsObserver() {}
   1647 
   1648 void AutomationProviderGetPasswordsObserver::OnPasswordStoreRequestDone(
   1649     CancelableRequestProvider::Handle handle,
   1650     const std::vector<webkit_glue::PasswordForm*>& result) {
   1651   if (!provider_) {
   1652     delete this;
   1653     return;
   1654   }
   1655 
   1656   scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
   1657 
   1658   ListValue* passwords = new ListValue;
   1659   for (std::vector<webkit_glue::PasswordForm*>::const_iterator it =
   1660           result.begin(); it != result.end(); ++it) {
   1661     DictionaryValue* password_val = new DictionaryValue;
   1662     webkit_glue::PasswordForm* password_form = *it;
   1663     password_val->SetString("username_value", password_form->username_value);
   1664     password_val->SetString("password_value", password_form->password_value);
   1665     password_val->SetString("signon_realm", password_form->signon_realm);
   1666     password_val->SetDouble(
   1667         "time", static_cast<double>(password_form->date_created.ToDoubleT()));
   1668     password_val->SetString("origin_url", password_form->origin.spec());
   1669     password_val->SetString("username_element",
   1670                             password_form->username_element);
   1671     password_val->SetString("password_element",
   1672                             password_form->password_element);
   1673     password_val->SetString("submit_element",
   1674                                      password_form->submit_element);
   1675     password_val->SetString("action_target", password_form->action.spec());
   1676     password_val->SetBoolean("blacklist", password_form->blacklisted_by_user);
   1677     passwords->Append(password_val);
   1678   }
   1679 
   1680   return_value->Set("passwords", passwords);
   1681   AutomationJSONReply(provider_, reply_message_.release()).SendSuccess(
   1682       return_value.get());
   1683   delete this;
   1684 }
   1685 
   1686 AutomationProviderBrowsingDataObserver::AutomationProviderBrowsingDataObserver(
   1687     AutomationProvider* provider,
   1688     IPC::Message* reply_message)
   1689     : provider_(provider->AsWeakPtr()),
   1690       reply_message_(reply_message) {
   1691 }
   1692 
   1693 AutomationProviderBrowsingDataObserver::
   1694     ~AutomationProviderBrowsingDataObserver() {}
   1695 
   1696 void AutomationProviderBrowsingDataObserver::OnBrowsingDataRemoverDone() {
   1697   if (provider_)
   1698     AutomationJSONReply(provider_, reply_message_.release()).SendSuccess(NULL);
   1699   delete this;
   1700 }
   1701 
   1702 OmniboxAcceptNotificationObserver::OmniboxAcceptNotificationObserver(
   1703     NavigationController* controller,
   1704     AutomationProvider* automation,
   1705     IPC::Message* reply_message)
   1706     : automation_(automation->AsWeakPtr()),
   1707       reply_message_(reply_message),
   1708       controller_(controller) {
   1709   Source<NavigationController> source(controller_);
   1710   registrar_.Add(this, NotificationType::LOAD_STOP, source);
   1711   // Pages requiring auth don't send LOAD_STOP.
   1712   registrar_.Add(this, NotificationType::AUTH_NEEDED, source);
   1713 }
   1714 
   1715 OmniboxAcceptNotificationObserver::~OmniboxAcceptNotificationObserver() {
   1716 }
   1717 
   1718 void OmniboxAcceptNotificationObserver::Observe(
   1719     NotificationType type,
   1720     const NotificationSource& source,
   1721     const NotificationDetails& details) {
   1722   if (type == NotificationType::LOAD_STOP ||
   1723       type == NotificationType::AUTH_NEEDED) {
   1724     if (automation_) {
   1725       AutomationJSONReply(automation_,
   1726                           reply_message_.release()).SendSuccess(NULL);
   1727     }
   1728     delete this;
   1729   } else {
   1730     NOTREACHED();
   1731   }
   1732 }
   1733 
   1734 SavePackageNotificationObserver::SavePackageNotificationObserver(
   1735     SavePackage* save_package,
   1736     AutomationProvider* automation,
   1737     IPC::Message* reply_message)
   1738     : automation_(automation->AsWeakPtr()),
   1739       reply_message_(reply_message) {
   1740   Source<SavePackage> source(save_package);
   1741   registrar_.Add(this, NotificationType::SAVE_PACKAGE_SUCCESSFULLY_FINISHED,
   1742                  source);
   1743 }
   1744 
   1745 SavePackageNotificationObserver::~SavePackageNotificationObserver() {}
   1746 
   1747 void SavePackageNotificationObserver::Observe(
   1748     NotificationType type,
   1749     const NotificationSource& source,
   1750     const NotificationDetails& details) {
   1751   if (type == NotificationType::SAVE_PACKAGE_SUCCESSFULLY_FINISHED) {
   1752     if (automation_) {
   1753       AutomationJSONReply(automation_,
   1754                           reply_message_.release()).SendSuccess(NULL);
   1755     }
   1756     delete this;
   1757   } else {
   1758     NOTREACHED();
   1759   }
   1760 }
   1761 
   1762 PageSnapshotTaker::PageSnapshotTaker(AutomationProvider* automation,
   1763                                      IPC::Message* reply_message,
   1764                                      RenderViewHost* render_view,
   1765                                      const FilePath& path)
   1766     : automation_(automation->AsWeakPtr()),
   1767       reply_message_(reply_message),
   1768       render_view_(render_view),
   1769       image_path_(path),
   1770       received_width_(false) {}
   1771 
   1772 PageSnapshotTaker::~PageSnapshotTaker() {}
   1773 
   1774 void PageSnapshotTaker::Start() {
   1775   ExecuteScript(L"window.domAutomationController.send(document.width);");
   1776 }
   1777 
   1778 void PageSnapshotTaker::OnDomOperationCompleted(const std::string& json) {
   1779   int dimension;
   1780   if (!base::StringToInt(json, &dimension)) {
   1781     LOG(ERROR) << "Could not parse received dimensions: " << json;
   1782     SendMessage(false);
   1783   } else if (!received_width_) {
   1784     received_width_ = true;
   1785     entire_page_size_.set_width(dimension);
   1786 
   1787     ExecuteScript(L"window.domAutomationController.send(document.height);");
   1788   } else {
   1789     entire_page_size_.set_height(dimension);
   1790 
   1791     ThumbnailGenerator* generator =
   1792         g_browser_process->GetThumbnailGenerator();
   1793     ThumbnailGenerator::ThumbnailReadyCallback* callback =
   1794         NewCallback(this, &PageSnapshotTaker::OnSnapshotTaken);
   1795     // Don't actually start the thumbnail generator, this leads to crashes on
   1796     // Mac, crbug.com/62986. Instead, just hook the generator to the
   1797     // RenderViewHost manually.
   1798 
   1799     generator->MonitorRenderer(render_view_, true);
   1800     generator->AskForSnapshot(render_view_, false, callback,
   1801                               entire_page_size_, entire_page_size_);
   1802   }
   1803 }
   1804 
   1805 void PageSnapshotTaker::OnSnapshotTaken(const SkBitmap& bitmap) {
   1806   base::ThreadRestrictions::ScopedAllowIO allow_io;
   1807   std::vector<unsigned char> png_data;
   1808   gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, true, &png_data);
   1809   int bytes_written = file_util::WriteFile(image_path_,
   1810       reinterpret_cast<char*>(&png_data[0]), png_data.size());
   1811   SendMessage(bytes_written == static_cast<int>(png_data.size()));
   1812 }
   1813 
   1814 void PageSnapshotTaker::ExecuteScript(const std::wstring& javascript) {
   1815   std::wstring set_automation_id;
   1816   base::SStringPrintf(
   1817       &set_automation_id,
   1818       L"window.domAutomationController.setAutomationId(%d);",
   1819       reply_message_->routing_id());
   1820 
   1821   render_view_->ExecuteJavascriptInWebFrame(string16(),
   1822                                             WideToUTF16Hack(set_automation_id));
   1823   render_view_->ExecuteJavascriptInWebFrame(string16(),
   1824                                             WideToUTF16Hack(javascript));
   1825 }
   1826 
   1827 void PageSnapshotTaker::SendMessage(bool success) {
   1828   if (automation_) {
   1829     if (success) {
   1830       AutomationJSONReply(automation_, reply_message_.release())
   1831           .SendSuccess(NULL);
   1832     } else {
   1833       AutomationJSONReply(automation_, reply_message_.release())
   1834           .SendError("Failed to take snapshot of page");
   1835     }
   1836   }
   1837   delete this;
   1838 }
   1839 
   1840 namespace {
   1841 
   1842 // Returns a vector of dictionaries containing information about installed apps,
   1843 // as identified from a given list of extensions.  The caller takes ownership
   1844 // of the created vector.
   1845 std::vector<DictionaryValue*>* GetAppInfoFromExtensions(
   1846     const ExtensionList* extensions,
   1847     ExtensionPrefs* ext_prefs) {
   1848   std::vector<DictionaryValue*>* apps_list =
   1849       new std::vector<DictionaryValue*>();
   1850   for (ExtensionList::const_iterator ext = extensions->begin();
   1851        ext != extensions->end(); ++ext) {
   1852     // Only return information about extensions that are actually apps.
   1853     if ((*ext)->is_app()) {
   1854       DictionaryValue* app_info = new DictionaryValue();
   1855       AppLauncherHandler::CreateAppInfo(*ext, ext_prefs, app_info);
   1856       app_info->SetBoolean("is_component_extension",
   1857                            (*ext)->location() == Extension::COMPONENT);
   1858 
   1859       // Convert the launch_type integer into a more descriptive string.
   1860       int launch_type;
   1861       app_info->GetInteger("launch_type", &launch_type);
   1862       if (launch_type == ExtensionPrefs::LAUNCH_PINNED) {
   1863         app_info->SetString("launch_type", "pinned");
   1864       } else if (launch_type == ExtensionPrefs::LAUNCH_REGULAR) {
   1865         app_info->SetString("launch_type", "regular");
   1866       } else if (launch_type == ExtensionPrefs::LAUNCH_FULLSCREEN) {
   1867         app_info->SetString("launch_type", "fullscreen");
   1868       } else if (launch_type == ExtensionPrefs::LAUNCH_WINDOW) {
   1869         app_info->SetString("launch_type", "window");
   1870       } else {
   1871         app_info->SetString("launch_type", "unknown");
   1872       }
   1873 
   1874       apps_list->push_back(app_info);
   1875     }
   1876   }
   1877   return apps_list;
   1878 }
   1879 
   1880 }  // namespace
   1881 
   1882 NTPInfoObserver::NTPInfoObserver(
   1883     AutomationProvider* automation,
   1884     IPC::Message* reply_message,
   1885     CancelableRequestConsumer* consumer)
   1886     : automation_(automation->AsWeakPtr()),
   1887       reply_message_(reply_message),
   1888       consumer_(consumer),
   1889       request_(0),
   1890       ntp_info_(new DictionaryValue) {
   1891   top_sites_ = automation_->profile()->GetTopSites();
   1892   if (!top_sites_) {
   1893     AutomationJSONReply(automation_, reply_message_.release())
   1894         .SendError("Profile does not have service for querying the top sites.");
   1895     return;
   1896   }
   1897   TabRestoreService* service = automation_->profile()->GetTabRestoreService();
   1898   if (!service) {
   1899     AutomationJSONReply(automation_, reply_message_.release())
   1900         .SendError("No TabRestoreService.");
   1901     return;
   1902   }
   1903 
   1904   // Collect information about the apps in the new tab page.
   1905   ExtensionService* ext_service = automation_->profile()->GetExtensionService();
   1906   if (!ext_service) {
   1907     AutomationJSONReply(automation_, reply_message_.release())
   1908         .SendError("No ExtensionService.");
   1909     return;
   1910   }
   1911   // Process enabled extensions.
   1912   ExtensionPrefs* ext_prefs = ext_service->extension_prefs();
   1913   ListValue* apps_list = new ListValue();
   1914   const ExtensionList* extensions = ext_service->extensions();
   1915   std::vector<DictionaryValue*>* enabled_apps = GetAppInfoFromExtensions(
   1916       extensions, ext_prefs);
   1917   for (std::vector<DictionaryValue*>::const_iterator app =
   1918        enabled_apps->begin(); app != enabled_apps->end(); ++app) {
   1919     (*app)->SetBoolean("is_disabled", false);
   1920     apps_list->Append(*app);
   1921   }
   1922   delete enabled_apps;
   1923   // Process disabled extensions.
   1924   const ExtensionList* disabled_extensions = ext_service->disabled_extensions();
   1925   std::vector<DictionaryValue*>* disabled_apps = GetAppInfoFromExtensions(
   1926       disabled_extensions, ext_prefs);
   1927   for (std::vector<DictionaryValue*>::const_iterator app =
   1928        disabled_apps->begin(); app != disabled_apps->end(); ++app) {
   1929     (*app)->SetBoolean("is_disabled", true);
   1930     apps_list->Append(*app);
   1931   }
   1932   delete disabled_apps;
   1933   ntp_info_->Set("apps", apps_list);
   1934 
   1935   // Get the info that would be displayed in the recently closed section.
   1936   ListValue* recently_closed_list = new ListValue;
   1937   NewTabUI::AddRecentlyClosedEntries(service->entries(),
   1938                                      recently_closed_list);
   1939   ntp_info_->Set("recently_closed", recently_closed_list);
   1940 
   1941   // Add default site URLs.
   1942   ListValue* default_sites_list = new ListValue;
   1943   std::vector<GURL> urls = MostVisitedHandler::GetPrePopulatedUrls();
   1944   for (size_t i = 0; i < urls.size(); ++i) {
   1945     default_sites_list->Append(Value::CreateStringValue(
   1946         urls[i].possibly_invalid_spec()));
   1947   }
   1948   ntp_info_->Set("default_sites", default_sites_list);
   1949 
   1950   registrar_.Add(this, NotificationType::TOP_SITES_UPDATED,
   1951                  Source<history::TopSites>(top_sites_));
   1952   if (top_sites_->loaded()) {
   1953     OnTopSitesLoaded();
   1954   } else {
   1955     registrar_.Add(this, NotificationType::TOP_SITES_LOADED,
   1956                    Source<Profile>(automation_->profile()));
   1957   }
   1958 }
   1959 
   1960 NTPInfoObserver::~NTPInfoObserver() {}
   1961 
   1962 void NTPInfoObserver::Observe(NotificationType type,
   1963                               const NotificationSource& source,
   1964                               const NotificationDetails& details) {
   1965   if (type == NotificationType::TOP_SITES_LOADED) {
   1966     OnTopSitesLoaded();
   1967   } else if (type == NotificationType::TOP_SITES_UPDATED) {
   1968     Details<CancelableRequestProvider::Handle> request_details(details);
   1969     if (request_ == *request_details.ptr()) {
   1970       top_sites_->GetMostVisitedURLs(
   1971           consumer_,
   1972           NewCallback(this, &NTPInfoObserver::OnTopSitesReceived));
   1973     }
   1974   }
   1975 }
   1976 
   1977 void NTPInfoObserver::OnTopSitesLoaded() {
   1978   request_ = top_sites_->StartQueryForMostVisited();
   1979 }
   1980 
   1981 void NTPInfoObserver::OnTopSitesReceived(
   1982     const history::MostVisitedURLList& visited_list) {
   1983   if (!automation_) {
   1984     delete this;
   1985     return;
   1986   }
   1987 
   1988   ListValue* list_value = new ListValue;
   1989   for (size_t i = 0; i < visited_list.size(); ++i) {
   1990     const history::MostVisitedURL& visited = visited_list[i];
   1991     if (visited.url.spec().empty())
   1992       break;  // This is the signal that there are no more real visited sites.
   1993     DictionaryValue* dict = new DictionaryValue;
   1994     dict->SetString("url", visited.url.spec());
   1995     dict->SetString("title", visited.title);
   1996     dict->SetBoolean("is_pinned", top_sites_->IsURLPinned(visited.url));
   1997     list_value->Append(dict);
   1998   }
   1999   ntp_info_->Set("most_visited", list_value);
   2000   AutomationJSONReply(automation_,
   2001                       reply_message_.release()).SendSuccess(ntp_info_.get());
   2002   delete this;
   2003 }
   2004 
   2005 AppLaunchObserver::AppLaunchObserver(
   2006     NavigationController* controller,
   2007     AutomationProvider* automation,
   2008     IPC::Message* reply_message,
   2009     extension_misc::LaunchContainer launch_container)
   2010     : controller_(controller),
   2011       automation_(automation->AsWeakPtr()),
   2012       reply_message_(reply_message),
   2013       launch_container_(launch_container),
   2014       new_window_id_(extension_misc::kUnknownWindowId) {
   2015   if (launch_container_ == extension_misc::LAUNCH_TAB) {
   2016     // Need to wait for the currently-active tab to reload.
   2017     Source<NavigationController> source(controller_);
   2018     registrar_.Add(this, NotificationType::LOAD_STOP, source);
   2019   } else {
   2020     // Need to wait for a new tab in a new window to load.
   2021     registrar_.Add(this, NotificationType::LOAD_STOP,
   2022                    NotificationService::AllSources());
   2023     registrar_.Add(this, NotificationType::BROWSER_WINDOW_READY,
   2024                    NotificationService::AllSources());
   2025   }
   2026 }
   2027 
   2028 AppLaunchObserver::~AppLaunchObserver() {}
   2029 
   2030 void AppLaunchObserver::Observe(NotificationType type,
   2031                                 const NotificationSource& source,
   2032                                 const NotificationDetails& details) {
   2033   if (type.value == NotificationType::LOAD_STOP) {
   2034     if (launch_container_ == extension_misc::LAUNCH_TAB) {
   2035       // The app has been launched in the new tab.
   2036       if (automation_) {
   2037         AutomationJSONReply(automation_,
   2038                             reply_message_.release()).SendSuccess(NULL);
   2039       }
   2040       delete this;
   2041       return;
   2042     } else {
   2043       // The app has launched only if the loaded tab is in the new window.
   2044       int window_id = Source<NavigationController>(source)->window_id().id();
   2045       if (window_id == new_window_id_) {
   2046         if (automation_) {
   2047           AutomationJSONReply(automation_,
   2048                               reply_message_.release()).SendSuccess(NULL);
   2049         }
   2050         delete this;
   2051         return;
   2052       }
   2053     }
   2054   } else if (type.value == NotificationType::BROWSER_WINDOW_READY) {
   2055     new_window_id_ = ExtensionTabUtil::GetWindowId(
   2056         Source<Browser>(source).ptr());
   2057   } else {
   2058     NOTREACHED();
   2059   }
   2060 }
   2061 
   2062 AutocompleteEditFocusedObserver::AutocompleteEditFocusedObserver(
   2063     AutomationProvider* automation,
   2064     AutocompleteEditModel* autocomplete_edit,
   2065     IPC::Message* reply_message)
   2066     : automation_(automation->AsWeakPtr()),
   2067       reply_message_(reply_message),
   2068       autocomplete_edit_model_(autocomplete_edit) {
   2069   Source<AutocompleteEditModel> source(autocomplete_edit);
   2070   registrar_.Add(this, NotificationType::AUTOCOMPLETE_EDIT_FOCUSED, source);
   2071 }
   2072 
   2073 AutocompleteEditFocusedObserver::~AutocompleteEditFocusedObserver() {}
   2074 
   2075 void AutocompleteEditFocusedObserver::Observe(
   2076     NotificationType type,
   2077     const NotificationSource& source,
   2078     const NotificationDetails& details) {
   2079   DCHECK(type == NotificationType::AUTOCOMPLETE_EDIT_FOCUSED);
   2080   if (automation_) {
   2081     AutomationMsg_WaitForAutocompleteEditFocus::WriteReplyParams(
   2082         reply_message_.get(), true);
   2083     automation_->Send(reply_message_.release());
   2084   }
   2085   delete this;
   2086 }
   2087 
   2088 namespace {
   2089 
   2090 // Returns whether the notification's host has a non-null process handle.
   2091 bool IsNotificationProcessReady(Balloon* balloon) {
   2092   return balloon->view() &&
   2093          balloon->view()->GetHost() &&
   2094          balloon->view()->GetHost()->render_view_host() &&
   2095          balloon->view()->GetHost()->render_view_host()->process()->GetHandle();
   2096 }
   2097 
   2098 // Returns whether all active notifications have an associated process ID.
   2099 bool AreActiveNotificationProcessesReady() {
   2100   NotificationUIManager* manager = g_browser_process->notification_ui_manager();
   2101   const BalloonCollection::Balloons& balloons =
   2102       manager->balloon_collection()->GetActiveBalloons();
   2103   BalloonCollection::Balloons::const_iterator iter;
   2104   for (iter = balloons.begin(); iter != balloons.end(); ++iter) {
   2105     if (!IsNotificationProcessReady(*iter))
   2106       return false;
   2107   }
   2108   return true;
   2109 }
   2110 
   2111 }  // namespace
   2112 
   2113 GetActiveNotificationsObserver::GetActiveNotificationsObserver(
   2114     AutomationProvider* automation,
   2115     IPC::Message* reply_message)
   2116     : reply_(automation, reply_message) {
   2117   if (AreActiveNotificationProcessesReady()) {
   2118     SendMessage();
   2119   } else {
   2120     registrar_.Add(this, NotificationType::RENDERER_PROCESS_CREATED,
   2121                    NotificationService::AllSources());
   2122   }
   2123 }
   2124 
   2125 GetActiveNotificationsObserver::~GetActiveNotificationsObserver() {}
   2126 
   2127 void GetActiveNotificationsObserver::Observe(
   2128     NotificationType type,
   2129     const NotificationSource& source,
   2130     const NotificationDetails& details) {
   2131   if (AreActiveNotificationProcessesReady())
   2132     SendMessage();
   2133 }
   2134 
   2135 void GetActiveNotificationsObserver::SendMessage() {
   2136   NotificationUIManager* manager =
   2137       g_browser_process->notification_ui_manager();
   2138   const BalloonCollection::Balloons& balloons =
   2139       manager->balloon_collection()->GetActiveBalloons();
   2140   scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
   2141   ListValue* list = new ListValue;
   2142   return_value->Set("notifications", list);
   2143   BalloonCollection::Balloons::const_iterator iter;
   2144   for (iter = balloons.begin(); iter != balloons.end(); ++iter) {
   2145     const Notification& notification = (*iter)->notification();
   2146     DictionaryValue* balloon = new DictionaryValue;
   2147     balloon->SetString("content_url", notification.content_url().spec());
   2148     balloon->SetString("origin_url", notification.origin_url().spec());
   2149     balloon->SetString("display_source", notification.display_source());
   2150     BalloonView* view = (*iter)->view();
   2151     balloon->SetInteger("pid", base::GetProcId(
   2152         view->GetHost()->render_view_host()->process()->GetHandle()));
   2153     list->Append(balloon);
   2154   }
   2155   reply_.SendSuccess(return_value.get());
   2156   delete this;
   2157 }
   2158 
   2159 OnNotificationBalloonCountObserver::OnNotificationBalloonCountObserver(
   2160     AutomationProvider* provider,
   2161     IPC::Message* reply_message,
   2162     BalloonCollection* collection,
   2163     int count)
   2164     : reply_(provider, reply_message),
   2165       collection_(collection),
   2166       count_(count) {
   2167   collection->set_on_collection_changed_callback(NewCallback(
   2168       this, &OnNotificationBalloonCountObserver::OnBalloonCollectionChanged));
   2169 }
   2170 
   2171 void OnNotificationBalloonCountObserver::OnBalloonCollectionChanged() {
   2172   if (static_cast<int>(collection_->GetActiveBalloons().size()) == count_) {
   2173     collection_->set_on_collection_changed_callback(NULL);
   2174     reply_.SendSuccess(NULL);
   2175     delete this;
   2176   }
   2177 }
   2178 
   2179 RendererProcessClosedObserver::RendererProcessClosedObserver(
   2180     AutomationProvider* automation,
   2181     IPC::Message* reply_message)
   2182     : automation_(automation->AsWeakPtr()),
   2183       reply_message_(reply_message) {
   2184   registrar_.Add(this, NotificationType::RENDERER_PROCESS_CLOSED,
   2185                  NotificationService::AllSources());
   2186 }
   2187 
   2188 RendererProcessClosedObserver::~RendererProcessClosedObserver() {}
   2189 
   2190 void RendererProcessClosedObserver::Observe(
   2191     NotificationType type,
   2192     const NotificationSource& source,
   2193     const NotificationDetails& details) {
   2194   if (automation_) {
   2195     AutomationJSONReply(automation_,
   2196                         reply_message_.release()).SendSuccess(NULL);
   2197   }
   2198   delete this;
   2199 }
   2200 
   2201 InputEventAckNotificationObserver::InputEventAckNotificationObserver(
   2202     AutomationProvider* automation,
   2203     IPC::Message* reply_message,
   2204     int event_type)
   2205     : automation_(automation->AsWeakPtr()),
   2206       reply_message_(reply_message),
   2207       event_type_(event_type) {
   2208   registrar_.Add(
   2209       this, NotificationType::RENDER_WIDGET_HOST_DID_RECEIVE_INPUT_EVENT_ACK,
   2210       NotificationService::AllSources());
   2211 }
   2212 
   2213 InputEventAckNotificationObserver::~InputEventAckNotificationObserver() {}
   2214 
   2215 void InputEventAckNotificationObserver::Observe(
   2216     NotificationType type,
   2217     const NotificationSource& source,
   2218     const NotificationDetails& details) {
   2219   Details<int> request_details(details);
   2220   if (event_type_ == *request_details.ptr()) {
   2221     if (automation_) {
   2222       AutomationJSONReply(automation_,
   2223                           reply_message_.release()).SendSuccess(NULL);
   2224     }
   2225     delete this;
   2226   } else {
   2227     LOG(WARNING) << "Ignoring unexpected event types.";
   2228   }
   2229 }
   2230 
   2231 AllTabsStoppedLoadingObserver::AllTabsStoppedLoadingObserver(
   2232     AutomationProvider* automation,
   2233     IPC::Message* reply_message)
   2234     : automation_(automation->AsWeakPtr()),
   2235       reply_message_(reply_message) {
   2236   for (BrowserList::const_iterator iter = BrowserList::begin();
   2237        iter != BrowserList::end();
   2238        ++iter) {
   2239     Browser* browser = *iter;
   2240     for (int i = 0; i < browser->tab_count(); ++i) {
   2241       TabContentsWrapper* contents_wrapper =
   2242           browser->GetTabContentsWrapperAt(i);
   2243       StartObserving(contents_wrapper->automation_tab_helper());
   2244       if (contents_wrapper->automation_tab_helper()->has_pending_loads())
   2245         pending_tabs_.insert(contents_wrapper->tab_contents());
   2246     }
   2247   }
   2248   CheckIfNoMorePendingLoads();
   2249 }
   2250 
   2251 AllTabsStoppedLoadingObserver::~AllTabsStoppedLoadingObserver() {
   2252 }
   2253 
   2254 void AllTabsStoppedLoadingObserver::OnFirstPendingLoad(
   2255     TabContents* tab_contents) {
   2256   pending_tabs_.insert(tab_contents);
   2257 }
   2258 
   2259 void AllTabsStoppedLoadingObserver::OnNoMorePendingLoads(
   2260     TabContents* tab_contents) {
   2261   if (!automation_) {
   2262     delete this;
   2263     return;
   2264   }
   2265 
   2266   TabSet::iterator iter = pending_tabs_.find(tab_contents);
   2267   if (iter == pending_tabs_.end()) {
   2268     LOG(ERROR) << "Received OnNoMorePendingLoads for tab without "
   2269                << "OnFirstPendingLoad.";
   2270     return;
   2271   }
   2272   pending_tabs_.erase(iter);
   2273   CheckIfNoMorePendingLoads();
   2274 }
   2275 
   2276 void AllTabsStoppedLoadingObserver::CheckIfNoMorePendingLoads() {
   2277   if (!automation_) {
   2278     delete this;
   2279     return;
   2280   }
   2281 
   2282   if (pending_tabs_.empty()) {
   2283     AutomationJSONReply(automation_,
   2284                         reply_message_.release()).SendSuccess(NULL);
   2285     delete this;
   2286   }
   2287 }
   2288 
   2289 NewTabObserver::NewTabObserver(AutomationProvider* automation,
   2290                                IPC::Message* reply_message)
   2291     : automation_(automation->AsWeakPtr()),
   2292       reply_message_(reply_message) {
   2293   // Use TAB_PARENTED to detect the new tab.
   2294   registrar_.Add(this,
   2295                  NotificationType::TAB_PARENTED,
   2296                  NotificationService::AllSources());
   2297 }
   2298 
   2299 void NewTabObserver::Observe(NotificationType type,
   2300                              const NotificationSource& source,
   2301                              const NotificationDetails& details) {
   2302   DCHECK_EQ(NotificationType::TAB_PARENTED, type.value);
   2303   NavigationController* controller = Source<NavigationController>(source).ptr();
   2304   if (automation_) {
   2305     // TODO(phajdan.jr): Clean up this hack. We write the correct return type
   2306     // here, but don't send the message. NavigationNotificationObserver
   2307     // will wait properly for the load to finish, and send the message,
   2308     // but it will also append its own return value at the end of the reply.
   2309     AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_.get(),
   2310                                                          true);
   2311     new NavigationNotificationObserver(controller, automation_,
   2312                                        reply_message_.release(),
   2313                                        1, false, false);
   2314   }
   2315   delete this;
   2316 }
   2317 
   2318 NewTabObserver::~NewTabObserver() {
   2319 }
   2320 
   2321 WaitForProcessLauncherThreadToGoIdleObserver::
   2322 WaitForProcessLauncherThreadToGoIdleObserver(
   2323     AutomationProvider* automation, IPC::Message* reply_message)
   2324     : automation_(automation->AsWeakPtr()),
   2325       reply_message_(reply_message) {
   2326   // Balanced in RunOnUIThread.
   2327   AddRef();
   2328   BrowserThread::PostTask(
   2329       BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
   2330       NewRunnableMethod(
   2331           this,
   2332           &WaitForProcessLauncherThreadToGoIdleObserver::
   2333               RunOnProcessLauncherThread));
   2334 }
   2335 
   2336 WaitForProcessLauncherThreadToGoIdleObserver::
   2337     ~WaitForProcessLauncherThreadToGoIdleObserver() {
   2338 }
   2339 
   2340 void WaitForProcessLauncherThreadToGoIdleObserver::
   2341 RunOnProcessLauncherThread() {
   2342   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER));
   2343   BrowserThread::PostTask(
   2344       BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
   2345       NewRunnableMethod(
   2346           this,
   2347           &WaitForProcessLauncherThreadToGoIdleObserver::
   2348           RunOnProcessLauncherThread2));
   2349 }
   2350 
   2351 void WaitForProcessLauncherThreadToGoIdleObserver::
   2352 RunOnProcessLauncherThread2() {
   2353   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER));
   2354   BrowserThread::PostTask(
   2355       BrowserThread::UI, FROM_HERE,
   2356       NewRunnableMethod(
   2357           this,
   2358           &WaitForProcessLauncherThreadToGoIdleObserver::RunOnUIThread));
   2359 }
   2360 
   2361 void WaitForProcessLauncherThreadToGoIdleObserver::RunOnUIThread() {
   2362   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   2363   if (automation_)
   2364     automation_->Send(reply_message_.release());
   2365   Release();
   2366 }
   2367