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