automation_provider_observers.cc revision 513209b27ff55e2841eac0e4120199c23acce758
1// Copyright (c) 2010 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
9#include "base/basictypes.h"
10#include "base/callback.h"
11#include "base/json/json_writer.h"
12#include "base/scoped_ptr.h"
13#include "base/string_util.h"
14#include "base/values.h"
15#include "chrome/app/chrome_command_ids.h"
16#include "chrome/browser/automation/automation_provider.h"
17#include "chrome/browser/automation/automation_provider_json.h"
18#include "chrome/browser/bookmarks/bookmark_model.h"
19#include "chrome/browser/browser.h"
20#include "chrome/browser/browser_list.h"
21#include "chrome/browser/dom_operation_notification_details.h"
22#include "chrome/browser/download/download_item.h"
23#include "chrome/browser/download/save_package.h"
24#include "chrome/browser/extensions/extension_host.h"
25#include "chrome/browser/extensions/extension_process_manager.h"
26#include "chrome/browser/extensions/extension_updater.h"
27#include "chrome/browser/login_prompt.h"
28#include "chrome/browser/metrics/metric_event_duration_details.h"
29#include "chrome/browser/notifications/balloon.h"
30#include "chrome/browser/notifications/balloon_collection.h"
31#include "chrome/browser/printing/print_job.h"
32#include "chrome/browser/profile.h"
33#include "chrome/browser/search_engines/template_url_model.h"
34#include "chrome/browser/tab_contents/navigation_controller.h"
35#include "chrome/browser/tab_contents/tab_contents.h"
36#include "chrome/browser/translate/page_translated_details.h"
37#include "chrome/browser/translate/translate_infobar_delegate.h"
38#include "chrome/common/extensions/extension.h"
39#include "chrome/common/notification_service.h"
40#include "chrome/test/automation/automation_constants.h"
41#include "gfx/rect.h"
42
43#if defined(OS_CHROMEOS)
44#include "chrome/browser/chromeos/login/authentication_notification_details.h"
45#endif
46
47// Holds onto start and stop timestamps for a particular tab
48class InitialLoadObserver::TabTime {
49 public:
50  explicit TabTime(base::TimeTicks started)
51      : load_start_time_(started) {
52  }
53  void set_stop_time(base::TimeTicks stopped) {
54    load_stop_time_ = stopped;
55  }
56  base::TimeTicks stop_time() const {
57    return load_stop_time_;
58  }
59  base::TimeTicks start_time() const {
60    return load_start_time_;
61  }
62 private:
63  base::TimeTicks load_start_time_;
64  base::TimeTicks load_stop_time_;
65};
66
67InitialLoadObserver::InitialLoadObserver(size_t tab_count,
68                                         AutomationProvider* automation)
69    : automation_(automation),
70      outstanding_tab_count_(tab_count),
71      init_time_(base::TimeTicks::Now()) {
72  if (outstanding_tab_count_ > 0) {
73    registrar_.Add(this, NotificationType::LOAD_START,
74                   NotificationService::AllSources());
75    registrar_.Add(this, NotificationType::LOAD_STOP,
76                   NotificationService::AllSources());
77  }
78}
79
80InitialLoadObserver::~InitialLoadObserver() {
81}
82
83void InitialLoadObserver::Observe(NotificationType type,
84                                  const NotificationSource& source,
85                                  const NotificationDetails& details) {
86  if (type == NotificationType::LOAD_START) {
87    if (outstanding_tab_count_ > loading_tabs_.size())
88      loading_tabs_.insert(TabTimeMap::value_type(
89          source.map_key(),
90          TabTime(base::TimeTicks::Now())));
91  } else if (type == NotificationType::LOAD_STOP) {
92    if (outstanding_tab_count_ > finished_tabs_.size()) {
93      TabTimeMap::iterator iter = loading_tabs_.find(source.map_key());
94      if (iter != loading_tabs_.end()) {
95        finished_tabs_.insert(source.map_key());
96        iter->second.set_stop_time(base::TimeTicks::Now());
97      }
98      if (outstanding_tab_count_ == finished_tabs_.size())
99        ConditionMet();
100    }
101  } else {
102    NOTREACHED();
103  }
104}
105
106DictionaryValue* InitialLoadObserver::GetTimingInformation() const {
107  ListValue* items = new ListValue;
108  for (TabTimeMap::const_iterator it = loading_tabs_.begin();
109       it != loading_tabs_.end();
110       ++it) {
111    DictionaryValue* item = new DictionaryValue;
112    base::TimeDelta delta_start = it->second.start_time() - init_time_;
113
114    item->SetReal("load_start_ms", delta_start.InMillisecondsF());
115    if (it->second.stop_time().is_null()) {
116      item->Set("load_stop_ms", Value::CreateNullValue());
117    } else {
118      base::TimeDelta delta_stop = it->second.stop_time() - init_time_;
119      item->SetReal("load_stop_ms", delta_stop.InMillisecondsF());
120    }
121    items->Append(item);
122  }
123  DictionaryValue* return_value = new DictionaryValue;
124  return_value->Set("tabs", items);
125  return return_value;
126}
127
128void InitialLoadObserver::ConditionMet() {
129  registrar_.RemoveAll();
130  automation_->Send(new AutomationMsg_InitialLoadsComplete(0));
131}
132
133NewTabUILoadObserver::NewTabUILoadObserver(AutomationProvider* automation)
134    : automation_(automation) {
135  registrar_.Add(this, NotificationType::INITIAL_NEW_TAB_UI_LOAD,
136                 NotificationService::AllSources());
137}
138
139NewTabUILoadObserver::~NewTabUILoadObserver() {
140}
141
142void NewTabUILoadObserver::Observe(NotificationType type,
143                                   const NotificationSource& source,
144                                   const NotificationDetails& details) {
145  if (type == NotificationType::INITIAL_NEW_TAB_UI_LOAD) {
146    Details<int> load_time(details);
147    automation_->Send(
148        new AutomationMsg_InitialNewTabUILoadComplete(0, *load_time.ptr()));
149  } else {
150    NOTREACHED();
151  }
152}
153
154NavigationControllerRestoredObserver::NavigationControllerRestoredObserver(
155    AutomationProvider* automation,
156    NavigationController* controller,
157    IPC::Message* reply_message)
158    : automation_(automation),
159      controller_(controller),
160      reply_message_(reply_message) {
161  if (FinishedRestoring()) {
162    SendDone();
163  } else {
164    registrar_.Add(this, NotificationType::LOAD_STOP,
165                   NotificationService::AllSources());
166  }
167}
168
169NavigationControllerRestoredObserver::~NavigationControllerRestoredObserver() {
170}
171
172void NavigationControllerRestoredObserver::Observe(
173    NotificationType type, const NotificationSource& source,
174    const NotificationDetails& details) {
175  if (FinishedRestoring()) {
176    SendDone();
177    registrar_.RemoveAll();
178  }
179}
180
181bool NavigationControllerRestoredObserver::FinishedRestoring() {
182  return (!controller_->needs_reload() && !controller_->pending_entry() &&
183          !controller_->tab_contents()->is_loading());
184}
185
186void NavigationControllerRestoredObserver::SendDone() {
187  DCHECK(reply_message_ != NULL);
188  automation_->Send(reply_message_);
189}
190
191NavigationNotificationObserver::NavigationNotificationObserver(
192    NavigationController* controller,
193    AutomationProvider* automation,
194    IPC::Message* reply_message,
195    int number_of_navigations,
196    bool include_current_navigation)
197  : automation_(automation),
198    reply_message_(reply_message),
199    controller_(controller),
200    navigations_remaining_(number_of_navigations),
201    navigation_started_(false) {
202  DCHECK_LT(0, navigations_remaining_);
203  Source<NavigationController> source(controller_);
204  registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED, source);
205  registrar_.Add(this, NotificationType::LOAD_START, source);
206  registrar_.Add(this, NotificationType::LOAD_STOP, source);
207  registrar_.Add(this, NotificationType::AUTH_NEEDED, source);
208  registrar_.Add(this, NotificationType::AUTH_SUPPLIED, source);
209  registrar_.Add(this, NotificationType::AUTH_CANCELLED, source);
210
211  if (include_current_navigation && controller->tab_contents()->is_loading())
212    navigation_started_ = true;
213}
214
215NavigationNotificationObserver::~NavigationNotificationObserver() {
216  if (reply_message_) {
217    // This means we did not receive a notification for this navigation.
218    // Send over a failed navigation status back to the caller to ensure that
219    // the caller does not hang waiting for the response.
220    IPC::ParamTraits<AutomationMsg_NavigationResponseValues>::Write(
221        reply_message_, AUTOMATION_MSG_NAVIGATION_ERROR);
222    automation_->Send(reply_message_);
223    reply_message_ = NULL;
224  }
225
226  automation_->RemoveNavigationStatusListener(this);
227}
228
229void NavigationNotificationObserver::Observe(
230    NotificationType type, const NotificationSource& source,
231    const NotificationDetails& details) {
232  // We listen for 2 events to determine when the navigation started because:
233  // - when this is used by the WaitForNavigation method, we might be invoked
234  // afer the load has started (but not after the entry was committed, as
235  // WaitForNavigation compares times of the last navigation).
236  // - when this is used with a page requiring authentication, we will not get
237  // a NotificationType::NAV_ENTRY_COMMITTED until after we authenticate, so
238  // we need the NotificationType::LOAD_START.
239  if (type == NotificationType::NAV_ENTRY_COMMITTED ||
240      type == NotificationType::LOAD_START) {
241    navigation_started_ = true;
242  } else if (type == NotificationType::LOAD_STOP) {
243    if (navigation_started_) {
244      navigation_started_ = false;
245      if (--navigations_remaining_ == 0)
246        ConditionMet(AUTOMATION_MSG_NAVIGATION_SUCCESS);
247    }
248  } else if (type == NotificationType::AUTH_SUPPLIED ||
249             type == NotificationType::AUTH_CANCELLED) {
250    // The LoginHandler for this tab is no longer valid.
251    automation_->RemoveLoginHandler(controller_);
252
253    // Treat this as if navigation started again, since load start/stop don't
254    // occur while authentication is ongoing.
255    navigation_started_ = true;
256  } else if (type == NotificationType::AUTH_NEEDED) {
257    // Remember the login handler that wants authentication.
258    // We do this in all cases (not just when navigation_started_ == true) so
259    // tests can still wait for auth dialogs outside of navigation.
260    LoginHandler* handler =
261        Details<LoginNotificationDetails>(details)->handler();
262    automation_->AddLoginHandler(controller_, handler);
263
264    // Respond that authentication is needed.
265    navigation_started_ = false;
266    ConditionMet(AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED);
267  } else {
268    NOTREACHED();
269  }
270}
271
272void NavigationNotificationObserver::ConditionMet(
273    AutomationMsg_NavigationResponseValues navigation_result) {
274  DCHECK(reply_message_ != NULL);
275
276  IPC::ParamTraits<AutomationMsg_NavigationResponseValues>::Write(
277      reply_message_, navigation_result);
278  automation_->Send(reply_message_);
279  reply_message_ = NULL;
280
281  delete this;
282}
283
284TabStripNotificationObserver::TabStripNotificationObserver(
285    NotificationType notification, AutomationProvider* automation)
286    : automation_(automation),
287      notification_(notification) {
288  registrar_.Add(this, notification_, NotificationService::AllSources());
289}
290
291TabStripNotificationObserver::~TabStripNotificationObserver() {
292}
293
294void TabStripNotificationObserver::Observe(NotificationType type,
295                                           const NotificationSource& source,
296                                           const NotificationDetails& details) {
297  if (type == notification_) {
298    ObserveTab(Source<NavigationController>(source).ptr());
299
300    // If verified, no need to observe anymore
301    automation_->RemoveTabStripObserver(this);
302    delete this;
303  } else {
304    NOTREACHED();
305  }
306}
307
308TabAppendedNotificationObserver::TabAppendedNotificationObserver(
309    Browser* parent, AutomationProvider* automation,
310    IPC::Message* reply_message)
311    : TabStripNotificationObserver(NotificationType::TAB_PARENTED, automation),
312      parent_(parent),
313      reply_message_(reply_message) {
314}
315
316void TabAppendedNotificationObserver::ObserveTab(
317    NavigationController* controller) {
318  if (automation_->GetIndexForNavigationController(controller, parent_) ==
319      TabStripModel::kNoTab) {
320    // This tab notification doesn't belong to the parent_.
321    return;
322  }
323
324  automation_->AddNavigationStatusListener(controller, reply_message_, 1,
325                                           false);
326}
327
328TabClosedNotificationObserver::TabClosedNotificationObserver(
329    AutomationProvider* automation, bool wait_until_closed,
330    IPC::Message* reply_message)
331    : TabStripNotificationObserver(wait_until_closed ?
332          NotificationType::TAB_CLOSED : NotificationType::TAB_CLOSING,
333          automation),
334      reply_message_(reply_message),
335      for_browser_command_(false) {
336}
337
338void TabClosedNotificationObserver::ObserveTab(
339    NavigationController* controller) {
340  if (for_browser_command_) {
341    AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_,
342                                                         true);
343  } else {
344    AutomationMsg_CloseTab::WriteReplyParams(reply_message_, true);
345  }
346  automation_->Send(reply_message_);
347}
348
349void TabClosedNotificationObserver::set_for_browser_command(
350    bool for_browser_command) {
351  for_browser_command_ = for_browser_command;
352}
353
354TabCountChangeObserver::TabCountChangeObserver(AutomationProvider* automation,
355                                               Browser* browser,
356                                               IPC::Message* reply_message,
357                                               int target_tab_count)
358    : automation_(automation),
359      reply_message_(reply_message),
360      tab_strip_model_(browser->tabstrip_model()),
361      target_tab_count_(target_tab_count) {
362  tab_strip_model_->AddObserver(this);
363  CheckTabCount();
364}
365
366TabCountChangeObserver::~TabCountChangeObserver() {
367  tab_strip_model_->RemoveObserver(this);
368}
369
370void TabCountChangeObserver::TabInsertedAt(TabContents* contents,
371                                           int index,
372                                           bool foreground) {
373  CheckTabCount();
374}
375
376void TabCountChangeObserver::TabDetachedAt(TabContents* contents, int index) {
377  CheckTabCount();
378}
379
380void TabCountChangeObserver::TabStripModelDeleted() {
381  AutomationMsg_WaitForTabCountToBecome::WriteReplyParams(reply_message_,
382                                                          false);
383  automation_->Send(reply_message_);
384  delete this;
385}
386
387void TabCountChangeObserver::CheckTabCount() {
388  if (tab_strip_model_->count() != target_tab_count_)
389    return;
390
391  AutomationMsg_WaitForTabCountToBecome::WriteReplyParams(reply_message_,
392                                                          true);
393  automation_->Send(reply_message_);
394  delete this;
395}
396
397bool DidExtensionHostsStopLoading(ExtensionProcessManager* manager) {
398  for (ExtensionProcessManager::const_iterator iter = manager->begin();
399       iter != manager->end(); ++iter) {
400    if (!(*iter)->did_stop_loading())
401      return false;
402  }
403  return true;
404}
405
406ExtensionInstallNotificationObserver::ExtensionInstallNotificationObserver(
407    AutomationProvider* automation, int id, IPC::Message* reply_message)
408    : automation_(automation),
409      id_(id),
410      reply_message_(reply_message) {
411  registrar_.Add(this, NotificationType::EXTENSION_LOADED,
412                 NotificationService::AllSources());
413  registrar_.Add(this, NotificationType::EXTENSION_INSTALL_ERROR,
414                 NotificationService::AllSources());
415  registrar_.Add(this, NotificationType::EXTENSION_UPDATE_DISABLED,
416                 NotificationService::AllSources());
417}
418
419ExtensionInstallNotificationObserver::~ExtensionInstallNotificationObserver() {
420}
421
422void ExtensionInstallNotificationObserver::Observe(
423    NotificationType type, const NotificationSource& source,
424    const NotificationDetails& details) {
425  switch (type.value) {
426    case NotificationType::EXTENSION_LOADED:
427      SendResponse(AUTOMATION_MSG_EXTENSION_INSTALL_SUCCEEDED);
428      break;
429    case NotificationType::EXTENSION_INSTALL_ERROR:
430    case NotificationType::EXTENSION_UPDATE_DISABLED:
431      SendResponse(AUTOMATION_MSG_EXTENSION_INSTALL_FAILED);
432      break;
433    default:
434      NOTREACHED();
435      break;
436  }
437
438  delete this;
439}
440
441void ExtensionInstallNotificationObserver::SendResponse(
442    AutomationMsg_ExtensionResponseValues response) {
443  if (reply_message_ != NULL) {
444    switch (id_) {
445      case AutomationMsg_InstallExtension::ID:
446        AutomationMsg_InstallExtension::WriteReplyParams(reply_message_,
447                                                         response);
448        break;
449      case AutomationMsg_LoadExpandedExtension::ID:
450        AutomationMsg_LoadExpandedExtension::WriteReplyParams(reply_message_,
451                                                              response);
452        break;
453      default:
454        NOTREACHED();
455        break;
456    }
457
458    automation_->Send(reply_message_);
459    reply_message_ = NULL;
460  }
461}
462
463ExtensionReadyNotificationObserver::ExtensionReadyNotificationObserver(
464    ExtensionProcessManager* manager, AutomationProvider* automation, int id,
465    IPC::Message* reply_message)
466    : manager_(manager),
467      automation_(automation),
468      id_(id),
469      reply_message_(reply_message),
470      extension_(NULL) {
471  registrar_.Add(this, NotificationType::EXTENSION_HOST_DID_STOP_LOADING,
472                 NotificationService::AllSources());
473  registrar_.Add(this, NotificationType::EXTENSION_LOADED,
474                 NotificationService::AllSources());
475  registrar_.Add(this, NotificationType::EXTENSION_INSTALL_ERROR,
476                 NotificationService::AllSources());
477  registrar_.Add(this, NotificationType::EXTENSION_UPDATE_DISABLED,
478                 NotificationService::AllSources());
479}
480
481ExtensionReadyNotificationObserver::~ExtensionReadyNotificationObserver() {
482}
483
484void ExtensionReadyNotificationObserver::Observe(
485    NotificationType type, const NotificationSource& source,
486    const NotificationDetails& details) {
487  bool success = false;
488  switch (type.value) {
489    case NotificationType::EXTENSION_HOST_DID_STOP_LOADING:
490      // Only continue on with this method if our extension has been loaded
491      // and all the extension hosts have stopped loading.
492      if (!extension_ || !DidExtensionHostsStopLoading(manager_))
493        return;
494      success = true;
495      break;
496    case NotificationType::EXTENSION_LOADED:
497      extension_ = Details<const Extension>(details).ptr();
498      if (!DidExtensionHostsStopLoading(manager_))
499        return;
500      success = true;
501      break;
502    case NotificationType::EXTENSION_INSTALL_ERROR:
503    case NotificationType::EXTENSION_UPDATE_DISABLED:
504      success = false;
505      break;
506    default:
507      NOTREACHED();
508      break;
509  }
510
511  if (id_ == AutomationMsg_InstallExtensionAndGetHandle::ID) {
512    // A handle of zero indicates an error.
513    int extension_handle = 0;
514    if (extension_)
515      extension_handle = automation_->AddExtension(extension_);
516    AutomationMsg_InstallExtensionAndGetHandle::WriteReplyParams(
517        reply_message_, extension_handle);
518  } else if (id_ == AutomationMsg_EnableExtension::ID) {
519    AutomationMsg_EnableExtension::WriteReplyParams(reply_message_, true);
520  } else {
521    NOTREACHED();
522    LOG(ERROR) << "Cannot write reply params for unknown message id.";
523  }
524
525  automation_->Send(reply_message_);
526  delete this;
527}
528
529ExtensionUnloadNotificationObserver::ExtensionUnloadNotificationObserver()
530    : did_receive_unload_notification_(false) {
531  registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
532                 NotificationService::AllSources());
533  registrar_.Add(this, NotificationType::EXTENSION_UNLOADED_DISABLED,
534                 NotificationService::AllSources());
535}
536
537ExtensionUnloadNotificationObserver::~ExtensionUnloadNotificationObserver() {
538}
539
540void ExtensionUnloadNotificationObserver::Observe(
541    NotificationType type, const NotificationSource& source,
542    const NotificationDetails& details) {
543  if (type.value == NotificationType::EXTENSION_UNLOADED ||
544      type.value == NotificationType::EXTENSION_UNLOADED_DISABLED) {
545    did_receive_unload_notification_ = true;
546  } else {
547    NOTREACHED();
548  }
549}
550
551ExtensionTestResultNotificationObserver::
552    ExtensionTestResultNotificationObserver(AutomationProvider* automation)
553    : automation_(automation) {
554  registrar_.Add(this, NotificationType::EXTENSION_TEST_PASSED,
555                 NotificationService::AllSources());
556  registrar_.Add(this, NotificationType::EXTENSION_TEST_FAILED,
557                 NotificationService::AllSources());
558}
559
560ExtensionTestResultNotificationObserver::
561    ~ExtensionTestResultNotificationObserver() {
562}
563
564void ExtensionTestResultNotificationObserver::Observe(
565    NotificationType type, const NotificationSource& source,
566    const NotificationDetails& details) {
567  switch (type.value) {
568    case NotificationType::EXTENSION_TEST_PASSED:
569      results_.push_back(true);
570      messages_.push_back("");
571      break;
572
573    case NotificationType::EXTENSION_TEST_FAILED:
574      results_.push_back(false);
575      messages_.push_back(*(Details<std::string>(details).ptr()));
576      break;
577
578    default:
579      NOTREACHED();
580  }
581  // There may be a reply message waiting for this event, so check.
582  MaybeSendResult();
583}
584
585void ExtensionTestResultNotificationObserver::MaybeSendResult() {
586  if (results_.size() > 0) {
587    // This release method should return the automation's current
588    // reply message, or NULL if there is no current one. If it is not
589    // NULL, we are stating that we will handle this reply message.
590    IPC::Message* reply_message = automation_->reply_message_release();
591    // Send the result back if we have a reply message.
592    if (reply_message) {
593      AutomationMsg_WaitForExtensionTestResult::WriteReplyParams(
594          reply_message, results_.front(), messages_.front());
595      results_.pop_front();
596      messages_.pop_front();
597      automation_->Send(reply_message);
598    }
599  }
600}
601
602BrowserOpenedNotificationObserver::BrowserOpenedNotificationObserver(
603    AutomationProvider* automation, IPC::Message* reply_message)
604    : automation_(automation),
605      reply_message_(reply_message),
606      for_browser_command_(false) {
607  registrar_.Add(this, NotificationType::BROWSER_OPENED,
608                 NotificationService::AllSources());
609}
610
611BrowserOpenedNotificationObserver::~BrowserOpenedNotificationObserver() {
612}
613
614void BrowserOpenedNotificationObserver::Observe(
615    NotificationType type, const NotificationSource& source,
616    const NotificationDetails& details) {
617  if (type == NotificationType::BROWSER_OPENED) {
618    if (for_browser_command_) {
619      AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_,
620                                                           true);
621    }
622    automation_->Send(reply_message_);
623    delete this;
624  } else {
625    NOTREACHED();
626  }
627}
628
629void BrowserOpenedNotificationObserver::set_for_browser_command(
630    bool for_browser_command) {
631  for_browser_command_ = for_browser_command;
632}
633
634BrowserClosedNotificationObserver::BrowserClosedNotificationObserver(
635    Browser* browser,
636    AutomationProvider* automation,
637    IPC::Message* reply_message)
638    : automation_(automation),
639      reply_message_(reply_message),
640      for_browser_command_(false) {
641  registrar_.Add(this, NotificationType::BROWSER_CLOSED,
642                 Source<Browser>(browser));
643}
644
645void BrowserClosedNotificationObserver::Observe(
646    NotificationType type, const NotificationSource& source,
647    const NotificationDetails& details) {
648  DCHECK(type == NotificationType::BROWSER_CLOSED);
649  Details<bool> close_app(details);
650  DCHECK(reply_message_ != NULL);
651  if (for_browser_command_) {
652    AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_,
653                                                         true);
654  } else {
655    AutomationMsg_CloseBrowser::WriteReplyParams(reply_message_, true,
656                                                 *(close_app.ptr()));
657  }
658  automation_->Send(reply_message_);
659  reply_message_ = NULL;
660  delete this;
661}
662
663void BrowserClosedNotificationObserver::set_for_browser_command(
664    bool for_browser_command) {
665  for_browser_command_ = for_browser_command;
666}
667
668BrowserCountChangeNotificationObserver::BrowserCountChangeNotificationObserver(
669    int target_count,
670    AutomationProvider* automation,
671    IPC::Message* reply_message)
672    : target_count_(target_count),
673      automation_(automation),
674      reply_message_(reply_message) {
675  registrar_.Add(this, NotificationType::BROWSER_OPENED,
676                 NotificationService::AllSources());
677  registrar_.Add(this, NotificationType::BROWSER_CLOSED,
678                 NotificationService::AllSources());
679}
680
681void BrowserCountChangeNotificationObserver::Observe(
682    NotificationType type, const NotificationSource& source,
683    const NotificationDetails& details) {
684  DCHECK(type == NotificationType::BROWSER_OPENED ||
685         type == NotificationType::BROWSER_CLOSED);
686  int current_count = static_cast<int>(BrowserList::size());
687  if (type == NotificationType::BROWSER_CLOSED) {
688    // At the time of the notification the browser being closed is not removed
689    // from the list. The real count is one less than the reported count.
690    DCHECK_LT(0, current_count);
691    current_count--;
692  }
693  if (current_count == target_count_) {
694    AutomationMsg_WaitForBrowserWindowCountToBecome::WriteReplyParams(
695        reply_message_, true);
696    automation_->Send(reply_message_);
697    reply_message_ = NULL;
698    delete this;
699  }
700}
701
702AppModalDialogShownObserver::AppModalDialogShownObserver(
703    AutomationProvider* automation, IPC::Message* reply_message)
704    : automation_(automation),
705      reply_message_(reply_message) {
706  registrar_.Add(this, NotificationType::APP_MODAL_DIALOG_SHOWN,
707                 NotificationService::AllSources());
708}
709
710AppModalDialogShownObserver::~AppModalDialogShownObserver() {
711}
712
713void AppModalDialogShownObserver::Observe(
714    NotificationType type, const NotificationSource& source,
715    const NotificationDetails& details) {
716  DCHECK(type == NotificationType::APP_MODAL_DIALOG_SHOWN);
717
718  AutomationMsg_WaitForAppModalDialogToBeShown::WriteReplyParams(
719      reply_message_, true);
720  automation_->Send(reply_message_);
721  reply_message_ = NULL;
722  delete this;
723}
724
725namespace {
726
727// Define mapping from command to notification
728struct CommandNotification {
729  int command;
730  NotificationType::Type notification_type;
731};
732
733const struct CommandNotification command_notifications[] = {
734  {IDC_DUPLICATE_TAB, NotificationType::TAB_PARENTED},
735  {IDC_NEW_TAB, NotificationType::INITIAL_NEW_TAB_UI_LOAD},
736
737  // Returns as soon as the restored tab is created. To further wait until
738  // the content page is loaded, use WaitForTabToBeRestored.
739  {IDC_RESTORE_TAB, NotificationType::TAB_PARENTED},
740
741  // For the following commands, we need to wait for a new tab to be created,
742  // load to finish, and title to change.
743  {IDC_MANAGE_EXTENSIONS, NotificationType::TAB_CONTENTS_TITLE_UPDATED},
744  {IDC_OPTIONS, NotificationType::TAB_CONTENTS_TITLE_UPDATED},
745  {IDC_SHOW_DOWNLOADS, NotificationType::TAB_CONTENTS_TITLE_UPDATED},
746  {IDC_SHOW_HISTORY, NotificationType::TAB_CONTENTS_TITLE_UPDATED},
747};
748
749}  // namespace
750
751ExecuteBrowserCommandObserver::~ExecuteBrowserCommandObserver() {
752}
753
754// static
755bool ExecuteBrowserCommandObserver::CreateAndRegisterObserver(
756    AutomationProvider* automation, Browser* browser, int command,
757    IPC::Message* reply_message) {
758  bool result = true;
759  switch (command) {
760    case IDC_NEW_WINDOW:
761    case IDC_NEW_INCOGNITO_WINDOW: {
762      BrowserOpenedNotificationObserver* observer =
763          new BrowserOpenedNotificationObserver(automation, reply_message);
764      observer->set_for_browser_command(true);
765      break;
766    }
767    case IDC_CLOSE_WINDOW: {
768      BrowserClosedNotificationObserver* observer =
769          new BrowserClosedNotificationObserver(browser, automation,
770                                                reply_message);
771      observer->set_for_browser_command(true);
772      break;
773    }
774    case IDC_CLOSE_TAB: {
775      TabClosedNotificationObserver* observer =
776          new TabClosedNotificationObserver(automation, true, reply_message);
777      observer->set_for_browser_command(true);
778      break;
779    }
780    case IDC_BACK:
781    case IDC_FORWARD:
782    case IDC_RELOAD: {
783      automation->AddNavigationStatusListener(
784          &browser->GetSelectedTabContents()->controller(),
785          reply_message, 1, false);
786      break;
787    }
788    default: {
789      ExecuteBrowserCommandObserver* observer =
790          new ExecuteBrowserCommandObserver(automation, reply_message);
791      if (!observer->Register(command)) {
792        delete observer;
793        result = false;
794      }
795      break;
796    }
797  }
798  return result;
799}
800
801void ExecuteBrowserCommandObserver::Observe(
802    NotificationType type, const NotificationSource& source,
803    const NotificationDetails& details) {
804  if (type == notification_type_) {
805    AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_,
806                                                         true);
807    automation_->Send(reply_message_);
808    delete this;
809  } else {
810    NOTREACHED();
811  }
812}
813
814ExecuteBrowserCommandObserver::ExecuteBrowserCommandObserver(
815    AutomationProvider* automation, IPC::Message* reply_message)
816    : automation_(automation),
817      notification_type_(NotificationType::ALL),
818      reply_message_(reply_message) {
819}
820
821bool ExecuteBrowserCommandObserver::Register(int command) {
822  if (!GetNotificationType(command, &notification_type_))
823    return false;
824  registrar_.Add(this, notification_type_, NotificationService::AllSources());
825  return true;
826}
827
828bool ExecuteBrowserCommandObserver::GetNotificationType(
829    int command, NotificationType::Type* type) {
830  if (!type)
831    return false;
832  bool found = false;
833  for (unsigned int i = 0; i < arraysize(command_notifications); i++) {
834    if (command_notifications[i].command == command) {
835      *type = command_notifications[i].notification_type;
836      found = true;
837      break;
838    }
839  }
840  return found;
841}
842
843FindInPageNotificationObserver::FindInPageNotificationObserver(
844    AutomationProvider* automation, TabContents* parent_tab,
845    bool reply_with_json, IPC::Message* reply_message)
846    : automation_(automation),
847      active_match_ordinal_(-1),
848      reply_with_json_(reply_with_json),
849      reply_message_(reply_message) {
850  registrar_.Add(this, NotificationType::FIND_RESULT_AVAILABLE,
851                 Source<TabContents>(parent_tab));
852}
853
854FindInPageNotificationObserver::~FindInPageNotificationObserver() {
855}
856
857void FindInPageNotificationObserver::Observe(
858    NotificationType type, const NotificationSource& source,
859    const NotificationDetails& details) {
860  Details<FindNotificationDetails> find_details(details);
861  if (!(find_details->final_update() && reply_message_ != NULL)) {
862    DVLOG(1) << "Ignoring, since we only care about the final message";
863    return;
864  }
865  // We get multiple responses and one of those will contain the ordinal.
866  // This message comes to us before the final update is sent.
867  if (find_details->request_id() == kFindInPageRequestId) {
868    if (reply_with_json_) {
869      scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
870      return_value->SetInteger("match_count",
871          find_details->number_of_matches());
872      gfx::Rect rect = find_details->selection_rect();
873      // If MatchCount is > 0, then rect should not be Empty.
874      // We dont guard it here because we want to let the test
875      // code catch this invalid case if needed.
876      if (!rect.IsEmpty()) {
877        return_value->SetInteger("match_left", rect.x());
878        return_value->SetInteger("match_top", rect.y());
879        return_value->SetInteger("match_right", rect.right());
880        return_value->SetInteger("match_bottom", rect.bottom());
881      }
882      AutomationJSONReply(automation_, reply_message_)
883          .SendSuccess(return_value.get());
884      delete this;
885    } else {
886      if (find_details->active_match_ordinal() > -1) {
887        active_match_ordinal_ = find_details->active_match_ordinal();
888        AutomationMsg_Find::WriteReplyParams(reply_message_,
889            active_match_ordinal_, find_details->number_of_matches());
890        automation_->Send(reply_message_);
891        reply_message_ = NULL;
892      }
893    }
894  }
895}
896
897// static
898const int FindInPageNotificationObserver::kFindInPageRequestId = -1;
899
900DomOperationNotificationObserver::DomOperationNotificationObserver(
901    AutomationProvider* automation)
902    : automation_(automation) {
903  registrar_.Add(this, NotificationType::DOM_OPERATION_RESPONSE,
904                 NotificationService::AllSources());
905}
906
907DomOperationNotificationObserver::~DomOperationNotificationObserver() {
908}
909
910void DomOperationNotificationObserver::Observe(
911    NotificationType type, const NotificationSource& source,
912    const NotificationDetails& details) {
913  if (NotificationType::DOM_OPERATION_RESPONSE == type) {
914    Details<DomOperationNotificationDetails> dom_op_details(details);
915
916    IPC::Message* reply_message = automation_->reply_message_release();
917    if (reply_message) {
918      AutomationMsg_DomOperation::WriteReplyParams(reply_message,
919                                                   dom_op_details->json());
920      automation_->Send(reply_message);
921    }
922  }
923}
924
925DocumentPrintedNotificationObserver::DocumentPrintedNotificationObserver(
926    AutomationProvider* automation, IPC::Message* reply_message)
927    : automation_(automation),
928      success_(false),
929      reply_message_(reply_message) {
930  registrar_.Add(this, NotificationType::PRINT_JOB_EVENT,
931                 NotificationService::AllSources());
932}
933
934DocumentPrintedNotificationObserver::~DocumentPrintedNotificationObserver() {
935  DCHECK(reply_message_ != NULL);
936  AutomationMsg_PrintNow::WriteReplyParams(reply_message_, success_);
937  automation_->Send(reply_message_);
938  automation_->RemoveNavigationStatusListener(this);
939}
940
941void DocumentPrintedNotificationObserver::Observe(
942    NotificationType type, const NotificationSource& source,
943    const NotificationDetails& details) {
944  using namespace printing;
945  DCHECK(type == NotificationType::PRINT_JOB_EVENT);
946  switch (Details<JobEventDetails>(details)->type()) {
947    case JobEventDetails::JOB_DONE: {
948      // Succeeded.
949      success_ = true;
950      delete this;
951      break;
952    }
953    case JobEventDetails::USER_INIT_CANCELED:
954    case JobEventDetails::FAILED: {
955      // Failed.
956      delete this;
957      break;
958    }
959    case JobEventDetails::NEW_DOC:
960    case JobEventDetails::USER_INIT_DONE:
961    case JobEventDetails::DEFAULT_INIT_DONE:
962    case JobEventDetails::NEW_PAGE:
963    case JobEventDetails::PAGE_DONE:
964    case JobEventDetails::DOC_DONE:
965    case JobEventDetails::ALL_PAGES_REQUESTED: {
966      // Don't care.
967      break;
968    }
969    default: {
970      NOTREACHED();
971      break;
972    }
973  }
974}
975
976MetricEventDurationObserver::MetricEventDurationObserver() {
977  registrar_.Add(this, NotificationType::METRIC_EVENT_DURATION,
978                 NotificationService::AllSources());
979}
980
981MetricEventDurationObserver::~MetricEventDurationObserver() {}
982
983int MetricEventDurationObserver::GetEventDurationMs(
984    const std::string& event_name) {
985  EventDurationMap::const_iterator it = durations_.find(event_name);
986  if (it == durations_.end())
987    return -1;
988  return it->second;
989}
990
991void MetricEventDurationObserver::Observe(NotificationType type,
992    const NotificationSource& source, const NotificationDetails& details) {
993  if (type != NotificationType::METRIC_EVENT_DURATION) {
994    NOTREACHED();
995    return;
996  }
997  MetricEventDurationDetails* metric_event_duration =
998      Details<MetricEventDurationDetails>(details).ptr();
999  durations_[metric_event_duration->event_name] =
1000      metric_event_duration->duration_ms;
1001}
1002
1003PageTranslatedObserver::PageTranslatedObserver(AutomationProvider* automation,
1004                                               IPC::Message* reply_message,
1005                                               TabContents* tab_contents)
1006  : automation_(automation),
1007    reply_message_(reply_message) {
1008  registrar_.Add(this, NotificationType::PAGE_TRANSLATED,
1009                 Source<TabContents>(tab_contents));
1010}
1011
1012PageTranslatedObserver::~PageTranslatedObserver() {}
1013
1014void PageTranslatedObserver::Observe(NotificationType type,
1015                                     const NotificationSource& source,
1016                                     const NotificationDetails& details) {
1017  DCHECK(type == NotificationType::PAGE_TRANSLATED);
1018  AutomationJSONReply reply(automation_, reply_message_);
1019
1020  PageTranslatedDetails* translated_details =
1021      Details<PageTranslatedDetails>(details).ptr();
1022  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
1023  return_value->SetBoolean(
1024      "translation_success",
1025      translated_details->error_type == TranslateErrors::NONE);
1026  reply.SendSuccess(return_value.get());
1027  delete this;
1028}
1029
1030TabLanguageDeterminedObserver::TabLanguageDeterminedObserver(
1031    AutomationProvider* automation, IPC::Message* reply_message,
1032    TabContents* tab_contents, TranslateInfoBarDelegate* translate_bar)
1033  : automation_(automation),
1034    reply_message_(reply_message),
1035    tab_contents_(tab_contents),
1036    translate_bar_(translate_bar) {
1037  registrar_.Add(this, NotificationType::TAB_LANGUAGE_DETERMINED,
1038                 Source<TabContents>(tab_contents));
1039}
1040
1041void TabLanguageDeterminedObserver::Observe(
1042    NotificationType type, const NotificationSource& source,
1043    const NotificationDetails& details) {
1044  DCHECK(type == NotificationType::TAB_LANGUAGE_DETERMINED);
1045
1046  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
1047  return_value->SetBoolean("page_translated",
1048                           tab_contents_->language_state().IsPageTranslated());
1049  return_value->SetBoolean(
1050      "can_translate_page", TranslatePrefs::CanTranslate(
1051          automation_->profile()->GetPrefs(),
1052          tab_contents_->language_state().original_language(),
1053          tab_contents_->GetURL()));
1054  return_value->SetString(
1055      "original_language",
1056      tab_contents_->language_state().original_language());
1057  if (translate_bar_) {
1058    DictionaryValue* bar_info = new DictionaryValue;
1059    std::map<TranslateInfoBarDelegate::Type, std::string> type_to_string;
1060    type_to_string[TranslateInfoBarDelegate::BEFORE_TRANSLATE] =
1061        "BEFORE_TRANSLATE";
1062    type_to_string[TranslateInfoBarDelegate::TRANSLATING] =
1063        "TRANSLATING";
1064    type_to_string[TranslateInfoBarDelegate::AFTER_TRANSLATE] =
1065        "AFTER_TRANSLATE";
1066    type_to_string[TranslateInfoBarDelegate::TRANSLATION_ERROR] =
1067        "TRANSLATION_ERROR";
1068
1069    bar_info->SetBoolean("always_translate_lang_button_showing",
1070                         translate_bar_->ShouldShowAlwaysTranslateButton());
1071    bar_info->SetBoolean("never_translate_lang_button_showing",
1072                         translate_bar_->ShouldShowNeverTranslateButton());
1073    bar_info->SetString("bar_state", type_to_string[translate_bar_->type()]);
1074    bar_info->SetString("target_lang_code",
1075                        translate_bar_->GetTargetLanguageCode());
1076    bar_info->SetString("original_lang_code",
1077                        translate_bar_->GetOriginalLanguageCode());
1078    return_value->Set("translate_bar", bar_info);
1079  }
1080  AutomationJSONReply(automation_, reply_message_)
1081      .SendSuccess(return_value.get());
1082  delete this;
1083}
1084
1085InfoBarCountObserver::InfoBarCountObserver(AutomationProvider* automation,
1086                                           IPC::Message* reply_message,
1087                                           TabContents* tab_contents,
1088                                           int target_count)
1089    : automation_(automation),
1090      reply_message_(reply_message),
1091      tab_contents_(tab_contents),
1092      target_count_(target_count) {
1093  Source<TabContents> source(tab_contents);
1094  registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_ADDED, source);
1095  registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_REMOVED, source);
1096  CheckCount();
1097}
1098
1099void InfoBarCountObserver::Observe(NotificationType type,
1100                                   const NotificationSource& source,
1101                                   const NotificationDetails& details) {
1102  DCHECK(type == NotificationType::TAB_CONTENTS_INFOBAR_ADDED ||
1103         type == NotificationType::TAB_CONTENTS_INFOBAR_REMOVED);
1104  CheckCount();
1105}
1106
1107void InfoBarCountObserver::CheckCount() {
1108  if (tab_contents_->infobar_delegate_count() != target_count_)
1109    return;
1110
1111  AutomationMsg_WaitForInfoBarCount::WriteReplyParams(reply_message_, true);
1112  automation_->Send(reply_message_);
1113  delete this;
1114}
1115
1116#if defined(OS_CHROMEOS)
1117LoginManagerObserver::LoginManagerObserver(
1118    AutomationProvider* automation,
1119    IPC::Message* reply_message)
1120    : automation_(automation),
1121      reply_message_(reply_message) {
1122
1123  registrar_.Add(this, NotificationType::LOGIN_AUTHENTICATION,
1124                 NotificationService::AllSources());
1125}
1126
1127void LoginManagerObserver::Observe(NotificationType type,
1128                                   const NotificationSource& source,
1129                                   const NotificationDetails& details) {
1130  DCHECK(type == NotificationType::LOGIN_AUTHENTICATION);
1131  Details<AuthenticationNotificationDetails> auth_details(details);
1132  AutomationMsg_LoginWithUserAndPass::WriteReplyParams(reply_message_,
1133      auth_details->success());
1134  automation_->Send(reply_message_);
1135  delete this;
1136}
1137#endif
1138
1139AutomationProviderBookmarkModelObserver::
1140AutomationProviderBookmarkModelObserver(
1141    AutomationProvider* provider,
1142    IPC::Message* reply_message,
1143    BookmarkModel* model) {
1144  automation_provider_ = provider;
1145  reply_message_ = reply_message;
1146  model_ = model;
1147  model_->AddObserver(this);
1148}
1149
1150AutomationProviderBookmarkModelObserver::
1151~AutomationProviderBookmarkModelObserver() {
1152  model_->RemoveObserver(this);
1153}
1154
1155void AutomationProviderBookmarkModelObserver::ReplyAndDelete(bool success) {
1156  AutomationMsg_WaitForBookmarkModelToLoad::WriteReplyParams(
1157      reply_message_, success);
1158  automation_provider_->Send(reply_message_);
1159  delete this;
1160}
1161
1162void AutomationProviderDownloadItemObserver::OnDownloadFileCompleted(
1163    DownloadItem* download) {
1164  download->RemoveObserver(this);
1165  if (--downloads_ == 0) {
1166    AutomationJSONReply(provider_, reply_message_).SendSuccess(NULL);
1167    delete this;
1168  }
1169}
1170
1171void AutomationProviderDownloadUpdatedObserver::OnDownloadUpdated(
1172    DownloadItem* download) {
1173  // If this observer is watching for open, only send the reply if the download
1174  // has been auto-opened.
1175  if (wait_for_open_ && !download->auto_opened())
1176    return;
1177
1178  download->RemoveObserver(this);
1179  scoped_ptr<DictionaryValue> return_value(
1180      provider_->GetDictionaryFromDownloadItem(download));
1181  AutomationJSONReply(provider_, reply_message_).SendSuccess(
1182      return_value.get());
1183  delete this;
1184}
1185
1186void AutomationProviderDownloadUpdatedObserver::OnDownloadOpened(
1187    DownloadItem* download) {
1188  download->RemoveObserver(this);
1189  scoped_ptr<DictionaryValue> return_value(
1190      provider_->GetDictionaryFromDownloadItem(download));
1191  AutomationJSONReply(provider_, reply_message_).SendSuccess(
1192      return_value.get());
1193  delete this;
1194}
1195
1196void AutomationProviderDownloadModelChangedObserver::ModelChanged() {
1197  AutomationJSONReply(provider_, reply_message_).SendSuccess(NULL);
1198  download_manager_->RemoveObserver(this);
1199  delete this;
1200}
1201
1202void AutomationProviderSearchEngineObserver::OnTemplateURLModelChanged() {
1203  TemplateURLModel* url_model = provider_->profile()->GetTemplateURLModel();
1204  url_model->RemoveObserver(this);
1205  AutomationJSONReply(provider_, reply_message_).SendSuccess(NULL);
1206  delete this;
1207}
1208
1209void AutomationProviderHistoryObserver::HistoryQueryComplete(
1210    HistoryService::Handle request_handle,
1211    history::QueryResults* results) {
1212  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
1213
1214  ListValue* history_list = new ListValue;
1215  for (size_t i = 0; i < results->size(); ++i) {
1216    DictionaryValue* page_value = new DictionaryValue;
1217    history::URLResult const &page = (*results)[i];
1218    page_value->SetString("title", page.title());
1219    page_value->SetString("url", page.url().spec());
1220    page_value->SetReal("time",
1221                        static_cast<double>(page.visit_time().ToDoubleT()));
1222    page_value->SetString("snippet", page.snippet().text());
1223    page_value->SetBoolean(
1224        "starred",
1225        provider_->profile()->GetBookmarkModel()->IsBookmarked(page.url()));
1226    history_list->Append(page_value);
1227  }
1228
1229  return_value->Set("history", history_list);
1230  // Return history info.
1231  AutomationJSONReply reply(provider_, reply_message_);
1232  reply.SendSuccess(return_value.get());
1233  delete this;
1234}
1235
1236void AutomationProviderImportSettingsObserver::ImportEnded() {
1237  // Send back an empty success message.
1238  AutomationJSONReply(provider_, reply_message_).SendSuccess(NULL);
1239  delete this;
1240}
1241
1242void AutomationProviderGetPasswordsObserver::OnPasswordStoreRequestDone(
1243    int handle, const std::vector<webkit_glue::PasswordForm*>& result) {
1244  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
1245
1246  ListValue* passwords = new ListValue;
1247  for (std::vector<webkit_glue::PasswordForm*>::const_iterator it =
1248          result.begin(); it != result.end(); ++it) {
1249    DictionaryValue* password_val = new DictionaryValue;
1250    webkit_glue::PasswordForm* password_form = *it;
1251    password_val->SetString("username_value", password_form->username_value);
1252    password_val->SetString("password_value", password_form->password_value);
1253    password_val->SetString("signon_realm", password_form->signon_realm);
1254    password_val->SetReal(
1255        "time", static_cast<double>(password_form->date_created.ToDoubleT()));
1256    password_val->SetString("origin_url", password_form->origin.spec());
1257    password_val->SetString("username_element",
1258                            password_form->username_element);
1259    password_val->SetString("password_element",
1260                            password_form->password_element);
1261    password_val->SetString("submit_element",
1262                                     password_form->submit_element);
1263    password_val->SetString("action_target", password_form->action.spec());
1264    password_val->SetBoolean("blacklist", password_form->blacklisted_by_user);
1265    passwords->Append(password_val);
1266  }
1267
1268  return_value->Set("passwords", passwords);
1269  AutomationJSONReply(provider_, reply_message_).SendSuccess(
1270      return_value.get());
1271  delete this;
1272}
1273
1274void AutomationProviderBrowsingDataObserver::OnBrowsingDataRemoverDone() {
1275  // Send back an empty success message
1276  AutomationJSONReply(provider_, reply_message_).SendSuccess(NULL);
1277  delete this;
1278}
1279
1280OmniboxAcceptNotificationObserver::OmniboxAcceptNotificationObserver(
1281    NavigationController* controller,
1282    AutomationProvider* automation,
1283    IPC::Message* reply_message)
1284  : automation_(automation),
1285    reply_message_(reply_message),
1286    controller_(controller) {
1287  Source<NavigationController> source(controller_);
1288  registrar_.Add(this, NotificationType::LOAD_STOP, source);
1289  // Pages requiring auth don't send LOAD_STOP.
1290  registrar_.Add(this, NotificationType::AUTH_NEEDED, source);
1291}
1292
1293OmniboxAcceptNotificationObserver::~OmniboxAcceptNotificationObserver() {
1294  automation_->RemoveNavigationStatusListener(this);
1295}
1296
1297void OmniboxAcceptNotificationObserver::Observe(
1298    NotificationType type,
1299    const NotificationSource& source,
1300    const NotificationDetails& details) {
1301  if (type == NotificationType::LOAD_STOP ||
1302      type == NotificationType::AUTH_NEEDED) {
1303    AutomationJSONReply(automation_, reply_message_).SendSuccess(NULL);
1304    delete this;
1305  } else {
1306    NOTREACHED();
1307  }
1308}
1309
1310SavePackageNotificationObserver::SavePackageNotificationObserver(
1311    SavePackage* save_package,
1312    AutomationProvider* automation,
1313    IPC::Message* reply_message) : automation_(automation),
1314                                   reply_message_(reply_message) {
1315  Source<SavePackage> source(save_package);
1316  registrar_.Add(this, NotificationType::SAVE_PACKAGE_SUCCESSFULLY_FINISHED,
1317                 source);
1318}
1319
1320void SavePackageNotificationObserver::Observe(
1321    NotificationType type,
1322    const NotificationSource& source,
1323    const NotificationDetails& details) {
1324  if (type == NotificationType::SAVE_PACKAGE_SUCCESSFULLY_FINISHED) {
1325    AutomationJSONReply(automation_, reply_message_).SendSuccess(NULL);
1326    delete this;
1327  } else {
1328    NOTREACHED();
1329  }
1330}
1331
1332AutocompleteEditFocusedObserver::AutocompleteEditFocusedObserver(
1333    AutomationProvider* automation,
1334    AutocompleteEditModel* autocomplete_edit,
1335    IPC::Message* reply_message)
1336    : automation_(automation),
1337      reply_message_(reply_message),
1338      autocomplete_edit_model_(autocomplete_edit) {
1339  Source<AutocompleteEditModel> source(autocomplete_edit);
1340  registrar_.Add(this, NotificationType::AUTOCOMPLETE_EDIT_FOCUSED, source);
1341}
1342
1343void AutocompleteEditFocusedObserver::Observe(
1344    NotificationType type,
1345    const NotificationSource& source,
1346    const NotificationDetails& details) {
1347  DCHECK(type == NotificationType::AUTOCOMPLETE_EDIT_FOCUSED);
1348  AutomationMsg_WaitForAutocompleteEditFocus::WriteReplyParams(
1349      reply_message_, true);
1350  automation_->Send(reply_message_);
1351  delete this;
1352}
1353
1354OnNotificationBalloonCountObserver::OnNotificationBalloonCountObserver(
1355    AutomationProvider* provider,
1356    IPC::Message* reply_message,
1357    BalloonCollection* collection,
1358    int count)
1359    : reply_(provider, reply_message),
1360      collection_(collection),
1361      count_(count) {
1362  collection->set_on_collection_changed_callback(NewCallback(
1363      this, &OnNotificationBalloonCountObserver::OnBalloonCollectionChanged));
1364}
1365
1366void OnNotificationBalloonCountObserver::OnBalloonCollectionChanged() {
1367  if (static_cast<int>(collection_->GetActiveBalloons().size()) == count_) {
1368    collection_->set_on_collection_changed_callback(NULL);
1369    reply_.SendSuccess(NULL);
1370    delete this;
1371  }
1372}
1373