automation_provider_observers.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
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/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_list.h"
26#include "chrome/browser/browser_process.h"
27#include "chrome/browser/dom_operation_notification_details.h"
28#include "chrome/browser/dom_ui/most_visited_handler.h"
29#include "chrome/browser/dom_ui/new_tab_ui.h"
30#include "chrome/browser/download/download_item.h"
31#include "chrome/browser/download/save_package.h"
32#include "chrome/browser/extensions/extension_host.h"
33#include "chrome/browser/extensions/extension_process_manager.h"
34#include "chrome/browser/extensions/extension_updater.h"
35#include "chrome/browser/history/top_sites.h"
36#include "chrome/browser/metrics/metric_event_duration_details.h"
37#include "chrome/browser/notifications/balloon.h"
38#include "chrome/browser/notifications/balloon_host.h"
39#include "chrome/browser/notifications/balloon_collection.h"
40#include "chrome/browser/notifications/notification.h"
41#include "chrome/browser/notifications/notification_ui_manager.h"
42#include "chrome/browser/printing/print_job.h"
43#include "chrome/browser/profiles/profile.h"
44#include "chrome/browser/renderer_host/render_process_host.h"
45#include "chrome/browser/renderer_host/render_view_host.h"
46#include "chrome/browser/search_engines/template_url_model.h"
47#include "chrome/browser/sessions/tab_restore_service.h"
48#include "chrome/browser/tab_contents/navigation_controller.h"
49#include "chrome/browser/tab_contents/tab_contents.h"
50#include "chrome/browser/tab_contents/thumbnail_generator.h"
51#include "chrome/browser/translate/page_translated_details.h"
52#include "chrome/browser/translate/translate_infobar_delegate.h"
53#include "chrome/browser/ui/browser.h"
54#include "chrome/browser/ui/find_bar/find_notification_details.h"
55#include "chrome/browser/ui/login/login_prompt.h"
56#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
57#include "chrome/common/automation_messages.h"
58#include "chrome/common/extensions/extension.h"
59#include "chrome/common/notification_service.h"
60#include "googleurl/src/gurl.h"
61#include "ui/gfx/codec/png_codec.h"
62#include "ui/gfx/rect.h"
63
64#if defined(OS_CHROMEOS)
65#include "chrome/browser/chromeos/login/authentication_notification_details.h"
66#endif
67
68// Holds onto start and stop timestamps for a particular tab
69class 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
88InitialLoadObserver::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
101InitialLoadObserver::~InitialLoadObserver() {
102}
103
104void 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
127DictionaryValue* 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
149void InitialLoadObserver::ConditionMet() {
150  registrar_.RemoveAll();
151  if (automation_)
152    automation_->OnInitialLoadsComplete();
153}
154
155NewTabUILoadObserver::NewTabUILoadObserver(AutomationProvider* automation)
156    : automation_(automation->AsWeakPtr()) {
157  registrar_.Add(this, NotificationType::INITIAL_NEW_TAB_UI_LOAD,
158                 NotificationService::AllSources());
159}
160
161NewTabUILoadObserver::~NewTabUILoadObserver() {
162}
163
164void 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
178NavigationControllerRestoredObserver::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
193NavigationControllerRestoredObserver::~NavigationControllerRestoredObserver() {
194}
195
196void NavigationControllerRestoredObserver::Observe(
197    NotificationType type, const NotificationSource& source,
198    const NotificationDetails& details) {
199  if (FinishedRestoring()) {
200    SendDone();
201    registrar_.RemoveAll();
202  }
203}
204
205bool NavigationControllerRestoredObserver::FinishedRestoring() {
206  return (!controller_->needs_reload() && !controller_->pending_entry() &&
207          !controller_->tab_contents()->is_loading());
208}
209
210void 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
219NavigationNotificationObserver::NavigationNotificationObserver(
220    NavigationController* controller,
221    AutomationProvider* automation,
222    IPC::Message* reply_message,
223    int number_of_navigations,
224    bool include_current_navigation)
225    : automation_(automation->AsWeakPtr()),
226      reply_message_(reply_message),
227      controller_(controller),
228      navigations_remaining_(number_of_navigations),
229      navigation_started_(false) {
230  DCHECK_LT(0, navigations_remaining_);
231  Source<NavigationController> source(controller_);
232  registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED, source);
233  registrar_.Add(this, NotificationType::LOAD_START, source);
234  registrar_.Add(this, NotificationType::LOAD_STOP, source);
235  registrar_.Add(this, NotificationType::AUTH_NEEDED, source);
236  registrar_.Add(this, NotificationType::AUTH_SUPPLIED, source);
237  registrar_.Add(this, NotificationType::AUTH_CANCELLED, source);
238
239  if (include_current_navigation && controller->tab_contents()->is_loading())
240    navigation_started_ = true;
241}
242
243NavigationNotificationObserver::~NavigationNotificationObserver() {
244}
245
246void NavigationNotificationObserver::Observe(
247    NotificationType type, const NotificationSource& source,
248    const NotificationDetails& details) {
249  if (!automation_) {
250    delete this;
251    return;
252  }
253
254  // We listen for 2 events to determine when the navigation started because:
255  // - when this is used by the WaitForNavigation method, we might be invoked
256  // afer the load has started (but not after the entry was committed, as
257  // WaitForNavigation compares times of the last navigation).
258  // - when this is used with a page requiring authentication, we will not get
259  // a NotificationType::NAV_ENTRY_COMMITTED until after we authenticate, so
260  // we need the NotificationType::LOAD_START.
261  if (type == NotificationType::NAV_ENTRY_COMMITTED ||
262      type == NotificationType::LOAD_START) {
263    navigation_started_ = true;
264  } else if (type == NotificationType::LOAD_STOP) {
265    if (navigation_started_) {
266      navigation_started_ = false;
267      if (--navigations_remaining_ == 0)
268        ConditionMet(AUTOMATION_MSG_NAVIGATION_SUCCESS);
269    }
270  } else if (type == NotificationType::AUTH_SUPPLIED ||
271             type == NotificationType::AUTH_CANCELLED) {
272    // The LoginHandler for this tab is no longer valid.
273    automation_->RemoveLoginHandler(controller_);
274
275    // Treat this as if navigation started again, since load start/stop don't
276    // occur while authentication is ongoing.
277    navigation_started_ = true;
278  } else if (type == NotificationType::AUTH_NEEDED) {
279    // Remember the login handler that wants authentication.
280    // We do this in all cases (not just when navigation_started_ == true) so
281    // tests can still wait for auth dialogs outside of navigation.
282    LoginHandler* handler =
283        Details<LoginNotificationDetails>(details)->handler();
284    automation_->AddLoginHandler(controller_, handler);
285
286    // Respond that authentication is needed.
287    navigation_started_ = false;
288    ConditionMet(AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED);
289  } else {
290    NOTREACHED();
291  }
292}
293
294void NavigationNotificationObserver::ConditionMet(
295    AutomationMsg_NavigationResponseValues navigation_result) {
296  if (automation_) {
297    IPC::ParamTraits<AutomationMsg_NavigationResponseValues>::Write(
298        reply_message_.get(), navigation_result);
299    automation_->Send(reply_message_.release());
300  }
301
302  delete this;
303}
304
305TabStripNotificationObserver::TabStripNotificationObserver(
306    NotificationType notification, AutomationProvider* automation)
307    : automation_(automation->AsWeakPtr()),
308      notification_(notification) {
309  registrar_.Add(this, notification_, NotificationService::AllSources());
310}
311
312TabStripNotificationObserver::~TabStripNotificationObserver() {
313}
314
315void TabStripNotificationObserver::Observe(NotificationType type,
316                                           const NotificationSource& source,
317                                           const NotificationDetails& details) {
318  if (type == notification_) {
319    ObserveTab(Source<NavigationController>(source).ptr());
320    delete this;
321  } else {
322    NOTREACHED();
323  }
324}
325
326TabAppendedNotificationObserver::TabAppendedNotificationObserver(
327    Browser* parent, AutomationProvider* automation,
328    IPC::Message* reply_message)
329    : TabStripNotificationObserver(NotificationType::TAB_PARENTED, automation),
330      parent_(parent),
331      reply_message_(reply_message) {
332}
333
334TabAppendedNotificationObserver::~TabAppendedNotificationObserver() {}
335
336void TabAppendedNotificationObserver::ObserveTab(
337    NavigationController* controller) {
338  if (!automation_)
339    return;
340
341  if (automation_->GetIndexForNavigationController(controller, parent_) ==
342      TabStripModel::kNoTab) {
343    // This tab notification doesn't belong to the parent_.
344    return;
345  }
346
347  new NavigationNotificationObserver(controller, automation_,
348                                     reply_message_.release(),
349                                     1, false);
350}
351
352TabClosedNotificationObserver::TabClosedNotificationObserver(
353    AutomationProvider* automation, bool wait_until_closed,
354    IPC::Message* reply_message)
355    : TabStripNotificationObserver(wait_until_closed ?
356          NotificationType::TAB_CLOSED : NotificationType::TAB_CLOSING,
357          automation),
358      reply_message_(reply_message),
359      for_browser_command_(false) {
360}
361
362TabClosedNotificationObserver::~TabClosedNotificationObserver() {}
363
364void TabClosedNotificationObserver::ObserveTab(
365    NavigationController* controller) {
366  if (!automation_)
367    return;
368
369  if (for_browser_command_) {
370    AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_.get(),
371                                                         true);
372  } else {
373    AutomationMsg_CloseTab::WriteReplyParams(reply_message_.get(), true);
374  }
375  automation_->Send(reply_message_.release());
376}
377
378void TabClosedNotificationObserver::set_for_browser_command(
379    bool for_browser_command) {
380  for_browser_command_ = for_browser_command;
381}
382
383TabCountChangeObserver::TabCountChangeObserver(AutomationProvider* automation,
384                                               Browser* browser,
385                                               IPC::Message* reply_message,
386                                               int target_tab_count)
387    : automation_(automation->AsWeakPtr()),
388      reply_message_(reply_message),
389      tab_strip_model_(browser->tabstrip_model()),
390      target_tab_count_(target_tab_count) {
391  tab_strip_model_->AddObserver(this);
392  CheckTabCount();
393}
394
395TabCountChangeObserver::~TabCountChangeObserver() {
396  tab_strip_model_->RemoveObserver(this);
397}
398
399void TabCountChangeObserver::TabInsertedAt(TabContentsWrapper* contents,
400                                           int index,
401                                           bool foreground) {
402  CheckTabCount();
403}
404
405void TabCountChangeObserver::TabDetachedAt(TabContentsWrapper* contents,
406                                           int index) {
407  CheckTabCount();
408}
409
410void TabCountChangeObserver::TabStripModelDeleted() {
411  if (automation_) {
412    AutomationMsg_WaitForTabCountToBecome::WriteReplyParams(
413        reply_message_.get(), false);
414    automation_->Send(reply_message_.release());
415  }
416
417  delete this;
418}
419
420void TabCountChangeObserver::CheckTabCount() {
421  if (tab_strip_model_->count() != target_tab_count_)
422    return;
423
424  if (automation_) {
425    AutomationMsg_WaitForTabCountToBecome::WriteReplyParams(
426        reply_message_.get(), true);
427    automation_->Send(reply_message_.release());
428  }
429
430  delete this;
431}
432
433bool DidExtensionHostsStopLoading(ExtensionProcessManager* manager) {
434  for (ExtensionProcessManager::const_iterator iter = manager->begin();
435       iter != manager->end(); ++iter) {
436    if (!(*iter)->did_stop_loading())
437      return false;
438  }
439  return true;
440}
441
442ExtensionInstallNotificationObserver::ExtensionInstallNotificationObserver(
443    AutomationProvider* automation, int id, IPC::Message* reply_message)
444    : automation_(automation->AsWeakPtr()),
445      id_(id),
446      reply_message_(reply_message) {
447  registrar_.Add(this, NotificationType::EXTENSION_LOADED,
448                 NotificationService::AllSources());
449  registrar_.Add(this, NotificationType::EXTENSION_INSTALL_ERROR,
450                 NotificationService::AllSources());
451  registrar_.Add(this, NotificationType::EXTENSION_UPDATE_DISABLED,
452                 NotificationService::AllSources());
453}
454
455ExtensionInstallNotificationObserver::~ExtensionInstallNotificationObserver() {
456}
457
458void ExtensionInstallNotificationObserver::Observe(
459    NotificationType type, const NotificationSource& source,
460    const NotificationDetails& details) {
461  switch (type.value) {
462    case NotificationType::EXTENSION_LOADED:
463      SendResponse(AUTOMATION_MSG_EXTENSION_INSTALL_SUCCEEDED);
464      break;
465    case NotificationType::EXTENSION_INSTALL_ERROR:
466    case NotificationType::EXTENSION_UPDATE_DISABLED:
467      SendResponse(AUTOMATION_MSG_EXTENSION_INSTALL_FAILED);
468      break;
469    default:
470      NOTREACHED();
471      break;
472  }
473
474  delete this;
475}
476
477void ExtensionInstallNotificationObserver::SendResponse(
478    AutomationMsg_ExtensionResponseValues response) {
479  if (!automation_ || !reply_message_.get()) {
480    delete this;
481    return;
482  }
483
484  switch (id_) {
485    case AutomationMsg_InstallExtension::ID:
486      AutomationMsg_InstallExtension::WriteReplyParams(reply_message_.get(),
487                                                       response);
488      break;
489    case AutomationMsg_LoadExpandedExtension::ID:
490      AutomationMsg_LoadExpandedExtension::WriteReplyParams(
491          reply_message_.get(), response);
492      break;
493    default:
494      NOTREACHED();
495      break;
496  }
497
498  automation_->Send(reply_message_.release());
499}
500
501ExtensionReadyNotificationObserver::ExtensionReadyNotificationObserver(
502    ExtensionProcessManager* manager, AutomationProvider* automation, int id,
503    IPC::Message* reply_message)
504    : manager_(manager),
505      automation_(automation->AsWeakPtr()),
506      id_(id),
507      reply_message_(reply_message),
508      extension_(NULL) {
509  registrar_.Add(this, NotificationType::EXTENSION_HOST_DID_STOP_LOADING,
510                 NotificationService::AllSources());
511  registrar_.Add(this, NotificationType::EXTENSION_LOADED,
512                 NotificationService::AllSources());
513  registrar_.Add(this, NotificationType::EXTENSION_INSTALL_ERROR,
514                 NotificationService::AllSources());
515  registrar_.Add(this, NotificationType::EXTENSION_UPDATE_DISABLED,
516                 NotificationService::AllSources());
517}
518
519ExtensionReadyNotificationObserver::~ExtensionReadyNotificationObserver() {
520}
521
522void ExtensionReadyNotificationObserver::Observe(
523    NotificationType type, const NotificationSource& source,
524    const NotificationDetails& details) {
525  if (!automation_) {
526    delete this;
527    return;
528  }
529
530  bool success = false;
531  switch (type.value) {
532    case NotificationType::EXTENSION_HOST_DID_STOP_LOADING:
533      // Only continue on with this method if our extension has been loaded
534      // and all the extension hosts have stopped loading.
535      if (!extension_ || !DidExtensionHostsStopLoading(manager_))
536        return;
537      success = true;
538      break;
539    case NotificationType::EXTENSION_LOADED:
540      extension_ = Details<const Extension>(details).ptr();
541      if (!DidExtensionHostsStopLoading(manager_))
542        return;
543      success = true;
544      break;
545    case NotificationType::EXTENSION_INSTALL_ERROR:
546    case NotificationType::EXTENSION_UPDATE_DISABLED:
547      success = false;
548      break;
549    default:
550      NOTREACHED();
551      break;
552  }
553
554  if (id_ == AutomationMsg_InstallExtensionAndGetHandle::ID) {
555    // A handle of zero indicates an error.
556    int extension_handle = 0;
557    if (extension_)
558      extension_handle = automation_->AddExtension(extension_);
559    AutomationMsg_InstallExtensionAndGetHandle::WriteReplyParams(
560        reply_message_.get(), extension_handle);
561  } else if (id_ == AutomationMsg_EnableExtension::ID) {
562    AutomationMsg_EnableExtension::WriteReplyParams(reply_message_.get(), true);
563  } else {
564    NOTREACHED();
565    LOG(ERROR) << "Cannot write reply params for unknown message id.";
566  }
567
568  automation_->Send(reply_message_.release());
569  delete this;
570}
571
572ExtensionUnloadNotificationObserver::ExtensionUnloadNotificationObserver()
573    : did_receive_unload_notification_(false) {
574  registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
575                 NotificationService::AllSources());
576}
577
578ExtensionUnloadNotificationObserver::~ExtensionUnloadNotificationObserver() {
579}
580
581void ExtensionUnloadNotificationObserver::Observe(
582    NotificationType type, const NotificationSource& source,
583    const NotificationDetails& details) {
584  if (type.value == NotificationType::EXTENSION_UNLOADED) {
585    did_receive_unload_notification_ = true;
586  } else {
587    NOTREACHED();
588  }
589}
590
591ExtensionTestResultNotificationObserver::
592    ExtensionTestResultNotificationObserver(AutomationProvider* automation)
593        : automation_(automation->AsWeakPtr()) {
594  registrar_.Add(this, NotificationType::EXTENSION_TEST_PASSED,
595                 NotificationService::AllSources());
596  registrar_.Add(this, NotificationType::EXTENSION_TEST_FAILED,
597                 NotificationService::AllSources());
598}
599
600ExtensionTestResultNotificationObserver::
601    ~ExtensionTestResultNotificationObserver() {
602}
603
604void ExtensionTestResultNotificationObserver::Observe(
605    NotificationType type, const NotificationSource& source,
606    const NotificationDetails& details) {
607  switch (type.value) {
608    case NotificationType::EXTENSION_TEST_PASSED:
609      results_.push_back(true);
610      messages_.push_back("");
611      break;
612
613    case NotificationType::EXTENSION_TEST_FAILED:
614      results_.push_back(false);
615      messages_.push_back(*(Details<std::string>(details).ptr()));
616      break;
617
618    default:
619      NOTREACHED();
620  }
621  // There may be a reply message waiting for this event, so check.
622  MaybeSendResult();
623}
624
625void ExtensionTestResultNotificationObserver::MaybeSendResult() {
626  if (!automation_)
627    return;
628
629  if (results_.size() > 0) {
630    // This release method should return the automation's current
631    // reply message, or NULL if there is no current one. If it is not
632    // NULL, we are stating that we will handle this reply message.
633    IPC::Message* reply_message = automation_->reply_message_release();
634    // Send the result back if we have a reply message.
635    if (reply_message) {
636      AutomationMsg_WaitForExtensionTestResult::WriteReplyParams(
637          reply_message, results_.front(), messages_.front());
638      results_.pop_front();
639      messages_.pop_front();
640      automation_->Send(reply_message);
641    }
642  }
643}
644
645BrowserOpenedNotificationObserver::BrowserOpenedNotificationObserver(
646    AutomationProvider* automation, IPC::Message* reply_message)
647    : automation_(automation->AsWeakPtr()),
648      reply_message_(reply_message),
649      for_browser_command_(false) {
650  registrar_.Add(this, NotificationType::BROWSER_OPENED,
651                 NotificationService::AllSources());
652}
653
654BrowserOpenedNotificationObserver::~BrowserOpenedNotificationObserver() {
655}
656
657void BrowserOpenedNotificationObserver::Observe(
658    NotificationType type, const NotificationSource& source,
659    const NotificationDetails& details) {
660  if (!automation_) {
661    delete this;
662    return;
663  }
664
665  if (type == NotificationType::BROWSER_OPENED) {
666    if (for_browser_command_) {
667      AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_.get(),
668                                                           true);
669    }
670    automation_->Send(reply_message_.release());
671    delete this;
672  } else {
673    NOTREACHED();
674  }
675}
676
677void BrowserOpenedNotificationObserver::set_for_browser_command(
678    bool for_browser_command) {
679  for_browser_command_ = for_browser_command;
680}
681
682BrowserClosedNotificationObserver::BrowserClosedNotificationObserver(
683    Browser* browser,
684    AutomationProvider* automation,
685    IPC::Message* reply_message)
686    : automation_(automation->AsWeakPtr()),
687      reply_message_(reply_message),
688      for_browser_command_(false) {
689  registrar_.Add(this, NotificationType::BROWSER_CLOSED,
690                 Source<Browser>(browser));
691}
692
693BrowserClosedNotificationObserver::~BrowserClosedNotificationObserver() {}
694
695void BrowserClosedNotificationObserver::Observe(
696    NotificationType type, const NotificationSource& source,
697    const NotificationDetails& details) {
698  DCHECK(type == NotificationType::BROWSER_CLOSED);
699
700  if (!automation_) {
701    delete this;
702    return;
703  }
704
705  Details<bool> close_app(details);
706
707  if (for_browser_command_) {
708    AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_.get(),
709                                                         true);
710  } else {
711    AutomationMsg_CloseBrowser::WriteReplyParams(reply_message_.get(), true,
712                                                 *(close_app.ptr()));
713  }
714  automation_->Send(reply_message_.release());
715  delete this;
716}
717
718void BrowserClosedNotificationObserver::set_for_browser_command(
719    bool for_browser_command) {
720  for_browser_command_ = for_browser_command;
721}
722
723BrowserCountChangeNotificationObserver::BrowserCountChangeNotificationObserver(
724    int target_count,
725    AutomationProvider* automation,
726    IPC::Message* reply_message)
727    : target_count_(target_count),
728      automation_(automation->AsWeakPtr()),
729      reply_message_(reply_message) {
730  registrar_.Add(this, NotificationType::BROWSER_OPENED,
731                 NotificationService::AllSources());
732  registrar_.Add(this, NotificationType::BROWSER_CLOSED,
733                 NotificationService::AllSources());
734}
735
736BrowserCountChangeNotificationObserver::
737~BrowserCountChangeNotificationObserver() {}
738
739void BrowserCountChangeNotificationObserver::Observe(
740    NotificationType type,
741    const NotificationSource& source,
742    const NotificationDetails& details) {
743  DCHECK(type == NotificationType::BROWSER_OPENED ||
744         type == NotificationType::BROWSER_CLOSED);
745  int current_count = static_cast<int>(BrowserList::size());
746  if (type == NotificationType::BROWSER_CLOSED) {
747    // At the time of the notification the browser being closed is not removed
748    // from the list. The real count is one less than the reported count.
749    DCHECK_LT(0, current_count);
750    current_count--;
751  }
752
753  if (!automation_) {
754    delete this;
755    return;
756  }
757
758  if (current_count == target_count_) {
759    AutomationMsg_WaitForBrowserWindowCountToBecome::WriteReplyParams(
760        reply_message_.get(), true);
761    automation_->Send(reply_message_.release());
762    delete this;
763  }
764}
765
766AppModalDialogShownObserver::AppModalDialogShownObserver(
767    AutomationProvider* automation, IPC::Message* reply_message)
768    : automation_(automation->AsWeakPtr()),
769      reply_message_(reply_message) {
770  registrar_.Add(this, NotificationType::APP_MODAL_DIALOG_SHOWN,
771                 NotificationService::AllSources());
772}
773
774AppModalDialogShownObserver::~AppModalDialogShownObserver() {
775}
776
777void AppModalDialogShownObserver::Observe(
778    NotificationType type, const NotificationSource& source,
779    const NotificationDetails& details) {
780  DCHECK(type == NotificationType::APP_MODAL_DIALOG_SHOWN);
781
782  if (automation_) {
783    AutomationMsg_WaitForAppModalDialogToBeShown::WriteReplyParams(
784        reply_message_.get(), true);
785    automation_->Send(reply_message_.release());
786  }
787  delete this;
788}
789
790namespace {
791
792// Define mapping from command to notification
793struct CommandNotification {
794  int command;
795  NotificationType::Type notification_type;
796};
797
798const struct CommandNotification command_notifications[] = {
799  {IDC_DUPLICATE_TAB, NotificationType::TAB_PARENTED},
800
801  // Returns as soon as the restored tab is created. To further wait until
802  // the content page is loaded, use WaitForTabToBeRestored.
803  {IDC_RESTORE_TAB, NotificationType::TAB_PARENTED},
804
805  // For the following commands, we need to wait for a new tab to be created,
806  // load to finish, and title to change.
807  {IDC_MANAGE_EXTENSIONS, NotificationType::TAB_CONTENTS_TITLE_UPDATED},
808  {IDC_OPTIONS, NotificationType::TAB_CONTENTS_TITLE_UPDATED},
809  {IDC_PRINT, NotificationType::TAB_CONTENTS_TITLE_UPDATED},
810  {IDC_SHOW_DOWNLOADS, NotificationType::TAB_CONTENTS_TITLE_UPDATED},
811  {IDC_SHOW_HISTORY, NotificationType::TAB_CONTENTS_TITLE_UPDATED},
812};
813
814}  // namespace
815
816ExecuteBrowserCommandObserver::~ExecuteBrowserCommandObserver() {
817}
818
819// static
820bool ExecuteBrowserCommandObserver::CreateAndRegisterObserver(
821    AutomationProvider* automation, Browser* browser, int command,
822    IPC::Message* reply_message) {
823  bool result = true;
824  switch (command) {
825    case IDC_NEW_TAB: {
826      new NewTabObserver(automation, reply_message);
827      break;
828    }
829    case IDC_NEW_WINDOW:
830    case IDC_NEW_INCOGNITO_WINDOW: {
831      BrowserOpenedNotificationObserver* observer =
832          new BrowserOpenedNotificationObserver(automation, reply_message);
833      observer->set_for_browser_command(true);
834      break;
835    }
836    case IDC_CLOSE_WINDOW: {
837      BrowserClosedNotificationObserver* observer =
838          new BrowserClosedNotificationObserver(browser, automation,
839                                                reply_message);
840      observer->set_for_browser_command(true);
841      break;
842    }
843    case IDC_CLOSE_TAB: {
844      TabClosedNotificationObserver* observer =
845          new TabClosedNotificationObserver(automation, true, reply_message);
846      observer->set_for_browser_command(true);
847      break;
848    }
849    case IDC_BACK:
850    case IDC_FORWARD:
851    case IDC_RELOAD: {
852      new NavigationNotificationObserver(
853          &browser->GetSelectedTabContents()->controller(),
854          automation, reply_message, 1, false);
855      break;
856    }
857    default: {
858      ExecuteBrowserCommandObserver* observer =
859          new ExecuteBrowserCommandObserver(automation, reply_message);
860      if (!observer->Register(command)) {
861        delete observer;
862        result = false;
863      }
864      break;
865    }
866  }
867  return result;
868}
869
870void ExecuteBrowserCommandObserver::Observe(
871    NotificationType type, const NotificationSource& source,
872    const NotificationDetails& details) {
873  if (type == notification_type_) {
874    if (automation_) {
875      AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_.get(),
876                                                           true);
877      automation_->Send(reply_message_.release());
878    }
879    delete this;
880  } else {
881    NOTREACHED();
882  }
883}
884
885ExecuteBrowserCommandObserver::ExecuteBrowserCommandObserver(
886    AutomationProvider* automation, IPC::Message* reply_message)
887    : automation_(automation->AsWeakPtr()),
888      notification_type_(NotificationType::ALL),
889      reply_message_(reply_message) {
890}
891
892bool ExecuteBrowserCommandObserver::Register(int command) {
893  if (!GetNotificationType(command, &notification_type_))
894    return false;
895  registrar_.Add(this, notification_type_, NotificationService::AllSources());
896  return true;
897}
898
899bool ExecuteBrowserCommandObserver::GetNotificationType(
900    int command, NotificationType::Type* type) {
901  if (!type)
902    return false;
903  bool found = false;
904  for (unsigned int i = 0; i < arraysize(command_notifications); i++) {
905    if (command_notifications[i].command == command) {
906      *type = command_notifications[i].notification_type;
907      found = true;
908      break;
909    }
910  }
911  return found;
912}
913
914FindInPageNotificationObserver::FindInPageNotificationObserver(
915    AutomationProvider* automation, TabContents* parent_tab,
916    bool reply_with_json, IPC::Message* reply_message)
917    : automation_(automation->AsWeakPtr()),
918      active_match_ordinal_(-1),
919      reply_with_json_(reply_with_json),
920      reply_message_(reply_message) {
921  registrar_.Add(this, NotificationType::FIND_RESULT_AVAILABLE,
922                 Source<TabContents>(parent_tab));
923}
924
925FindInPageNotificationObserver::~FindInPageNotificationObserver() {
926}
927
928void FindInPageNotificationObserver::Observe(
929    NotificationType type, const NotificationSource& source,
930    const NotificationDetails& details) {
931  Details<FindNotificationDetails> find_details(details);
932  if (!(find_details->final_update() && reply_message_ != NULL)) {
933    DVLOG(1) << "Ignoring, since we only care about the final message";
934    return;
935  }
936
937  if (!automation_) {
938    delete this;
939    return;
940  }
941
942  // We get multiple responses and one of those will contain the ordinal.
943  // This message comes to us before the final update is sent.
944  if (find_details->request_id() == kFindInPageRequestId) {
945    if (reply_with_json_) {
946      scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
947      return_value->SetInteger("match_count",
948          find_details->number_of_matches());
949      gfx::Rect rect = find_details->selection_rect();
950      // If MatchCount is > 0, then rect should not be Empty.
951      // We dont guard it here because we want to let the test
952      // code catch this invalid case if needed.
953      if (!rect.IsEmpty()) {
954        return_value->SetInteger("match_left", rect.x());
955        return_value->SetInteger("match_top", rect.y());
956        return_value->SetInteger("match_right", rect.right());
957        return_value->SetInteger("match_bottom", rect.bottom());
958      }
959      AutomationJSONReply(automation_, reply_message_.release())
960          .SendSuccess(return_value.get());
961      delete this;
962    } else {
963      if (find_details->active_match_ordinal() > -1) {
964        active_match_ordinal_ = find_details->active_match_ordinal();
965        AutomationMsg_Find::WriteReplyParams(reply_message_.get(),
966            active_match_ordinal_, find_details->number_of_matches());
967        automation_->Send(reply_message_.release());
968      }
969    }
970  }
971}
972
973// static
974const int FindInPageNotificationObserver::kFindInPageRequestId = -1;
975
976DomOperationObserver::DomOperationObserver() {
977  registrar_.Add(this, NotificationType::DOM_OPERATION_RESPONSE,
978                 NotificationService::AllSources());
979}
980
981DomOperationObserver::~DomOperationObserver() {}
982
983void DomOperationObserver::Observe(
984    NotificationType type, const NotificationSource& source,
985    const NotificationDetails& details) {
986  if (NotificationType::DOM_OPERATION_RESPONSE == type) {
987    Details<DomOperationNotificationDetails> dom_op_details(details);
988    OnDomOperationCompleted(dom_op_details->json());
989  }
990}
991
992DomOperationMessageSender::DomOperationMessageSender(
993    AutomationProvider* automation)
994    : automation_(automation->AsWeakPtr()) {
995}
996
997DomOperationMessageSender::~DomOperationMessageSender() {}
998
999void DomOperationMessageSender::OnDomOperationCompleted(
1000    const std::string& json) {
1001  if (!automation_)
1002    return;
1003
1004  IPC::Message* reply_message = automation_->reply_message_release();
1005  if (reply_message) {
1006    AutomationMsg_DomOperation::WriteReplyParams(reply_message, json);
1007    automation_->Send(reply_message);
1008  } else {
1009    LOG(ERROR) << "DOM operation completed, but no reply message";
1010  }
1011}
1012
1013DocumentPrintedNotificationObserver::DocumentPrintedNotificationObserver(
1014    AutomationProvider* automation, IPC::Message* reply_message)
1015    : automation_(automation->AsWeakPtr()),
1016      success_(false),
1017      reply_message_(reply_message) {
1018  registrar_.Add(this, NotificationType::PRINT_JOB_EVENT,
1019                 NotificationService::AllSources());
1020}
1021
1022DocumentPrintedNotificationObserver::~DocumentPrintedNotificationObserver() {
1023  if (automation_) {
1024    AutomationMsg_PrintNow::WriteReplyParams(reply_message_.get(), success_);
1025    automation_->Send(reply_message_.release());
1026  }
1027}
1028
1029void DocumentPrintedNotificationObserver::Observe(
1030    NotificationType type, const NotificationSource& source,
1031    const NotificationDetails& details) {
1032  using namespace printing;
1033  DCHECK(type == NotificationType::PRINT_JOB_EVENT);
1034  switch (Details<JobEventDetails>(details)->type()) {
1035    case JobEventDetails::JOB_DONE: {
1036      // Succeeded.
1037      success_ = true;
1038      delete this;
1039      break;
1040    }
1041    case JobEventDetails::USER_INIT_CANCELED:
1042    case JobEventDetails::FAILED: {
1043      // Failed.
1044      delete this;
1045      break;
1046    }
1047    case JobEventDetails::NEW_DOC:
1048    case JobEventDetails::USER_INIT_DONE:
1049    case JobEventDetails::DEFAULT_INIT_DONE:
1050    case JobEventDetails::NEW_PAGE:
1051    case JobEventDetails::PAGE_DONE:
1052    case JobEventDetails::DOC_DONE:
1053    case JobEventDetails::ALL_PAGES_REQUESTED: {
1054      // Don't care.
1055      break;
1056    }
1057    default: {
1058      NOTREACHED();
1059      break;
1060    }
1061  }
1062}
1063
1064MetricEventDurationObserver::MetricEventDurationObserver() {
1065  registrar_.Add(this, NotificationType::METRIC_EVENT_DURATION,
1066                 NotificationService::AllSources());
1067}
1068
1069MetricEventDurationObserver::~MetricEventDurationObserver() {}
1070
1071int MetricEventDurationObserver::GetEventDurationMs(
1072    const std::string& event_name) {
1073  EventDurationMap::const_iterator it = durations_.find(event_name);
1074  if (it == durations_.end())
1075    return -1;
1076  return it->second;
1077}
1078
1079void MetricEventDurationObserver::Observe(NotificationType type,
1080    const NotificationSource& source, const NotificationDetails& details) {
1081  if (type != NotificationType::METRIC_EVENT_DURATION) {
1082    NOTREACHED();
1083    return;
1084  }
1085  MetricEventDurationDetails* metric_event_duration =
1086      Details<MetricEventDurationDetails>(details).ptr();
1087  durations_[metric_event_duration->event_name] =
1088      metric_event_duration->duration_ms;
1089}
1090
1091PageTranslatedObserver::PageTranslatedObserver(AutomationProvider* automation,
1092                                               IPC::Message* reply_message,
1093                                               TabContents* tab_contents)
1094  : automation_(automation->AsWeakPtr()),
1095    reply_message_(reply_message) {
1096  registrar_.Add(this, NotificationType::PAGE_TRANSLATED,
1097                 Source<TabContents>(tab_contents));
1098}
1099
1100PageTranslatedObserver::~PageTranslatedObserver() {}
1101
1102void PageTranslatedObserver::Observe(NotificationType type,
1103                                     const NotificationSource& source,
1104                                     const NotificationDetails& details) {
1105  if (!automation_) {
1106    delete this;
1107    return;
1108  }
1109
1110  DCHECK(type == NotificationType::PAGE_TRANSLATED);
1111  AutomationJSONReply reply(automation_, reply_message_.release());
1112
1113  PageTranslatedDetails* translated_details =
1114      Details<PageTranslatedDetails>(details).ptr();
1115  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
1116  return_value->SetBoolean(
1117      "translation_success",
1118      translated_details->error_type == TranslateErrors::NONE);
1119  reply.SendSuccess(return_value.get());
1120  delete this;
1121}
1122
1123TabLanguageDeterminedObserver::TabLanguageDeterminedObserver(
1124    AutomationProvider* automation, IPC::Message* reply_message,
1125    TabContents* tab_contents, TranslateInfoBarDelegate* translate_bar)
1126    : automation_(automation->AsWeakPtr()),
1127      reply_message_(reply_message),
1128      tab_contents_(tab_contents),
1129      translate_bar_(translate_bar) {
1130  registrar_.Add(this, NotificationType::TAB_LANGUAGE_DETERMINED,
1131                 Source<TabContents>(tab_contents));
1132}
1133
1134TabLanguageDeterminedObserver::~TabLanguageDeterminedObserver() {}
1135
1136void TabLanguageDeterminedObserver::Observe(
1137    NotificationType type, const NotificationSource& source,
1138    const NotificationDetails& details) {
1139  DCHECK(type == NotificationType::TAB_LANGUAGE_DETERMINED);
1140
1141  if (!automation_) {
1142    delete this;
1143    return;
1144  }
1145
1146  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
1147  return_value->SetBoolean("page_translated",
1148                           tab_contents_->language_state().IsPageTranslated());
1149  return_value->SetBoolean(
1150      "can_translate_page", TranslatePrefs::CanTranslate(
1151          automation_->profile()->GetPrefs(),
1152          tab_contents_->language_state().original_language(),
1153          tab_contents_->GetURL()));
1154  return_value->SetString(
1155      "original_language",
1156      tab_contents_->language_state().original_language());
1157  if (translate_bar_) {
1158    DictionaryValue* bar_info = new DictionaryValue;
1159    std::map<TranslateInfoBarDelegate::Type, std::string> type_to_string;
1160    type_to_string[TranslateInfoBarDelegate::BEFORE_TRANSLATE] =
1161        "BEFORE_TRANSLATE";
1162    type_to_string[TranslateInfoBarDelegate::TRANSLATING] =
1163        "TRANSLATING";
1164    type_to_string[TranslateInfoBarDelegate::AFTER_TRANSLATE] =
1165        "AFTER_TRANSLATE";
1166    type_to_string[TranslateInfoBarDelegate::TRANSLATION_ERROR] =
1167        "TRANSLATION_ERROR";
1168
1169    bar_info->SetBoolean("always_translate_lang_button_showing",
1170                         translate_bar_->ShouldShowAlwaysTranslateButton());
1171    bar_info->SetBoolean("never_translate_lang_button_showing",
1172                         translate_bar_->ShouldShowNeverTranslateButton());
1173    bar_info->SetString("bar_state", type_to_string[translate_bar_->type()]);
1174    bar_info->SetString("target_lang_code",
1175                        translate_bar_->GetTargetLanguageCode());
1176    bar_info->SetString("original_lang_code",
1177                        translate_bar_->GetOriginalLanguageCode());
1178    return_value->Set("translate_bar", bar_info);
1179  }
1180  AutomationJSONReply(automation_, reply_message_.release())
1181      .SendSuccess(return_value.get());
1182  delete this;
1183}
1184
1185InfoBarCountObserver::InfoBarCountObserver(AutomationProvider* automation,
1186                                           IPC::Message* reply_message,
1187                                           TabContents* tab_contents,
1188                                           size_t target_count)
1189    : automation_(automation->AsWeakPtr()),
1190      reply_message_(reply_message),
1191      tab_contents_(tab_contents),
1192      target_count_(target_count) {
1193  Source<TabContents> source(tab_contents);
1194  registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_ADDED, source);
1195  registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_REMOVED, source);
1196  CheckCount();
1197}
1198
1199InfoBarCountObserver::~InfoBarCountObserver() {}
1200
1201void InfoBarCountObserver::Observe(NotificationType type,
1202                                   const NotificationSource& source,
1203                                   const NotificationDetails& details) {
1204  DCHECK(type == NotificationType::TAB_CONTENTS_INFOBAR_ADDED ||
1205         type == NotificationType::TAB_CONTENTS_INFOBAR_REMOVED);
1206  CheckCount();
1207}
1208
1209void InfoBarCountObserver::CheckCount() {
1210  if (tab_contents_->infobar_count() != target_count_)
1211    return;
1212
1213  if (automation_) {
1214    AutomationMsg_WaitForInfoBarCount::WriteReplyParams(reply_message_.get(),
1215                                                        true);
1216    automation_->Send(reply_message_.release());
1217  }
1218  delete this;
1219}
1220
1221#if defined(OS_CHROMEOS)
1222LoginManagerObserver::LoginManagerObserver(
1223    AutomationProvider* automation,
1224    IPC::Message* reply_message)
1225    : automation_(automation->AsWeakPtr()),
1226      reply_message_(reply_message) {
1227
1228  registrar_.Add(this, NotificationType::LOGIN_USER_CHANGED,
1229                 NotificationService::AllSources());
1230}
1231
1232void LoginManagerObserver::Observe(NotificationType type,
1233                                   const NotificationSource& source,
1234                                   const NotificationDetails& details) {
1235  DCHECK(type == NotificationType::LOGIN_USER_CHANGED);
1236
1237  if (!automation_) {
1238    delete this;
1239    return;
1240  }
1241
1242  AutomationJSONReply reply(automation_, reply_message_.release());
1243  Details<AuthenticationNotificationDetails> auth_details(details);
1244  if (auth_details->success())
1245    reply.SendSuccess(NULL);
1246  else
1247    reply.SendError("Login failure.");
1248  delete this;
1249}
1250
1251ScreenLockUnlockObserver::ScreenLockUnlockObserver(
1252    AutomationProvider* automation,
1253    IPC::Message* reply_message,
1254    bool lock_screen)
1255    : automation_(automation),
1256      reply_message_(reply_message),
1257      lock_screen_(lock_screen) {
1258
1259  registrar_.Add(this, NotificationType::SCREEN_LOCK_STATE_CHANGED,
1260                 NotificationService::AllSources());
1261}
1262
1263void ScreenLockUnlockObserver::Observe(NotificationType type,
1264                                       const NotificationSource& source,
1265                                       const NotificationDetails& details) {
1266  DCHECK(type == NotificationType::SCREEN_LOCK_STATE_CHANGED);
1267  AutomationJSONReply reply(automation_, reply_message_);
1268  bool is_screen_locked = *Details<bool>(details).ptr();
1269  if (lock_screen_ == is_screen_locked)
1270    reply.SendSuccess(NULL);
1271  else
1272    reply.SendError("Screen lock failure.");
1273  delete this;
1274}
1275#endif
1276
1277AutomationProviderBookmarkModelObserver::
1278AutomationProviderBookmarkModelObserver(
1279    AutomationProvider* provider,
1280    IPC::Message* reply_message,
1281    BookmarkModel* model)
1282    : automation_provider_(provider->AsWeakPtr()),
1283      reply_message_(reply_message),
1284      model_(model) {
1285  model_->AddObserver(this);
1286}
1287
1288AutomationProviderBookmarkModelObserver::
1289~AutomationProviderBookmarkModelObserver() {
1290  model_->RemoveObserver(this);
1291}
1292
1293void AutomationProviderBookmarkModelObserver::Loaded(BookmarkModel* model) {
1294  ReplyAndDelete(true);
1295}
1296
1297void AutomationProviderBookmarkModelObserver::BookmarkModelBeingDeleted(
1298    BookmarkModel* model) {
1299  ReplyAndDelete(false);
1300}
1301
1302void AutomationProviderBookmarkModelObserver::ReplyAndDelete(bool success) {
1303  if (automation_provider_) {
1304    AutomationMsg_WaitForBookmarkModelToLoad::WriteReplyParams(
1305        reply_message_.get(), success);
1306    automation_provider_->Send(reply_message_.release());
1307  }
1308  delete this;
1309}
1310
1311AutomationProviderDownloadItemObserver::AutomationProviderDownloadItemObserver(
1312    AutomationProvider* provider,
1313    IPC::Message* reply_message,
1314    int downloads)
1315    : provider_(provider->AsWeakPtr()),
1316      reply_message_(reply_message),
1317      downloads_(downloads) {
1318}
1319
1320AutomationProviderDownloadItemObserver::
1321~AutomationProviderDownloadItemObserver() {}
1322
1323void AutomationProviderDownloadItemObserver::OnDownloadUpdated(
1324    DownloadItem* download) {
1325}
1326
1327void AutomationProviderDownloadItemObserver::OnDownloadFileCompleted(
1328    DownloadItem* download) {
1329  download->RemoveObserver(this);
1330  if (--downloads_ == 0) {
1331    if (provider_) {
1332      AutomationJSONReply(provider_,
1333                          reply_message_.release()).SendSuccess(NULL);
1334    }
1335    delete this;
1336  }
1337}
1338
1339void AutomationProviderDownloadItemObserver::OnDownloadOpened(
1340    DownloadItem* download) {
1341}
1342
1343AutomationProviderDownloadUpdatedObserver::
1344AutomationProviderDownloadUpdatedObserver(
1345    AutomationProvider* provider,
1346    IPC::Message* reply_message,
1347    bool wait_for_open)
1348    : provider_(provider->AsWeakPtr()),
1349      reply_message_(reply_message),
1350      wait_for_open_(wait_for_open) {
1351}
1352
1353AutomationProviderDownloadUpdatedObserver::
1354~AutomationProviderDownloadUpdatedObserver() {}
1355
1356void AutomationProviderDownloadUpdatedObserver::OnDownloadUpdated(
1357    DownloadItem* download) {
1358  // If this observer is watching for open, only send the reply if the download
1359  // has been auto-opened.
1360  if (wait_for_open_ && !download->auto_opened())
1361    return;
1362
1363  download->RemoveObserver(this);
1364  scoped_ptr<DictionaryValue> return_value(
1365      provider_->GetDictionaryFromDownloadItem(download));
1366
1367  if (provider_) {
1368    AutomationJSONReply(provider_, reply_message_.release()).SendSuccess(
1369        return_value.get());
1370  }
1371  delete this;
1372}
1373
1374void AutomationProviderDownloadUpdatedObserver::OnDownloadOpened(
1375    DownloadItem* download) {
1376  download->RemoveObserver(this);
1377  scoped_ptr<DictionaryValue> return_value(
1378      provider_->GetDictionaryFromDownloadItem(download));
1379
1380  if (provider_) {
1381    AutomationJSONReply(provider_, reply_message_.release()).SendSuccess(
1382        return_value.get());
1383  }
1384  delete this;
1385}
1386
1387void AutomationProviderDownloadUpdatedObserver::OnDownloadFileCompleted(
1388    DownloadItem* download) {
1389}
1390
1391AutomationProviderDownloadModelChangedObserver::
1392AutomationProviderDownloadModelChangedObserver(
1393    AutomationProvider* provider,
1394    IPC::Message* reply_message,
1395    DownloadManager* download_manager)
1396    : provider_(provider->AsWeakPtr()),
1397      reply_message_(reply_message),
1398      download_manager_(download_manager) {
1399}
1400
1401AutomationProviderDownloadModelChangedObserver::
1402~AutomationProviderDownloadModelChangedObserver() {}
1403
1404void AutomationProviderDownloadModelChangedObserver::ModelChanged() {
1405  download_manager_->RemoveObserver(this);
1406
1407  if (provider_)
1408    AutomationJSONReply(provider_, reply_message_.release()).SendSuccess(NULL);
1409  delete this;
1410}
1411
1412AutomationProviderSearchEngineObserver::AutomationProviderSearchEngineObserver(
1413    AutomationProvider* provider,
1414    IPC::Message* reply_message)
1415    : provider_(provider->AsWeakPtr()),
1416      reply_message_(reply_message) {
1417}
1418
1419AutomationProviderSearchEngineObserver::
1420~AutomationProviderSearchEngineObserver() {}
1421
1422void AutomationProviderSearchEngineObserver::OnTemplateURLModelChanged() {
1423  TemplateURLModel* url_model = provider_->profile()->GetTemplateURLModel();
1424  url_model->RemoveObserver(this);
1425
1426  if (provider_)
1427    AutomationJSONReply(provider_, reply_message_.release()).SendSuccess(NULL);
1428  delete this;
1429}
1430
1431AutomationProviderHistoryObserver::AutomationProviderHistoryObserver(
1432    AutomationProvider* provider,
1433    IPC::Message* reply_message)
1434    : provider_(provider->AsWeakPtr()),
1435      reply_message_(reply_message) {
1436}
1437
1438AutomationProviderHistoryObserver::~AutomationProviderHistoryObserver() {}
1439
1440void AutomationProviderHistoryObserver::HistoryQueryComplete(
1441    HistoryService::Handle request_handle,
1442    history::QueryResults* results) {
1443  if (!provider_) {
1444    delete this;
1445    return;
1446  }
1447
1448  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
1449
1450  ListValue* history_list = new ListValue;
1451  for (size_t i = 0; i < results->size(); ++i) {
1452    DictionaryValue* page_value = new DictionaryValue;
1453    history::URLResult const &page = (*results)[i];
1454    page_value->SetString("title", page.title());
1455    page_value->SetString("url", page.url().spec());
1456    page_value->SetDouble("time",
1457                          static_cast<double>(page.visit_time().ToDoubleT()));
1458    page_value->SetString("snippet", page.snippet().text());
1459    page_value->SetBoolean(
1460        "starred",
1461        provider_->profile()->GetBookmarkModel()->IsBookmarked(page.url()));
1462    history_list->Append(page_value);
1463  }
1464
1465  return_value->Set("history", history_list);
1466  // Return history info.
1467  AutomationJSONReply reply(provider_, reply_message_.release());
1468  reply.SendSuccess(return_value.get());
1469  delete this;
1470}
1471
1472AutomationProviderImportSettingsObserver::
1473AutomationProviderImportSettingsObserver(
1474    AutomationProvider* provider,
1475    IPC::Message* reply_message)
1476    : provider_(provider->AsWeakPtr()),
1477      reply_message_(reply_message) {
1478}
1479
1480AutomationProviderImportSettingsObserver::
1481~AutomationProviderImportSettingsObserver() {}
1482
1483void AutomationProviderImportSettingsObserver::ImportStarted() {
1484}
1485
1486void AutomationProviderImportSettingsObserver::ImportItemStarted(
1487    importer::ImportItem item) {
1488}
1489
1490void AutomationProviderImportSettingsObserver::ImportItemEnded(
1491    importer::ImportItem item) {
1492}
1493
1494void AutomationProviderImportSettingsObserver::ImportEnded() {
1495  if (provider_)
1496    AutomationJSONReply(provider_, reply_message_.release()).SendSuccess(NULL);
1497  delete this;
1498}
1499
1500AutomationProviderGetPasswordsObserver::AutomationProviderGetPasswordsObserver(
1501    AutomationProvider* provider,
1502    IPC::Message* reply_message)
1503    : provider_(provider->AsWeakPtr()),
1504      reply_message_(reply_message) {
1505}
1506
1507AutomationProviderGetPasswordsObserver::
1508~AutomationProviderGetPasswordsObserver() {}
1509
1510void AutomationProviderGetPasswordsObserver::OnPasswordStoreRequestDone(
1511    int handle, const std::vector<webkit_glue::PasswordForm*>& result) {
1512  if (!provider_) {
1513    delete this;
1514    return;
1515  }
1516
1517  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
1518
1519  ListValue* passwords = new ListValue;
1520  for (std::vector<webkit_glue::PasswordForm*>::const_iterator it =
1521          result.begin(); it != result.end(); ++it) {
1522    DictionaryValue* password_val = new DictionaryValue;
1523    webkit_glue::PasswordForm* password_form = *it;
1524    password_val->SetString("username_value", password_form->username_value);
1525    password_val->SetString("password_value", password_form->password_value);
1526    password_val->SetString("signon_realm", password_form->signon_realm);
1527    password_val->SetDouble(
1528        "time", static_cast<double>(password_form->date_created.ToDoubleT()));
1529    password_val->SetString("origin_url", password_form->origin.spec());
1530    password_val->SetString("username_element",
1531                            password_form->username_element);
1532    password_val->SetString("password_element",
1533                            password_form->password_element);
1534    password_val->SetString("submit_element",
1535                                     password_form->submit_element);
1536    password_val->SetString("action_target", password_form->action.spec());
1537    password_val->SetBoolean("blacklist", password_form->blacklisted_by_user);
1538    passwords->Append(password_val);
1539  }
1540
1541  return_value->Set("passwords", passwords);
1542  AutomationJSONReply(provider_, reply_message_.release()).SendSuccess(
1543      return_value.get());
1544  delete this;
1545}
1546
1547AutomationProviderBrowsingDataObserver::AutomationProviderBrowsingDataObserver(
1548    AutomationProvider* provider,
1549    IPC::Message* reply_message)
1550    : provider_(provider->AsWeakPtr()),
1551      reply_message_(reply_message) {
1552}
1553
1554AutomationProviderBrowsingDataObserver::
1555~AutomationProviderBrowsingDataObserver() {}
1556
1557void AutomationProviderBrowsingDataObserver::OnBrowsingDataRemoverDone() {
1558  if (provider_)
1559    AutomationJSONReply(provider_, reply_message_.release()).SendSuccess(NULL);
1560  delete this;
1561}
1562
1563OmniboxAcceptNotificationObserver::OmniboxAcceptNotificationObserver(
1564    NavigationController* controller,
1565    AutomationProvider* automation,
1566    IPC::Message* reply_message)
1567    : automation_(automation->AsWeakPtr()),
1568      reply_message_(reply_message),
1569      controller_(controller) {
1570  Source<NavigationController> source(controller_);
1571  registrar_.Add(this, NotificationType::LOAD_STOP, source);
1572  // Pages requiring auth don't send LOAD_STOP.
1573  registrar_.Add(this, NotificationType::AUTH_NEEDED, source);
1574}
1575
1576OmniboxAcceptNotificationObserver::~OmniboxAcceptNotificationObserver() {
1577}
1578
1579void OmniboxAcceptNotificationObserver::Observe(
1580    NotificationType type,
1581    const NotificationSource& source,
1582    const NotificationDetails& details) {
1583  if (type == NotificationType::LOAD_STOP ||
1584      type == NotificationType::AUTH_NEEDED) {
1585    if (automation_) {
1586      AutomationJSONReply(automation_,
1587                          reply_message_.release()).SendSuccess(NULL);
1588    }
1589    delete this;
1590  } else {
1591    NOTREACHED();
1592  }
1593}
1594
1595SavePackageNotificationObserver::SavePackageNotificationObserver(
1596    SavePackage* save_package,
1597    AutomationProvider* automation,
1598    IPC::Message* reply_message)
1599    : automation_(automation->AsWeakPtr()),
1600      reply_message_(reply_message) {
1601  Source<SavePackage> source(save_package);
1602  registrar_.Add(this, NotificationType::SAVE_PACKAGE_SUCCESSFULLY_FINISHED,
1603                 source);
1604}
1605
1606SavePackageNotificationObserver::~SavePackageNotificationObserver() {}
1607
1608void SavePackageNotificationObserver::Observe(
1609    NotificationType type,
1610    const NotificationSource& source,
1611    const NotificationDetails& details) {
1612  if (type == NotificationType::SAVE_PACKAGE_SUCCESSFULLY_FINISHED) {
1613    if (automation_) {
1614      AutomationJSONReply(automation_,
1615                          reply_message_.release()).SendSuccess(NULL);
1616    }
1617    delete this;
1618  } else {
1619    NOTREACHED();
1620  }
1621}
1622
1623PageSnapshotTaker::PageSnapshotTaker(AutomationProvider* automation,
1624                                     IPC::Message* reply_message,
1625                                     RenderViewHost* render_view,
1626                                     const FilePath& path)
1627    : automation_(automation->AsWeakPtr()),
1628      reply_message_(reply_message),
1629      render_view_(render_view),
1630      image_path_(path),
1631      received_width_(false) {}
1632
1633PageSnapshotTaker::~PageSnapshotTaker() {}
1634
1635void PageSnapshotTaker::Start() {
1636  ExecuteScript(L"window.domAutomationController.send(document.width);");
1637}
1638
1639void PageSnapshotTaker::OnDomOperationCompleted(const std::string& json) {
1640  int dimension;
1641  if (!base::StringToInt(json, &dimension)) {
1642    LOG(ERROR) << "Could not parse received dimensions: " << json;
1643    SendMessage(false);
1644  } else if (!received_width_) {
1645    received_width_ = true;
1646    entire_page_size_.set_width(dimension);
1647
1648    ExecuteScript(L"window.domAutomationController.send(document.height);");
1649  } else {
1650    entire_page_size_.set_height(dimension);
1651
1652    ThumbnailGenerator* generator =
1653        g_browser_process->GetThumbnailGenerator();
1654    ThumbnailGenerator::ThumbnailReadyCallback* callback =
1655        NewCallback(this, &PageSnapshotTaker::OnSnapshotTaken);
1656    // Don't actually start the thumbnail generator, this leads to crashes on
1657    // Mac, crbug.com/62986. Instead, just hook the generator to the
1658    // RenderViewHost manually.
1659
1660    generator->MonitorRenderer(render_view_, true);
1661    generator->AskForSnapshot(render_view_, false, callback,
1662                              entire_page_size_, entire_page_size_);
1663  }
1664}
1665
1666void PageSnapshotTaker::OnSnapshotTaken(const SkBitmap& bitmap) {
1667  base::ThreadRestrictions::ScopedAllowIO allow_io;
1668  std::vector<unsigned char> png_data;
1669  gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, true, &png_data);
1670  int bytes_written = file_util::WriteFile(image_path_,
1671      reinterpret_cast<char*>(&png_data[0]), png_data.size());
1672  SendMessage(bytes_written == static_cast<int>(png_data.size()));
1673}
1674
1675void PageSnapshotTaker::ExecuteScript(const std::wstring& javascript) {
1676  std::wstring set_automation_id;
1677  base::SStringPrintf(
1678      &set_automation_id,
1679      L"window.domAutomationController.setAutomationId(%d);",
1680      reply_message_->routing_id());
1681
1682  render_view_->ExecuteJavascriptInWebFrame(string16(),
1683                                            WideToUTF16Hack(set_automation_id));
1684  render_view_->ExecuteJavascriptInWebFrame(string16(),
1685                                            WideToUTF16Hack(javascript));
1686}
1687
1688void PageSnapshotTaker::SendMessage(bool success) {
1689  if (automation_) {
1690    AutomationMsg_CaptureEntirePageAsPNG::WriteReplyParams(reply_message_.get(),
1691                                                           success);
1692    automation_->Send(reply_message_.release());
1693  }
1694  delete this;
1695}
1696
1697NTPInfoObserver::NTPInfoObserver(
1698    AutomationProvider* automation,
1699    IPC::Message* reply_message,
1700    CancelableRequestConsumer* consumer)
1701    : automation_(automation->AsWeakPtr()),
1702      reply_message_(reply_message),
1703      consumer_(consumer),
1704      request_(0),
1705      ntp_info_(new DictionaryValue) {
1706  top_sites_ = automation_->profile()->GetTopSites();
1707  if (!top_sites_) {
1708    AutomationJSONReply(automation_, reply_message_.release())
1709        .SendError("Profile does not have service for querying the top sites.");
1710    return;
1711  }
1712  TabRestoreService* service = automation_->profile()->GetTabRestoreService();
1713  if (!service) {
1714    AutomationJSONReply(automation_, reply_message_.release())
1715        .SendError("No TabRestoreService.");
1716    return;
1717  }
1718
1719  // Get the info that would be displayed in the recently closed section.
1720  ListValue* recently_closed_list = new ListValue;
1721  NewTabUI::AddRecentlyClosedEntries(service->entries(),
1722                                     recently_closed_list);
1723  ntp_info_->Set("recently_closed", recently_closed_list);
1724
1725  // Add default site URLs.
1726  ListValue* default_sites_list = new ListValue;
1727  std::vector<GURL> urls = MostVisitedHandler::GetPrePopulatedUrls();
1728  for (size_t i = 0; i < urls.size(); ++i) {
1729    default_sites_list->Append(Value::CreateStringValue(
1730        urls[i].possibly_invalid_spec()));
1731  }
1732  ntp_info_->Set("default_sites", default_sites_list);
1733
1734  registrar_.Add(this, NotificationType::TOP_SITES_UPDATED,
1735                 Source<history::TopSites>(top_sites_));
1736  if (top_sites_->loaded()) {
1737    OnTopSitesLoaded();
1738  } else {
1739    registrar_.Add(this, NotificationType::TOP_SITES_LOADED,
1740                   Source<Profile>(automation_->profile()));
1741  }
1742}
1743
1744NTPInfoObserver::~NTPInfoObserver() {}
1745
1746void NTPInfoObserver::Observe(NotificationType type,
1747                              const NotificationSource& source,
1748                              const NotificationDetails& details) {
1749  if (type == NotificationType::TOP_SITES_LOADED) {
1750    OnTopSitesLoaded();
1751  } else if (type == NotificationType::TOP_SITES_UPDATED) {
1752    Details<CancelableRequestProvider::Handle> request_details(details);
1753    if (request_ == *request_details.ptr()) {
1754      top_sites_->GetMostVisitedURLs(
1755          consumer_,
1756          NewCallback(this, &NTPInfoObserver::OnTopSitesReceived));
1757    }
1758  }
1759}
1760
1761void NTPInfoObserver::OnTopSitesLoaded() {
1762  request_ = top_sites_->StartQueryForMostVisited();
1763}
1764
1765void NTPInfoObserver::OnTopSitesReceived(
1766    const history::MostVisitedURLList& visited_list) {
1767  if (!automation_) {
1768    delete this;
1769    return;
1770  }
1771
1772  ListValue* list_value = new ListValue;
1773  for (size_t i = 0; i < visited_list.size(); ++i) {
1774    const history::MostVisitedURL& visited = visited_list[i];
1775    if (visited.url.spec().empty())
1776      break;  // This is the signal that there are no more real visited sites.
1777    DictionaryValue* dict = new DictionaryValue;
1778    dict->SetString("url", visited.url.spec());
1779    dict->SetString("title", visited.title);
1780    dict->SetBoolean("is_pinned", top_sites_->IsURLPinned(visited.url));
1781    list_value->Append(dict);
1782  }
1783  ntp_info_->Set("most_visited", list_value);
1784  AutomationJSONReply(automation_,
1785                      reply_message_.release()).SendSuccess(ntp_info_.get());
1786  delete this;
1787}
1788
1789AutocompleteEditFocusedObserver::AutocompleteEditFocusedObserver(
1790    AutomationProvider* automation,
1791    AutocompleteEditModel* autocomplete_edit,
1792    IPC::Message* reply_message)
1793    : automation_(automation->AsWeakPtr()),
1794      reply_message_(reply_message),
1795      autocomplete_edit_model_(autocomplete_edit) {
1796  Source<AutocompleteEditModel> source(autocomplete_edit);
1797  registrar_.Add(this, NotificationType::AUTOCOMPLETE_EDIT_FOCUSED, source);
1798}
1799
1800AutocompleteEditFocusedObserver::~AutocompleteEditFocusedObserver() {}
1801
1802void AutocompleteEditFocusedObserver::Observe(
1803    NotificationType type,
1804    const NotificationSource& source,
1805    const NotificationDetails& details) {
1806  DCHECK(type == NotificationType::AUTOCOMPLETE_EDIT_FOCUSED);
1807  if (automation_) {
1808    AutomationMsg_WaitForAutocompleteEditFocus::WriteReplyParams(
1809        reply_message_.get(), true);
1810    automation_->Send(reply_message_.release());
1811  }
1812  delete this;
1813}
1814
1815namespace {
1816
1817// Returns whether the notification's host has a non-null process handle.
1818bool IsNotificationProcessReady(Balloon* balloon) {
1819  return balloon->view() &&
1820         balloon->view()->GetHost() &&
1821         balloon->view()->GetHost()->render_view_host() &&
1822         balloon->view()->GetHost()->render_view_host()->process()->GetHandle();
1823}
1824
1825// Returns whether all active notifications have an associated process ID.
1826bool AreActiveNotificationProcessesReady() {
1827  NotificationUIManager* manager = g_browser_process->notification_ui_manager();
1828  const BalloonCollection::Balloons& balloons =
1829      manager->balloon_collection()->GetActiveBalloons();
1830  BalloonCollection::Balloons::const_iterator iter;
1831  for (iter = balloons.begin(); iter != balloons.end(); ++iter) {
1832    if (!IsNotificationProcessReady(*iter))
1833      return false;
1834  }
1835  return true;
1836}
1837
1838}  // namespace
1839
1840GetActiveNotificationsObserver::GetActiveNotificationsObserver(
1841    AutomationProvider* automation,
1842    IPC::Message* reply_message)
1843    : reply_(automation, reply_message) {
1844  if (AreActiveNotificationProcessesReady()) {
1845    SendMessage();
1846  } else {
1847    registrar_.Add(this, NotificationType::RENDERER_PROCESS_CREATED,
1848                   NotificationService::AllSources());
1849  }
1850}
1851
1852GetActiveNotificationsObserver::~GetActiveNotificationsObserver() {}
1853
1854void GetActiveNotificationsObserver::Observe(
1855    NotificationType type,
1856    const NotificationSource& source,
1857    const NotificationDetails& details) {
1858  if (AreActiveNotificationProcessesReady())
1859    SendMessage();
1860}
1861
1862void GetActiveNotificationsObserver::SendMessage() {
1863  NotificationUIManager* manager =
1864      g_browser_process->notification_ui_manager();
1865  const BalloonCollection::Balloons& balloons =
1866      manager->balloon_collection()->GetActiveBalloons();
1867  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
1868  ListValue* list = new ListValue;
1869  return_value->Set("notifications", list);
1870  BalloonCollection::Balloons::const_iterator iter;
1871  for (iter = balloons.begin(); iter != balloons.end(); ++iter) {
1872    const Notification& notification = (*iter)->notification();
1873    DictionaryValue* balloon = new DictionaryValue;
1874    balloon->SetString("content_url", notification.content_url().spec());
1875    balloon->SetString("origin_url", notification.origin_url().spec());
1876    balloon->SetString("display_source", notification.display_source());
1877    BalloonView* view = (*iter)->view();
1878    balloon->SetInteger("pid", base::GetProcId(
1879        view->GetHost()->render_view_host()->process()->GetHandle()));
1880    list->Append(balloon);
1881  }
1882  reply_.SendSuccess(return_value.get());
1883  delete this;
1884}
1885
1886OnNotificationBalloonCountObserver::OnNotificationBalloonCountObserver(
1887    AutomationProvider* provider,
1888    IPC::Message* reply_message,
1889    BalloonCollection* collection,
1890    int count)
1891    : reply_(provider, reply_message),
1892      collection_(collection),
1893      count_(count) {
1894  collection->set_on_collection_changed_callback(NewCallback(
1895      this, &OnNotificationBalloonCountObserver::OnBalloonCollectionChanged));
1896}
1897
1898void OnNotificationBalloonCountObserver::OnBalloonCollectionChanged() {
1899  if (static_cast<int>(collection_->GetActiveBalloons().size()) == count_) {
1900    collection_->set_on_collection_changed_callback(NULL);
1901    reply_.SendSuccess(NULL);
1902    delete this;
1903  }
1904}
1905
1906RendererProcessClosedObserver::RendererProcessClosedObserver(
1907    AutomationProvider* automation,
1908    IPC::Message* reply_message)
1909    : automation_(automation->AsWeakPtr()),
1910      reply_message_(reply_message) {
1911  registrar_.Add(this, NotificationType::RENDERER_PROCESS_CLOSED,
1912                 NotificationService::AllSources());
1913}
1914
1915RendererProcessClosedObserver::~RendererProcessClosedObserver() {}
1916
1917void RendererProcessClosedObserver::Observe(
1918    NotificationType type,
1919    const NotificationSource& source,
1920    const NotificationDetails& details) {
1921  if (automation_) {
1922    AutomationJSONReply(automation_,
1923                        reply_message_.release()).SendSuccess(NULL);
1924  }
1925  delete this;
1926}
1927
1928InputEventAckNotificationObserver::InputEventAckNotificationObserver(
1929    AutomationProvider* automation,
1930    IPC::Message* reply_message,
1931    int event_type)
1932    : automation_(automation->AsWeakPtr()),
1933      reply_message_(reply_message),
1934      event_type_(event_type) {
1935  registrar_.Add(
1936      this, NotificationType::RENDER_WIDGET_HOST_DID_RECEIVE_INPUT_EVENT_ACK,
1937      NotificationService::AllSources());
1938}
1939
1940InputEventAckNotificationObserver::~InputEventAckNotificationObserver() {}
1941
1942void InputEventAckNotificationObserver::Observe(
1943    NotificationType type,
1944    const NotificationSource& source,
1945    const NotificationDetails& details) {
1946  Details<int> request_details(details);
1947  if (event_type_ == *request_details.ptr()) {
1948    if (automation_) {
1949      AutomationJSONReply(automation_,
1950                          reply_message_.release()).SendSuccess(NULL);
1951    }
1952    delete this;
1953  } else {
1954    LOG(WARNING) << "Ignoring unexpected event types.";
1955  }
1956}
1957
1958NewTabObserver::NewTabObserver(AutomationProvider* automation,
1959                               IPC::Message* reply_message)
1960    : automation_(automation->AsWeakPtr()),
1961      reply_message_(reply_message) {
1962  // Use TAB_PARENTED to detect the new tab.
1963  registrar_.Add(this,
1964                 NotificationType::TAB_PARENTED,
1965                 NotificationService::AllSources());
1966}
1967
1968void NewTabObserver::Observe(NotificationType type,
1969                             const NotificationSource& source,
1970                             const NotificationDetails& details) {
1971  DCHECK_EQ(NotificationType::TAB_PARENTED, type.value);
1972  NavigationController* controller = Source<NavigationController>(source).ptr();
1973  if (automation_) {
1974    // TODO(phajdan.jr): Clean up this hack. We write the correct return type
1975    // here, but don't send the message. NavigationNotificationObserver
1976    // will wait properly for the load to finish, and send the message,
1977    // but it will also append its own return value at the end of the reply.
1978    AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_.get(),
1979                                                         true);
1980    new NavigationNotificationObserver(controller, automation_,
1981                                       reply_message_.release(),
1982                                       1, false);
1983  }
1984  delete this;
1985}
1986
1987NewTabObserver::~NewTabObserver() {
1988}
1989
1990WaitForProcessLauncherThreadToGoIdleObserver::
1991WaitForProcessLauncherThreadToGoIdleObserver(
1992    AutomationProvider* automation, IPC::Message* reply_message)
1993    : automation_(automation->AsWeakPtr()),
1994      reply_message_(reply_message) {
1995  // Balanced in RunOnUIThread.
1996  AddRef();
1997  BrowserThread::PostTask(
1998      BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
1999      NewRunnableMethod(
2000          this,
2001          &WaitForProcessLauncherThreadToGoIdleObserver::
2002              RunOnProcessLauncherThread));
2003}
2004
2005WaitForProcessLauncherThreadToGoIdleObserver::
2006~WaitForProcessLauncherThreadToGoIdleObserver() {
2007}
2008
2009void WaitForProcessLauncherThreadToGoIdleObserver::
2010RunOnProcessLauncherThread() {
2011  BrowserThread::PostTask(
2012      BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
2013      NewRunnableMethod(
2014          this,
2015          &WaitForProcessLauncherThreadToGoIdleObserver::RunOnUIThread));
2016}
2017
2018void WaitForProcessLauncherThreadToGoIdleObserver::RunOnUIThread() {
2019  if (automation_)
2020    automation_->Send(reply_message_.release());
2021  Release();
2022}
2023