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