- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / automation / automation_provider_observers.cc
1 // Copyright (c) 2012 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/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/callback.h"
15 #include "base/file_util.h"
16 #include "base/json/json_writer.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "base/stl_util.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/threading/thread_restrictions.h"
24 #include "base/values.h"
25 #include "chrome/app/chrome_command_ids.h"
26 #include "chrome/browser/automation/automation_provider.h"
27 #include "chrome/browser/automation/automation_provider_json.h"
28 #include "chrome/browser/bookmarks/bookmark_model.h"
29 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
30 #include "chrome/browser/browser_process.h"
31 #include "chrome/browser/chrome_notification_types.h"
32 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
33 #include "chrome/browser/extensions/crx_installer.h"
34 #include "chrome/browser/extensions/extension_host.h"
35 #include "chrome/browser/extensions/extension_process_manager.h"
36 #include "chrome/browser/extensions/extension_service.h"
37 #include "chrome/browser/extensions/extension_system.h"
38 #include "chrome/browser/extensions/extension_tab_util.h"
39 #include "chrome/browser/history/history_types.h"
40 #include "chrome/browser/history/top_sites.h"
41 #include "chrome/browser/infobars/confirm_infobar_delegate.h"
42 #include "chrome/browser/infobars/infobar_service.h"
43 #include "chrome/browser/metrics/metric_event_duration_details.h"
44 #include "chrome/browser/notifications/balloon.h"
45 #include "chrome/browser/notifications/balloon_collection.h"
46 #include "chrome/browser/notifications/balloon_host.h"
47 #include "chrome/browser/notifications/balloon_notification_ui_manager.h"
48 #include "chrome/browser/notifications/notification.h"
49 #include "chrome/browser/password_manager/password_store_change.h"
50 #include "chrome/browser/profiles/profile.h"
51 #include "chrome/browser/renderer_host/chrome_render_message_filter.h"
52 #include "chrome/browser/search_engines/template_url_service.h"
53 #include "chrome/browser/search_engines/template_url_service_factory.h"
54 #include "chrome/browser/sessions/session_tab_helper.h"
55 #include "chrome/browser/sessions/tab_restore_service.h"
56 #include "chrome/browser/sessions/tab_restore_service_factory.h"
57 #include "chrome/browser/ui/browser.h"
58 #include "chrome/browser/ui/browser_iterator.h"
59 #include "chrome/browser/ui/browser_list.h"
60 #include "chrome/browser/ui/browser_window.h"
61 #include "chrome/browser/ui/find_bar/find_notification_details.h"
62 #include "chrome/browser/ui/host_desktop.h"
63 #include "chrome/browser/ui/login/login_prompt.h"
64 #include "chrome/browser/ui/tabs/tab_strip_model.h"
65 #include "chrome/browser/ui/webui/ntp/app_launcher_handler.h"
66 #include "chrome/browser/ui/webui/ntp/most_visited_handler.h"
67 #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
68 #include "chrome/browser/ui/webui/ntp/recently_closed_tabs_handler.h"
69 #include "chrome/common/automation_constants.h"
70 #include "chrome/common/automation_messages.h"
71 #include "chrome/common/content_settings_types.h"
72 #include "chrome/common/extensions/extension.h"
73 #include "content/public/browser/dom_operation_notification_details.h"
74 #include "content/public/browser/navigation_controller.h"
75 #include "content/public/browser/notification_service.h"
76 #include "content/public/browser/render_process_host.h"
77 #include "content/public/browser/render_view_host.h"
78 #include "content/public/browser/web_contents.h"
79 #include "content/public/common/process_type.h"
80 #include "extensions/common/manifest.h"
81 #include "extensions/common/view_type.h"
82 #include "ui/gfx/codec/png_codec.h"
83 #include "ui/gfx/rect.h"
84 #include "url/gurl.h"
85
86 using content::BrowserThread;
87 using content::DomOperationNotificationDetails;
88 using content::DownloadItem;
89 using content::DownloadManager;
90 using content::NavigationController;
91 using content::RenderViewHost;
92 using content::WebContents;
93
94 // Holds onto start and stop timestamps for a particular tab
95 class InitialLoadObserver::TabTime {
96  public:
97   explicit TabTime(base::TimeTicks started)
98       : load_start_time_(started) {
99   }
100   void set_stop_time(base::TimeTicks stopped) {
101     load_stop_time_ = stopped;
102   }
103   base::TimeTicks stop_time() const {
104     return load_stop_time_;
105   }
106   base::TimeTicks start_time() const {
107     return load_start_time_;
108   }
109  private:
110   base::TimeTicks load_start_time_;
111   base::TimeTicks load_stop_time_;
112 };
113
114 InitialLoadObserver::InitialLoadObserver(size_t tab_count,
115                                          AutomationProvider* automation)
116     : automation_(automation->AsWeakPtr()),
117       crashed_tab_count_(0),
118       outstanding_tab_count_(tab_count),
119       init_time_(base::TimeTicks::Now()) {
120   if (outstanding_tab_count_ > 0) {
121     registrar_.Add(this, content::NOTIFICATION_LOAD_START,
122                    content::NotificationService::AllSources());
123     registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
124                    content::NotificationService::AllSources());
125     registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
126                    content::NotificationService::AllSources());
127   }
128 }
129
130 InitialLoadObserver::~InitialLoadObserver() {
131 }
132
133 void InitialLoadObserver::Observe(int type,
134                                   const content::NotificationSource& source,
135                                   const content::NotificationDetails& details) {
136   if (type == content::NOTIFICATION_LOAD_START) {
137     if (outstanding_tab_count_ > loading_tabs_.size())
138       loading_tabs_.insert(TabTimeMap::value_type(
139           source.map_key(),
140           TabTime(base::TimeTicks::Now())));
141   } else if (type == content::NOTIFICATION_LOAD_STOP) {
142     if (outstanding_tab_count_ > finished_tabs_.size()) {
143       TabTimeMap::iterator iter = loading_tabs_.find(source.map_key());
144       if (iter != loading_tabs_.end()) {
145         finished_tabs_.insert(source.map_key());
146         iter->second.set_stop_time(base::TimeTicks::Now());
147       }
148     }
149   } else if (type == content::NOTIFICATION_RENDERER_PROCESS_CLOSED) {
150     base::TerminationStatus status =
151         content::Details<content::RenderProcessHost::RendererClosedDetails>(
152             details)->status;
153     switch (status) {
154       case base::TERMINATION_STATUS_NORMAL_TERMINATION:
155         break;
156
157       case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
158       case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
159       case base::TERMINATION_STATUS_PROCESS_CRASHED:
160         crashed_tab_count_++;
161         break;
162
163       case base::TERMINATION_STATUS_STILL_RUNNING:
164         LOG(ERROR) << "Got RENDERER_PROCESS_CLOSED notification, "
165                    << "but the process is still running. We may miss further "
166                    << "crash notification, resulting in hangs.";
167         break;
168
169       default:
170         LOG(ERROR) << "Unhandled termination status " << status;
171         NOTREACHED();
172         break;
173     }
174   } else {
175     NOTREACHED();
176   }
177
178   if (finished_tabs_.size() + crashed_tab_count_ >= outstanding_tab_count_)
179     ConditionMet();
180 }
181
182 DictionaryValue* InitialLoadObserver::GetTimingInformation() const {
183   ListValue* items = new ListValue;
184   for (TabTimeMap::const_iterator it = loading_tabs_.begin();
185        it != loading_tabs_.end();
186        ++it) {
187     DictionaryValue* item = new DictionaryValue;
188     base::TimeDelta delta_start = it->second.start_time() - init_time_;
189
190     item->SetDouble("load_start_ms", delta_start.InMillisecondsF());
191     if (it->second.stop_time().is_null()) {
192       item->Set("load_stop_ms", Value::CreateNullValue());
193     } else {
194       base::TimeDelta delta_stop = it->second.stop_time() - init_time_;
195       item->SetDouble("load_stop_ms", delta_stop.InMillisecondsF());
196     }
197     items->Append(item);
198   }
199   DictionaryValue* return_value = new DictionaryValue;
200   return_value->Set("tabs", items);
201   return return_value;
202 }
203
204 void InitialLoadObserver::ConditionMet() {
205   registrar_.RemoveAll();
206   if (automation_.get())
207     automation_->OnInitialTabLoadsComplete();
208 }
209
210 NewTabUILoadObserver::NewTabUILoadObserver(AutomationProvider* automation,
211                                            Profile* profile)
212     : automation_(automation->AsWeakPtr()) {
213   registrar_.Add(this, chrome::NOTIFICATION_INITIAL_NEW_TAB_UI_LOAD,
214                  content::Source<Profile>(profile));
215 }
216
217 NewTabUILoadObserver::~NewTabUILoadObserver() {
218 }
219
220 void NewTabUILoadObserver::Observe(int type,
221                                    const content::NotificationSource& source,
222                                    const content::NotificationDetails& details) {
223   if (type == chrome::NOTIFICATION_INITIAL_NEW_TAB_UI_LOAD) {
224     content::Details<int> load_time(details);
225     if (automation_.get()) {
226       automation_->Send(
227           new AutomationMsg_InitialNewTabUILoadComplete(*load_time.ptr()));
228     }
229   } else {
230     NOTREACHED();
231   }
232 }
233
234 NavigationControllerRestoredObserver::NavigationControllerRestoredObserver(
235     AutomationProvider* automation,
236     NavigationController* controller,
237     IPC::Message* reply_message)
238     : automation_(automation->AsWeakPtr()),
239       controller_(controller),
240       reply_message_(reply_message) {
241   if (FinishedRestoring()) {
242     SendDone();
243   } else {
244     registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
245                    content::NotificationService::AllSources());
246   }
247 }
248
249 NavigationControllerRestoredObserver::~NavigationControllerRestoredObserver() {
250 }
251
252 void NavigationControllerRestoredObserver::Observe(
253     int type, const content::NotificationSource& source,
254     const content::NotificationDetails& details) {
255   if (FinishedRestoring()) {
256     registrar_.RemoveAll();
257     SendDone();
258   }
259 }
260
261 bool NavigationControllerRestoredObserver::FinishedRestoring() {
262   return (!controller_->NeedsReload() && !controller_->GetPendingEntry() &&
263           !controller_->GetWebContents()->IsLoading());
264 }
265
266 void NavigationControllerRestoredObserver::SendDone() {
267   if (automation_.get()) {
268     AutomationJSONReply(automation_.get(), reply_message_.release())
269         .SendSuccess(NULL);
270   }
271   delete this;
272 }
273
274 NavigationNotificationObserver::NavigationNotificationObserver(
275     NavigationController* controller,
276     AutomationProvider* automation,
277     IPC::Message* reply_message,
278     int number_of_navigations,
279     bool include_current_navigation,
280     bool use_json_interface)
281     : automation_(automation->AsWeakPtr()),
282       reply_message_(reply_message),
283       controller_(controller),
284       navigations_remaining_(number_of_navigations),
285       navigation_started_(false),
286       use_json_interface_(use_json_interface) {
287   if (number_of_navigations == 0) {
288     ConditionMet(AUTOMATION_MSG_NAVIGATION_SUCCESS);
289     return;
290   }
291   DCHECK_LT(0, navigations_remaining_);
292   content::Source<NavigationController> source(controller_);
293   registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, source);
294   registrar_.Add(this, content::NOTIFICATION_LOAD_START, source);
295   registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, source);
296   registrar_.Add(this, chrome::NOTIFICATION_AUTH_NEEDED, source);
297   registrar_.Add(this, chrome::NOTIFICATION_AUTH_SUPPLIED, source);
298   registrar_.Add(this, chrome::NOTIFICATION_AUTH_CANCELLED, source);
299   registrar_.Add(this, chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN,
300                  content::NotificationService::AllSources());
301
302   if (include_current_navigation && controller->GetWebContents()->IsLoading())
303     navigation_started_ = true;
304 }
305
306 NavigationNotificationObserver::~NavigationNotificationObserver() {
307 }
308
309 void NavigationNotificationObserver::Observe(
310     int type, const content::NotificationSource& source,
311     const content::NotificationDetails& details) {
312   if (!automation_.get()) {
313     delete this;
314     return;
315   }
316
317   // We listen for 2 events to determine when the navigation started because:
318   // - when this is used by the WaitForNavigation method, we might be invoked
319   // afer the load has started (but not after the entry was committed, as
320   // WaitForNavigation compares times of the last navigation).
321   // - when this is used with a page requiring authentication, we will not get
322   // a chrome::NAV_ENTRY_COMMITTED until after we authenticate, so
323   // we need the chrome::LOAD_START.
324   if (type == content::NOTIFICATION_NAV_ENTRY_COMMITTED ||
325       type == content::NOTIFICATION_LOAD_START) {
326     navigation_started_ = true;
327   } else if (type == content::NOTIFICATION_LOAD_STOP) {
328     if (navigation_started_) {
329       navigation_started_ = false;
330       if (--navigations_remaining_ == 0)
331         ConditionMet(AUTOMATION_MSG_NAVIGATION_SUCCESS);
332     }
333   } else if (type == chrome::NOTIFICATION_AUTH_SUPPLIED ||
334              type == chrome::NOTIFICATION_AUTH_CANCELLED) {
335     // Treat this as if navigation started again, since load start/stop don't
336     // occur while authentication is ongoing.
337     navigation_started_ = true;
338   } else if (type == chrome::NOTIFICATION_AUTH_NEEDED) {
339     // Respond that authentication is needed.
340     navigation_started_ = false;
341     ConditionMet(AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED);
342   } else if (type == chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN) {
343     ConditionMet(AUTOMATION_MSG_NAVIGATION_BLOCKED_BY_MODAL_DIALOG);
344   } else {
345     NOTREACHED();
346   }
347 }
348
349 void NavigationNotificationObserver::ConditionMet(
350     AutomationMsg_NavigationResponseValues navigation_result) {
351   if (automation_.get()) {
352     if (use_json_interface_) {
353       if (navigation_result == AUTOMATION_MSG_NAVIGATION_SUCCESS) {
354         DictionaryValue dict;
355         dict.SetInteger("result", navigation_result);
356         AutomationJSONReply(automation_.get(), reply_message_.release())
357             .SendSuccess(&dict);
358       } else {
359         AutomationJSONReply(automation_.get(), reply_message_.release())
360             .SendError(base::StringPrintf(
361                  "Navigation failed with error code=%d.", navigation_result));
362       }
363     } else {
364       IPC::ParamTraits<int>::Write(
365           reply_message_.get(), navigation_result);
366       automation_->Send(reply_message_.release());
367     }
368   }
369
370   delete this;
371 }
372
373 TabStripNotificationObserver::TabStripNotificationObserver(
374     int notification, AutomationProvider* automation)
375     : automation_(automation->AsWeakPtr()),
376       notification_(notification) {
377   registrar_.Add(this, notification_,
378                  content::NotificationService::AllSources());
379 }
380
381 TabStripNotificationObserver::~TabStripNotificationObserver() {
382 }
383
384 void TabStripNotificationObserver::Observe(
385     int type,
386     const content::NotificationSource& source,
387     const content::NotificationDetails& details) {
388   DCHECK_EQ(notification_, type);
389   if (type == chrome::NOTIFICATION_TAB_PARENTED) {
390     ObserveTab(&content::Source<content::WebContents>(source)->GetController());
391   } else if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) {
392     ObserveTab(&content::Source<content::WebContents>(source)->GetController());
393   } else {
394     ObserveTab(content::Source<NavigationController>(source).ptr());
395   }
396   delete this;
397 }
398
399 TabAppendedNotificationObserver::TabAppendedNotificationObserver(
400     Browser* parent,
401     AutomationProvider* automation,
402     IPC::Message* reply_message,
403     bool use_json_interface)
404     : TabStripNotificationObserver(chrome::NOTIFICATION_TAB_PARENTED,
405                                    automation),
406       parent_(parent),
407       reply_message_(reply_message),
408       use_json_interface_(use_json_interface) {
409 }
410
411 TabAppendedNotificationObserver::~TabAppendedNotificationObserver() {}
412
413 void TabAppendedNotificationObserver::ObserveTab(
414     NavigationController* controller) {
415   if (!automation_.get() || !reply_message_.get())
416     return;
417
418   if (automation_->GetIndexForNavigationController(controller, parent_) ==
419       TabStripModel::kNoTab) {
420     // This tab notification doesn't belong to the parent_.
421     return;
422   }
423
424   new NavigationNotificationObserver(controller,
425                                      automation_.get(),
426                                      reply_message_.release(),
427                                      1,
428                                      false,
429                                      use_json_interface_);
430 }
431
432 IPC::Message* TabAppendedNotificationObserver::ReleaseReply() {
433   return reply_message_.release();
434 }
435
436 TabClosedNotificationObserver::TabClosedNotificationObserver(
437     AutomationProvider* automation,
438     bool wait_until_closed,
439     IPC::Message* reply_message,
440     bool use_json_interface)
441     : TabStripNotificationObserver((wait_until_closed ?
442           static_cast<int>(content::NOTIFICATION_WEB_CONTENTS_DESTROYED) :
443           static_cast<int>(chrome::NOTIFICATION_TAB_CLOSING)), automation),
444       reply_message_(reply_message),
445       use_json_interface_(use_json_interface),
446       for_browser_command_(false) {
447 }
448
449 TabClosedNotificationObserver::~TabClosedNotificationObserver() {}
450
451 void TabClosedNotificationObserver::ObserveTab(
452     NavigationController* controller) {
453   if (!automation_.get())
454     return;
455
456   if (use_json_interface_) {
457     AutomationJSONReply(automation_.get(), reply_message_.release())
458         .SendSuccess(NULL);
459   } else {
460     if (for_browser_command_) {
461       AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_.get(),
462                                                            true);
463     } else {
464       AutomationMsg_CloseTab::WriteReplyParams(reply_message_.get(), true);
465     }
466     automation_->Send(reply_message_.release());
467   }
468 }
469
470 void TabClosedNotificationObserver::set_for_browser_command(
471     bool for_browser_command) {
472   for_browser_command_ = for_browser_command;
473 }
474
475 TabCountChangeObserver::TabCountChangeObserver(AutomationProvider* automation,
476                                                Browser* browser,
477                                                IPC::Message* reply_message,
478                                                int target_tab_count)
479     : automation_(automation->AsWeakPtr()),
480       reply_message_(reply_message),
481       tab_strip_model_(browser->tab_strip_model()),
482       target_tab_count_(target_tab_count) {
483   tab_strip_model_->AddObserver(this);
484   CheckTabCount();
485 }
486
487 TabCountChangeObserver::~TabCountChangeObserver() {
488   tab_strip_model_->RemoveObserver(this);
489 }
490
491 void TabCountChangeObserver::TabInsertedAt(WebContents* contents,
492                                            int index,
493                                            bool foreground) {
494   CheckTabCount();
495 }
496
497 void TabCountChangeObserver::TabDetachedAt(WebContents* contents,
498                                            int index) {
499   CheckTabCount();
500 }
501
502 void TabCountChangeObserver::TabStripModelDeleted() {
503   if (automation_.get()) {
504     AutomationMsg_WaitForTabCountToBecome::WriteReplyParams(
505         reply_message_.get(), false);
506     automation_->Send(reply_message_.release());
507   }
508
509   delete this;
510 }
511
512 void TabCountChangeObserver::CheckTabCount() {
513   if (tab_strip_model_->count() != target_tab_count_)
514     return;
515
516   if (automation_.get()) {
517     AutomationMsg_WaitForTabCountToBecome::WriteReplyParams(
518         reply_message_.get(), true);
519     automation_->Send(reply_message_.release());
520   }
521
522   delete this;
523 }
524
525 bool DidExtensionViewsStopLoading(ExtensionProcessManager* manager) {
526   ExtensionProcessManager::ViewSet all_views = manager->GetAllViews();
527   for (ExtensionProcessManager::ViewSet::const_iterator iter =
528            all_views.begin();
529        iter != all_views.end(); ++iter) {
530     if ((*iter)->IsLoading())
531       return false;
532   }
533   return true;
534 }
535
536 ExtensionUninstallObserver::ExtensionUninstallObserver(
537     AutomationProvider* automation,
538     IPC::Message* reply_message,
539     const std::string& id)
540     : automation_(automation->AsWeakPtr()),
541       reply_message_(reply_message),
542       id_(id) {
543   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
544                  content::NotificationService::AllSources());
545   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALL_NOT_ALLOWED,
546                  content::NotificationService::AllSources());
547 }
548
549 ExtensionUninstallObserver::~ExtensionUninstallObserver() {
550 }
551
552 void ExtensionUninstallObserver::Observe(
553     int type,
554     const content::NotificationSource& source,
555     const content::NotificationDetails& details) {
556   if (!automation_.get()) {
557     delete this;
558     return;
559   }
560
561   switch (type) {
562     case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: {
563       if (id_ == content::Details<extensions::Extension>(details).ptr()->id()) {
564         scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
565         return_value->SetBoolean("success", true);
566         AutomationJSONReply(automation_.get(), reply_message_.release())
567             .SendSuccess(return_value.get());
568         delete this;
569         return;
570       }
571       break;
572     }
573
574     case chrome::NOTIFICATION_EXTENSION_UNINSTALL_NOT_ALLOWED: {
575       const extensions::Extension* extension =
576           content::Details<extensions::Extension>(details).ptr();
577       if (id_ == extension->id()) {
578         scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
579         return_value->SetBoolean("success", false);
580         AutomationJSONReply(automation_.get(), reply_message_.release())
581             .SendSuccess(return_value.get());
582         delete this;
583         return;
584       }
585       break;
586     }
587
588     default:
589       NOTREACHED();
590   }
591 }
592
593 ExtensionReadyNotificationObserver::ExtensionReadyNotificationObserver(
594     ExtensionProcessManager* manager, ExtensionService* service,
595     AutomationProvider* automation, IPC::Message* reply_message)
596     : manager_(manager),
597       service_(service),
598       automation_(automation->AsWeakPtr()),
599       reply_message_(reply_message),
600       extension_(NULL) {
601   Init();
602 }
603
604 ExtensionReadyNotificationObserver::~ExtensionReadyNotificationObserver() {
605 }
606
607 void ExtensionReadyNotificationObserver::Init() {
608   registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
609                  content::NotificationService::AllSources());
610   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
611                  content::NotificationService::AllSources());
612   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOAD_ERROR,
613                  content::NotificationService::AllSources());
614   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR,
615                  content::NotificationService::AllSources());
616   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED,
617                  content::NotificationService::AllSources());
618 }
619
620 void ExtensionReadyNotificationObserver::Observe(
621     int type, const content::NotificationSource& source,
622     const content::NotificationDetails& details) {
623   if (!automation_.get()) {
624     delete this;
625     return;
626   }
627
628   switch (type) {
629     case content::NOTIFICATION_LOAD_STOP:
630       // Only continue on with this method if our extension has been loaded
631       // and all the extension views have stopped loading.
632       if (!extension_ || !DidExtensionViewsStopLoading(manager_))
633         return;
634       break;
635     case chrome::NOTIFICATION_EXTENSION_LOADED: {
636       const extensions::Extension* loaded_extension =
637           content::Details<const extensions::Extension>(details).ptr();
638       // Only track an internal or unpacked extension load.
639       extensions::Manifest::Location location = loaded_extension->location();
640       if (location != extensions::Manifest::INTERNAL &&
641           !extensions::Manifest::IsUnpackedLocation(location))
642         return;
643       extension_ = loaded_extension;
644       if (!DidExtensionViewsStopLoading(manager_))
645         return;
646       // For some reason, the background extension view is not yet
647       // created at this point so just checking whether all extension views
648       // are loaded is not sufficient. If background page is not ready,
649       // we wait for NOTIFICATION_LOAD_STOP.
650       if (!service_->IsBackgroundPageReady(extension_))
651         return;
652       break;
653     }
654     case chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR:
655     case chrome::NOTIFICATION_EXTENSION_LOAD_ERROR:
656     case chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED:
657       break;
658     default:
659       NOTREACHED();
660       break;
661   }
662
663   AutomationJSONReply reply(automation_.get(), reply_message_.release());
664   if (extension_) {
665     DictionaryValue dict;
666     dict.SetString("id", extension_->id());
667     reply.SendSuccess(&dict);
668   } else {
669     reply.SendError("Extension could not be installed");
670   }
671   delete this;
672 }
673
674 ExtensionUnloadNotificationObserver::ExtensionUnloadNotificationObserver()
675     : did_receive_unload_notification_(false) {
676   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
677                  content::NotificationService::AllSources());
678 }
679
680 ExtensionUnloadNotificationObserver::~ExtensionUnloadNotificationObserver() {
681 }
682
683 void ExtensionUnloadNotificationObserver::Observe(
684     int type, const content::NotificationSource& source,
685     const content::NotificationDetails& details) {
686   if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
687     did_receive_unload_notification_ = true;
688   } else {
689     NOTREACHED();
690   }
691 }
692
693 ExtensionsUpdatedObserver::ExtensionsUpdatedObserver(
694     ExtensionProcessManager* manager, AutomationProvider* automation,
695     IPC::Message* reply_message)
696     : manager_(manager), automation_(automation->AsWeakPtr()),
697       reply_message_(reply_message), updater_finished_(false) {
698   registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
699                  content::NotificationService::AllSources());
700 }
701
702 ExtensionsUpdatedObserver::~ExtensionsUpdatedObserver() {
703 }
704
705 void ExtensionsUpdatedObserver::Observe(
706     int type, const content::NotificationSource& source,
707     const content::NotificationDetails& details) {
708   if (!automation_.get()) {
709     delete this;
710     return;
711   }
712
713   DCHECK(type == content::NOTIFICATION_LOAD_STOP);
714   MaybeReply();
715 }
716
717 void ExtensionsUpdatedObserver::UpdateCheckFinished() {
718   if (!automation_.get()) {
719     delete this;
720     return;
721   }
722
723   // Extension updater has completed updating all extensions.
724   updater_finished_ = true;
725   MaybeReply();
726 }
727
728 void ExtensionsUpdatedObserver::MaybeReply() {
729   // Send the reply if (1) the extension updater has finished updating all
730   // extensions; and (2) all extension views have stopped loading.
731   if (updater_finished_ && DidExtensionViewsStopLoading(manager_)) {
732     AutomationJSONReply reply(automation_.get(), reply_message_.release());
733     reply.SendSuccess(NULL);
734     delete this;
735   }
736 }
737
738 BrowserOpenedNotificationObserver::BrowserOpenedNotificationObserver(
739     AutomationProvider* automation,
740     IPC::Message* reply_message,
741     bool use_json_interface)
742     : automation_(automation->AsWeakPtr()),
743       reply_message_(reply_message),
744       new_window_id_(extension_misc::kUnknownWindowId),
745       use_json_interface_(use_json_interface),
746       for_browser_command_(false) {
747   registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED,
748                  content::NotificationService::AllBrowserContextsAndSources());
749   registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
750                  content::NotificationService::AllBrowserContextsAndSources());
751 }
752
753 BrowserOpenedNotificationObserver::~BrowserOpenedNotificationObserver() {
754 }
755
756 void BrowserOpenedNotificationObserver::Observe(
757     int type,
758     const content::NotificationSource& source,
759     const content::NotificationDetails& details) {
760   if (!automation_.get()) {
761     delete this;
762     return;
763   }
764
765   if (type == chrome::NOTIFICATION_BROWSER_OPENED) {
766     // Store the new browser ID and continue waiting for a new tab within it
767     // to stop loading.
768     new_window_id_ = ExtensionTabUtil::GetWindowId(
769         content::Source<Browser>(source).ptr());
770   } else {
771     DCHECK_EQ(content::NOTIFICATION_LOAD_STOP, type);
772     // Only send the result if the loaded tab is in the new window.
773     NavigationController* controller =
774         content::Source<NavigationController>(source).ptr();
775     SessionTabHelper* session_tab_helper =
776         SessionTabHelper::FromWebContents(controller->GetWebContents());
777     int window_id = session_tab_helper ? session_tab_helper->window_id().id()
778                                        : -1;
779     if (window_id == new_window_id_) {
780       if (use_json_interface_) {
781         AutomationJSONReply(automation_.get(), reply_message_.release())
782             .SendSuccess(NULL);
783       } else {
784         if (for_browser_command_) {
785           AutomationMsg_WindowExecuteCommand::WriteReplyParams(
786               reply_message_.get(), true);
787         }
788         automation_->Send(reply_message_.release());
789       }
790       delete this;
791       return;
792     }
793   }
794 }
795
796 void BrowserOpenedNotificationObserver::set_for_browser_command(
797     bool for_browser_command) {
798   for_browser_command_ = for_browser_command;
799 }
800
801 BrowserClosedNotificationObserver::BrowserClosedNotificationObserver(
802     Browser* browser,
803     AutomationProvider* automation,
804     IPC::Message* reply_message,
805     bool use_json_interface)
806     : automation_(automation->AsWeakPtr()),
807       reply_message_(reply_message),
808       use_json_interface_(use_json_interface),
809       for_browser_command_(false) {
810   registrar_.Add(this, chrome::NOTIFICATION_BROWSER_CLOSED,
811                  content::Source<Browser>(browser));
812 }
813
814 BrowserClosedNotificationObserver::~BrowserClosedNotificationObserver() {}
815
816 void BrowserClosedNotificationObserver::Observe(
817     int type, const content::NotificationSource& source,
818     const content::NotificationDetails& details) {
819   DCHECK_EQ(chrome::NOTIFICATION_BROWSER_CLOSED, type);
820
821   if (!automation_.get()) {
822     delete this;
823     return;
824   }
825
826   // The automation layer doesn't support non-native desktops.
827   int browser_count = static_cast<int>(BrowserList::GetInstance(
828                           chrome::HOST_DESKTOP_TYPE_NATIVE)->size());
829   // We get the notification before the browser is removed from the BrowserList.
830   bool app_closing = browser_count == 1;
831
832   if (use_json_interface_) {
833     AutomationJSONReply(automation_.get(), reply_message_.release())
834         .SendSuccess(NULL);
835   } else {
836     if (for_browser_command_) {
837       AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_.get(),
838                                                            true);
839     } else {
840       AutomationMsg_CloseBrowser::WriteReplyParams(reply_message_.get(), true,
841                                                    app_closing);
842     }
843     automation_->Send(reply_message_.release());
844   }
845   delete this;
846 }
847
848 void BrowserClosedNotificationObserver::set_for_browser_command(
849     bool for_browser_command) {
850   for_browser_command_ = for_browser_command;
851 }
852
853 BrowserCountChangeNotificationObserver::BrowserCountChangeNotificationObserver(
854     int target_count,
855     AutomationProvider* automation,
856     IPC::Message* reply_message)
857     : target_count_(target_count),
858       automation_(automation->AsWeakPtr()),
859       reply_message_(reply_message) {
860   registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED,
861                  content::NotificationService::AllBrowserContextsAndSources());
862   registrar_.Add(this, chrome::NOTIFICATION_BROWSER_CLOSED,
863                  content::NotificationService::AllBrowserContextsAndSources());
864 }
865
866 BrowserCountChangeNotificationObserver::
867     ~BrowserCountChangeNotificationObserver() {}
868
869 void BrowserCountChangeNotificationObserver::Observe(
870     int type,
871     const content::NotificationSource& source,
872     const content::NotificationDetails& details) {
873   DCHECK(type == chrome::NOTIFICATION_BROWSER_OPENED ||
874          type == chrome::NOTIFICATION_BROWSER_CLOSED);
875
876   // The automation layer doesn't support non-native desktops.
877   int current_count = static_cast<int>(BrowserList::GetInstance(
878                           chrome::HOST_DESKTOP_TYPE_NATIVE)->size());
879   if (type == chrome::NOTIFICATION_BROWSER_CLOSED) {
880     // At the time of the notification the browser being closed is not removed
881     // from the list. The real count is one less than the reported count.
882     DCHECK_LT(0, current_count);
883     current_count--;
884   }
885
886   if (!automation_.get()) {
887     delete this;
888     return;
889   }
890
891   if (current_count == target_count_) {
892     AutomationMsg_WaitForBrowserWindowCountToBecome::WriteReplyParams(
893         reply_message_.get(), true);
894     automation_->Send(reply_message_.release());
895     delete this;
896   }
897 }
898
899 namespace {
900
901 // Define mapping from command to notification
902 struct CommandNotification {
903   int command;
904   int notification_type;
905 };
906
907 const struct CommandNotification command_notifications[] = {
908   {IDC_DUPLICATE_TAB, chrome::NOTIFICATION_TAB_PARENTED},
909
910   // Returns as soon as the restored tab is created. To further wait until
911   // the content page is loaded, use WaitForTabToBeRestored.
912   {IDC_RESTORE_TAB, chrome::NOTIFICATION_TAB_PARENTED},
913
914   // For the following commands, we need to wait for a new tab to be created,
915   // load to finish, and title to change.
916   {IDC_MANAGE_EXTENSIONS, content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED},
917   {IDC_OPTIONS, content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED},
918   {IDC_PRINT, content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED},
919   {IDC_SHOW_DOWNLOADS, content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED},
920   {IDC_SHOW_HISTORY, content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED},
921 };
922
923 }  // namespace
924
925 ExecuteBrowserCommandObserver::~ExecuteBrowserCommandObserver() {
926 }
927
928 // static
929 bool ExecuteBrowserCommandObserver::CreateAndRegisterObserver(
930     AutomationProvider* automation,
931     Browser* browser,
932     int command,
933     IPC::Message* reply_message,
934     bool use_json_interface) {
935   bool result = true;
936   switch (command) {
937     case IDC_NEW_TAB: {
938       new NewTabObserver(automation, reply_message, use_json_interface);
939       break;
940     }
941     case IDC_NEW_WINDOW:
942     case IDC_NEW_INCOGNITO_WINDOW: {
943       BrowserOpenedNotificationObserver* observer =
944           new BrowserOpenedNotificationObserver(automation, reply_message,
945                                                 use_json_interface);
946       observer->set_for_browser_command(true);
947       break;
948     }
949     case IDC_CLOSE_WINDOW: {
950       BrowserClosedNotificationObserver* observer =
951           new BrowserClosedNotificationObserver(browser, automation,
952                                                 reply_message,
953                                                 use_json_interface);
954       observer->set_for_browser_command(true);
955       break;
956     }
957     case IDC_CLOSE_TAB: {
958       TabClosedNotificationObserver* observer =
959           new TabClosedNotificationObserver(automation, true, reply_message,
960                                             use_json_interface);
961       observer->set_for_browser_command(true);
962       break;
963     }
964     case IDC_BACK:
965     case IDC_FORWARD:
966     case IDC_RELOAD: {
967       new NavigationNotificationObserver(
968           &browser->tab_strip_model()->GetActiveWebContents()->GetController(),
969           automation, reply_message, 1, false, use_json_interface);
970       break;
971     }
972     default: {
973       ExecuteBrowserCommandObserver* observer =
974           new ExecuteBrowserCommandObserver(automation, reply_message,
975                                             use_json_interface);
976       if (!observer->Register(command)) {
977         observer->ReleaseReply();
978         delete observer;
979         result = false;
980       }
981       break;
982     }
983   }
984   return result;
985 }
986
987 void ExecuteBrowserCommandObserver::Observe(
988     int type, const content::NotificationSource& source,
989     const content::NotificationDetails& details) {
990   if (type == notification_type_) {
991     if (automation_.get()) {
992       if (use_json_interface_) {
993         AutomationJSONReply(automation_.get(), reply_message_.release())
994             .SendSuccess(NULL);
995       } else {
996         AutomationMsg_WindowExecuteCommand::WriteReplyParams(
997             reply_message_.get(), true);
998         automation_->Send(reply_message_.release());
999       }
1000     }
1001     delete this;
1002   } else {
1003     NOTREACHED();
1004   }
1005 }
1006
1007 ExecuteBrowserCommandObserver::ExecuteBrowserCommandObserver(
1008     AutomationProvider* automation,
1009     IPC::Message* reply_message,
1010     bool use_json_interface)
1011     : automation_(automation->AsWeakPtr()),
1012       notification_type_(content::NOTIFICATION_ALL),
1013       reply_message_(reply_message),
1014       use_json_interface_(use_json_interface) {
1015 }
1016
1017 bool ExecuteBrowserCommandObserver::Register(int command) {
1018   if (!Getint(command, &notification_type_))
1019     return false;
1020   registrar_.Add(this, notification_type_,
1021                  content::NotificationService::AllSources());
1022   return true;
1023 }
1024
1025 bool ExecuteBrowserCommandObserver::Getint(
1026     int command, int* type) {
1027   if (!type)
1028     return false;
1029   bool found = false;
1030   for (unsigned int i = 0; i < arraysize(command_notifications); i++) {
1031     if (command_notifications[i].command == command) {
1032       *type = command_notifications[i].notification_type;
1033       found = true;
1034       break;
1035     }
1036   }
1037   return found;
1038 }
1039
1040 IPC::Message* ExecuteBrowserCommandObserver::ReleaseReply() {
1041   return reply_message_.release();
1042 }
1043
1044 FindInPageNotificationObserver::FindInPageNotificationObserver(
1045     AutomationProvider* automation, WebContents* parent_tab,
1046     bool reply_with_json, IPC::Message* reply_message)
1047     : automation_(automation->AsWeakPtr()),
1048       active_match_ordinal_(-1),
1049       reply_with_json_(reply_with_json),
1050       reply_message_(reply_message) {
1051   registrar_.Add(this, chrome::NOTIFICATION_FIND_RESULT_AVAILABLE,
1052                  content::Source<WebContents>(parent_tab));
1053 }
1054
1055 FindInPageNotificationObserver::~FindInPageNotificationObserver() {
1056 }
1057
1058 void FindInPageNotificationObserver::Observe(
1059     int type, const content::NotificationSource& source,
1060     const content::NotificationDetails& details) {
1061   content::Details<FindNotificationDetails> find_details(details);
1062   if (!(find_details->final_update() && reply_message_ != NULL)) {
1063     DVLOG(1) << "Ignoring, since we only care about the final message";
1064     return;
1065   }
1066
1067   if (!automation_.get()) {
1068     delete this;
1069     return;
1070   }
1071
1072   // We get multiple responses and one of those will contain the ordinal.
1073   // This message comes to us before the final update is sent.
1074   if (find_details->request_id() == kFindInPageRequestId) {
1075     if (reply_with_json_) {
1076       scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
1077       return_value->SetInteger("match_count",
1078           find_details->number_of_matches());
1079       gfx::Rect rect = find_details->selection_rect();
1080       // If MatchCount is > 0, then rect should not be Empty.
1081       // We dont guard it here because we want to let the test
1082       // code catch this invalid case if needed.
1083       if (!rect.IsEmpty()) {
1084         return_value->SetInteger("match_left", rect.x());
1085         return_value->SetInteger("match_top", rect.y());
1086         return_value->SetInteger("match_right", rect.right());
1087         return_value->SetInteger("match_bottom", rect.bottom());
1088       }
1089       AutomationJSONReply(automation_.get(), reply_message_.release())
1090           .SendSuccess(return_value.get());
1091       delete this;
1092     } else {
1093       if (find_details->active_match_ordinal() > -1) {
1094         active_match_ordinal_ = find_details->active_match_ordinal();
1095         AutomationMsg_Find::WriteReplyParams(reply_message_.get(),
1096             active_match_ordinal_, find_details->number_of_matches());
1097         automation_->Send(reply_message_.release());
1098       }
1099     }
1100   }
1101 }
1102
1103 // static
1104 const int FindInPageNotificationObserver::kFindInPageRequestId = -1;
1105
1106 DomOperationObserver::DomOperationObserver(int automation_id)
1107     : automation_id_(automation_id) {
1108   registrar_.Add(this, content::NOTIFICATION_DOM_OPERATION_RESPONSE,
1109                  content::NotificationService::AllSources());
1110   registrar_.Add(this, chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN,
1111                  content::NotificationService::AllSources());
1112   registrar_.Add(this, chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
1113                  content::NotificationService::AllSources());
1114 }
1115
1116 DomOperationObserver::~DomOperationObserver() {}
1117
1118 void DomOperationObserver::Observe(
1119     int type, const content::NotificationSource& source,
1120     const content::NotificationDetails& details) {
1121   if (type == content::NOTIFICATION_DOM_OPERATION_RESPONSE) {
1122     content::Details<DomOperationNotificationDetails> dom_op_details(details);
1123     if (dom_op_details->automation_id == automation_id_)
1124       OnDomOperationCompleted(dom_op_details->json);
1125   } else if (type == chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN) {
1126     OnJavascriptBlocked();
1127   } else {
1128     DCHECK_EQ(chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED, type);
1129     WebContents* web_contents = content::Source<WebContents>(source).ptr();
1130     if (web_contents) {
1131       TabSpecificContentSettings* tab_content_settings =
1132           TabSpecificContentSettings::FromWebContents(web_contents);
1133       if (tab_content_settings &&
1134           tab_content_settings->IsContentBlocked(
1135               CONTENT_SETTINGS_TYPE_JAVASCRIPT))
1136         OnJavascriptBlocked();
1137     }
1138   }
1139 }
1140
1141 DomOperationMessageSender::DomOperationMessageSender(
1142     AutomationProvider* automation,
1143     IPC::Message* reply_message,
1144     bool use_json_interface)
1145     : DomOperationObserver(0),
1146       automation_(automation->AsWeakPtr()),
1147       reply_message_(reply_message),
1148       use_json_interface_(use_json_interface) {
1149 }
1150
1151 DomOperationMessageSender::~DomOperationMessageSender() {}
1152
1153 void DomOperationMessageSender::OnDomOperationCompleted(
1154     const std::string& json) {
1155   if (automation_.get()) {
1156     if (use_json_interface_) {
1157       DictionaryValue dict;
1158       dict.SetString("result", json);
1159       AutomationJSONReply(automation_.get(), reply_message_.release())
1160           .SendSuccess(&dict);
1161     } else {
1162       AutomationMsg_DomOperation::WriteReplyParams(reply_message_.get(), json);
1163       automation_->Send(reply_message_.release());
1164     }
1165   }
1166   delete this;
1167 }
1168
1169 void DomOperationMessageSender::OnJavascriptBlocked() {
1170   if (automation_.get() && use_json_interface_) {
1171     AutomationJSONReply(automation_.get(), reply_message_.release())
1172         .SendError("Javascript execution was blocked");
1173     delete this;
1174   }
1175 }
1176
1177 MetricEventDurationObserver::MetricEventDurationObserver() {
1178   registrar_.Add(this, chrome::NOTIFICATION_METRIC_EVENT_DURATION,
1179                  content::NotificationService::AllSources());
1180 }
1181
1182 MetricEventDurationObserver::~MetricEventDurationObserver() {}
1183
1184 int MetricEventDurationObserver::GetEventDurationMs(
1185     const std::string& event_name) {
1186   EventDurationMap::const_iterator it = durations_.find(event_name);
1187   if (it == durations_.end())
1188     return -1;
1189   return it->second;
1190 }
1191
1192 void MetricEventDurationObserver::Observe(
1193     int type,
1194     const content::NotificationSource& source,
1195     const content::NotificationDetails& details) {
1196   if (type != chrome::NOTIFICATION_METRIC_EVENT_DURATION) {
1197     NOTREACHED();
1198     return;
1199   }
1200   MetricEventDurationDetails* metric_event_duration =
1201       content::Details<MetricEventDurationDetails>(details).ptr();
1202   durations_[metric_event_duration->event_name] =
1203       metric_event_duration->duration_ms;
1204 }
1205
1206 InfoBarCountObserver::InfoBarCountObserver(AutomationProvider* automation,
1207                                            IPC::Message* reply_message,
1208                                            WebContents* web_contents,
1209                                            size_t target_count)
1210     : automation_(automation->AsWeakPtr()),
1211       reply_message_(reply_message),
1212       web_contents_(web_contents),
1213       target_count_(target_count) {
1214   content::Source<InfoBarService> source(
1215       InfoBarService::FromWebContents(web_contents));
1216   registrar_.Add(this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED,
1217                  source);
1218   registrar_.Add(this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED,
1219                  source);
1220   CheckCount();
1221 }
1222
1223 InfoBarCountObserver::~InfoBarCountObserver() {}
1224
1225 void InfoBarCountObserver::Observe(
1226     int type,
1227     const content::NotificationSource& source,
1228     const content::NotificationDetails& details) {
1229   DCHECK(type == chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED ||
1230          type == chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED);
1231   CheckCount();
1232 }
1233
1234 void InfoBarCountObserver::CheckCount() {
1235   if (InfoBarService::FromWebContents(web_contents_)->infobar_count() !=
1236       target_count_)
1237     return;
1238
1239   if (automation_.get()) {
1240     AutomationMsg_WaitForInfoBarCount::WriteReplyParams(reply_message_.get(),
1241                                                         true);
1242     automation_->Send(reply_message_.release());
1243   }
1244   delete this;
1245 }
1246
1247 AutomationProviderBookmarkModelObserver::
1248 AutomationProviderBookmarkModelObserver(
1249     AutomationProvider* provider,
1250     IPC::Message* reply_message,
1251     BookmarkModel* model,
1252     bool use_json_interface)
1253     : automation_provider_(provider->AsWeakPtr()),
1254       reply_message_(reply_message),
1255       model_(model),
1256       use_json_interface_(use_json_interface) {
1257   model_->AddObserver(this);
1258 }
1259
1260 AutomationProviderBookmarkModelObserver::
1261     ~AutomationProviderBookmarkModelObserver() {
1262   model_->RemoveObserver(this);
1263 }
1264
1265 void AutomationProviderBookmarkModelObserver::Loaded(BookmarkModel* model,
1266                                                      bool ids_reassigned) {
1267   ReplyAndDelete(true);
1268 }
1269
1270 void AutomationProviderBookmarkModelObserver::BookmarkModelBeingDeleted(
1271     BookmarkModel* model) {
1272   ReplyAndDelete(false);
1273 }
1274
1275 IPC::Message* AutomationProviderBookmarkModelObserver::ReleaseReply() {
1276   return reply_message_.release();
1277 }
1278
1279 void AutomationProviderBookmarkModelObserver::ReplyAndDelete(bool success) {
1280   if (automation_provider_.get()) {
1281     if (use_json_interface_) {
1282       AutomationJSONReply(automation_provider_.get(), reply_message_.release())
1283           .SendSuccess(NULL);
1284     } else {
1285       AutomationMsg_WaitForBookmarkModelToLoad::WriteReplyParams(
1286           reply_message_.get(), success);
1287       automation_provider_->Send(reply_message_.release());
1288     }
1289   }
1290   delete this;
1291 }
1292
1293 AutomationProviderDownloadUpdatedObserver::
1294 AutomationProviderDownloadUpdatedObserver(
1295     AutomationProvider* provider,
1296     IPC::Message* reply_message,
1297     bool wait_for_open,
1298     bool incognito)
1299     : provider_(provider->AsWeakPtr()),
1300       reply_message_(reply_message),
1301       wait_for_open_(wait_for_open),
1302       incognito_(incognito) {
1303 }
1304
1305 AutomationProviderDownloadUpdatedObserver::
1306     ~AutomationProviderDownloadUpdatedObserver() {}
1307
1308 void AutomationProviderDownloadUpdatedObserver::OnDownloadUpdated(
1309     DownloadItem* download) {
1310   // If this observer is watching for open, only send the reply if the download
1311   // has been auto-opened.
1312   if (wait_for_open_ && !download->GetAutoOpened())
1313     return;
1314
1315   download->RemoveObserver(this);
1316
1317   if (provider_.get()) {
1318     scoped_ptr<DictionaryValue> return_value(
1319         provider_->GetDictionaryFromDownloadItem(download, incognito_));
1320     AutomationJSONReply(provider_.get(), reply_message_.release())
1321         .SendSuccess(return_value.get());
1322   }
1323   delete this;
1324 }
1325
1326 void AutomationProviderDownloadUpdatedObserver::OnDownloadOpened(
1327     DownloadItem* download) {
1328   download->RemoveObserver(this);
1329
1330   if (provider_.get()) {
1331     scoped_ptr<DictionaryValue> return_value(
1332         provider_->GetDictionaryFromDownloadItem(download, incognito_));
1333     AutomationJSONReply(provider_.get(), reply_message_.release())
1334         .SendSuccess(return_value.get());
1335   }
1336   delete this;
1337 }
1338
1339 AutomationProviderDownloadModelChangedObserver::
1340 AutomationProviderDownloadModelChangedObserver(
1341     AutomationProvider* provider,
1342     IPC::Message* reply_message,
1343     DownloadManager* download_manager)
1344     : provider_(provider->AsWeakPtr()),
1345       reply_message_(reply_message),
1346       notifier_(download_manager, this) {
1347 }
1348
1349 AutomationProviderDownloadModelChangedObserver::
1350     ~AutomationProviderDownloadModelChangedObserver() {}
1351
1352 void AutomationProviderDownloadModelChangedObserver::ModelChanged() {
1353   if (provider_.get())
1354     AutomationJSONReply(provider_.get(), reply_message_.release())
1355         .SendSuccess(NULL);
1356   delete this;
1357 }
1358
1359 void AutomationProviderDownloadModelChangedObserver::OnDownloadCreated(
1360     DownloadManager* manager, DownloadItem* item) {
1361   ModelChanged();
1362 }
1363
1364 void AutomationProviderDownloadModelChangedObserver::OnDownloadRemoved(
1365     DownloadManager* manager, DownloadItem* item) {
1366   ModelChanged();
1367 }
1368
1369 AllDownloadsCompleteObserver::AllDownloadsCompleteObserver(
1370     AutomationProvider* provider,
1371     IPC::Message* reply_message,
1372     DownloadManager* download_manager,
1373     ListValue* pre_download_ids)
1374     : provider_(provider->AsWeakPtr()),
1375       reply_message_(reply_message),
1376       download_manager_(download_manager) {
1377   for (ListValue::iterator it = pre_download_ids->begin();
1378        it != pre_download_ids->end(); ++it) {
1379     int val = 0;
1380     if ((*it)->GetAsInteger(&val)) {
1381       pre_download_ids_.insert(val);
1382     } else {
1383       AutomationJSONReply(provider_.get(), reply_message_.release())
1384           .SendError("Cannot convert ID of prior download to integer.");
1385       delete this;
1386       return;
1387     }
1388   }
1389   download_manager_->AddObserver(this);
1390   DownloadManager::DownloadVector all_items;
1391   download_manager->GetAllDownloads(&all_items);
1392   for (DownloadManager::DownloadVector::const_iterator
1393        it = all_items.begin(); it != all_items.end(); ++it) {
1394     OnDownloadCreated(download_manager_, *it);
1395   }
1396   ReplyIfNecessary();
1397 }
1398
1399 AllDownloadsCompleteObserver::~AllDownloadsCompleteObserver() {
1400   if (download_manager_) {
1401     download_manager_->RemoveObserver(this);
1402     download_manager_ = NULL;
1403   }
1404   for (std::set<DownloadItem*>::const_iterator it = pending_downloads_.begin();
1405        it != pending_downloads_.end(); ++it) {
1406     (*it)->RemoveObserver(this);
1407   }
1408   pending_downloads_.clear();
1409 }
1410
1411 void AllDownloadsCompleteObserver::ManagerGoingDown(DownloadManager* manager) {
1412   DCHECK_EQ(manager, download_manager_);
1413   download_manager_->RemoveObserver(this);
1414   download_manager_ = NULL;
1415 }
1416
1417 void AllDownloadsCompleteObserver::OnDownloadCreated(
1418     DownloadManager* manager, DownloadItem* item) {
1419   // This method is also called in the c-tor for previously existing items.
1420   if (pre_download_ids_.find(item->GetId()) == pre_download_ids_.end() &&
1421       item->GetState() == DownloadItem::IN_PROGRESS) {
1422     item->AddObserver(this);
1423     pending_downloads_.insert(item);
1424   }
1425 }
1426
1427 void AllDownloadsCompleteObserver::OnDownloadUpdated(DownloadItem* download) {
1428   // If the current download's status has changed to a final state (not state
1429   // "in progress"), remove it from the pending list.
1430   if (download->GetState() != DownloadItem::IN_PROGRESS) {
1431     download->RemoveObserver(this);
1432     pending_downloads_.erase(download);
1433     ReplyIfNecessary();
1434   }
1435 }
1436
1437 void AllDownloadsCompleteObserver::ReplyIfNecessary() {
1438   if (!pending_downloads_.empty())
1439     return;
1440
1441   download_manager_->RemoveObserver(this);
1442   if (provider_.get())
1443     AutomationJSONReply(provider_.get(), reply_message_.release())
1444         .SendSuccess(NULL);
1445   delete this;
1446 }
1447
1448 AutomationProviderSearchEngineObserver::AutomationProviderSearchEngineObserver(
1449     AutomationProvider* provider,
1450     Profile* profile,
1451     IPC::Message* reply_message)
1452     : provider_(provider->AsWeakPtr()),
1453       profile_(profile),
1454       reply_message_(reply_message) {
1455 }
1456
1457 AutomationProviderSearchEngineObserver::
1458     ~AutomationProviderSearchEngineObserver() {}
1459
1460 void AutomationProviderSearchEngineObserver::OnTemplateURLServiceChanged() {
1461   if (provider_.get()) {
1462     TemplateURLService* url_service =
1463         TemplateURLServiceFactory::GetForProfile(profile_);
1464     url_service->RemoveObserver(this);
1465     AutomationJSONReply(provider_.get(), reply_message_.release())
1466         .SendSuccess(NULL);
1467   }
1468   delete this;
1469 }
1470
1471 AutomationProviderHistoryObserver::AutomationProviderHistoryObserver(
1472     AutomationProvider* provider,
1473     IPC::Message* reply_message)
1474     : provider_(provider->AsWeakPtr()),
1475       reply_message_(reply_message) {
1476 }
1477
1478 AutomationProviderHistoryObserver::~AutomationProviderHistoryObserver() {}
1479
1480 void AutomationProviderHistoryObserver::HistoryQueryComplete(
1481     HistoryService::Handle request_handle,
1482     history::QueryResults* results) {
1483   if (!provider_.get()) {
1484     delete this;
1485     return;
1486   }
1487
1488   scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
1489
1490   ListValue* history_list = new ListValue;
1491   for (size_t i = 0; i < results->size(); ++i) {
1492     DictionaryValue* page_value = new DictionaryValue;
1493     history::URLResult const &page = (*results)[i];
1494     page_value->SetString("title", page.title());
1495     page_value->SetString("url", page.url().spec());
1496     page_value->SetDouble("time",
1497                           static_cast<double>(page.visit_time().ToDoubleT()));
1498     page_value->SetString("snippet", page.snippet().text());
1499     page_value->SetBoolean(
1500         "starred",
1501         BookmarkModelFactory::GetForProfile(
1502             provider_->profile())->IsBookmarked(page.url()));
1503     history_list->Append(page_value);
1504   }
1505
1506   return_value->Set("history", history_list);
1507   // Return history info.
1508   AutomationJSONReply reply(provider_.get(), reply_message_.release());
1509   reply.SendSuccess(return_value.get());
1510   delete this;
1511 }
1512
1513 AutomationProviderImportSettingsObserver::
1514 AutomationProviderImportSettingsObserver(
1515     AutomationProvider* provider,
1516     IPC::Message* reply_message)
1517     : provider_(provider->AsWeakPtr()),
1518       reply_message_(reply_message) {
1519 }
1520
1521 AutomationProviderImportSettingsObserver::
1522     ~AutomationProviderImportSettingsObserver() {}
1523
1524 void AutomationProviderImportSettingsObserver::ImportStarted() {
1525 }
1526
1527 void AutomationProviderImportSettingsObserver::ImportItemStarted(
1528     importer::ImportItem item) {
1529 }
1530
1531 void AutomationProviderImportSettingsObserver::ImportItemEnded(
1532     importer::ImportItem item) {
1533 }
1534
1535 void AutomationProviderImportSettingsObserver::ImportEnded() {
1536   if (provider_.get())
1537     AutomationJSONReply(provider_.get(), reply_message_.release())
1538         .SendSuccess(NULL);
1539   delete this;
1540 }
1541
1542 AutomationProviderGetPasswordsObserver::AutomationProviderGetPasswordsObserver(
1543     AutomationProvider* provider,
1544     IPC::Message* reply_message)
1545     : provider_(provider->AsWeakPtr()),
1546       reply_message_(reply_message) {
1547 }
1548
1549 AutomationProviderGetPasswordsObserver::
1550     ~AutomationProviderGetPasswordsObserver() {}
1551
1552 void AutomationProviderGetPasswordsObserver::OnPasswordStoreRequestDone(
1553     CancelableRequestProvider::Handle handle,
1554     const std::vector<autofill::PasswordForm*>& result) {
1555   if (!provider_.get()) {
1556     delete this;
1557     return;
1558   }
1559
1560   scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
1561
1562   ListValue* passwords = new ListValue;
1563   for (std::vector<autofill::PasswordForm*>::const_iterator it =
1564            result.begin(); it != result.end(); ++it) {
1565     DictionaryValue* password_val = new DictionaryValue;
1566     autofill::PasswordForm* password_form = *it;
1567     password_val->SetString("username_value", password_form->username_value);
1568     password_val->SetString("password_value", password_form->password_value);
1569     password_val->SetString("signon_realm", password_form->signon_realm);
1570     password_val->SetDouble(
1571         "time", static_cast<double>(password_form->date_created.ToDoubleT()));
1572     password_val->SetString("origin_url", password_form->origin.spec());
1573     password_val->SetString("username_element",
1574                             password_form->username_element);
1575     password_val->SetString("password_element",
1576                             password_form->password_element);
1577     password_val->SetString("submit_element", password_form->submit_element);
1578     password_val->SetString("action_target", password_form->action.spec());
1579     password_val->SetBoolean("blacklist", password_form->blacklisted_by_user);
1580     passwords->Append(password_val);
1581   }
1582
1583   return_value->Set("passwords", passwords);
1584   AutomationJSONReply(provider_.get(), reply_message_.release())
1585       .SendSuccess(return_value.get());
1586   delete this;
1587 }
1588
1589 void AutomationProviderGetPasswordsObserver::OnGetPasswordStoreResults(
1590     const std::vector<autofill::PasswordForm*>& results) {
1591   // TODO(kaiwang): Implement when I refactor
1592   // PasswordManager::GetAutofillableLogins.
1593   NOTIMPLEMENTED();
1594 }
1595
1596 PasswordStoreLoginsChangedObserver::PasswordStoreLoginsChangedObserver(
1597     AutomationProvider* automation,
1598     IPC::Message* reply_message,
1599     PasswordStoreChange::Type expected_type,
1600     const std::string& result_key)
1601     : automation_(automation->AsWeakPtr()),
1602       reply_message_(reply_message),
1603       expected_type_(expected_type),
1604       result_key_(result_key),
1605       done_event_(false, false) {
1606   AddRef();
1607 }
1608
1609 PasswordStoreLoginsChangedObserver::~PasswordStoreLoginsChangedObserver() {
1610   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1611 }
1612
1613 void PasswordStoreLoginsChangedObserver::Init() {
1614   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1615   BrowserThread::PostTask(
1616       BrowserThread::DB,
1617       FROM_HERE,
1618       base::Bind(&PasswordStoreLoginsChangedObserver::RegisterObserversTask,
1619                  this));
1620   done_event_.Wait();
1621 }
1622
1623 void PasswordStoreLoginsChangedObserver::RegisterObserversTask() {
1624   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
1625   registrar_.reset(new content::NotificationRegistrar);
1626   registrar_->Add(this, chrome::NOTIFICATION_LOGINS_CHANGED,
1627                   content::NotificationService::AllSources());
1628   done_event_.Signal();
1629 }
1630
1631 void PasswordStoreLoginsChangedObserver::Observe(
1632     int type,
1633     const content::NotificationSource& source,
1634     const content::NotificationDetails& details) {
1635   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
1636   DCHECK(type == chrome::NOTIFICATION_LOGINS_CHANGED);
1637   registrar_.reset();  // Must be done from the DB thread.
1638   PasswordStoreChangeList* change_details =
1639       content::Details<PasswordStoreChangeList>(details).ptr();
1640   if (change_details->size() != 1 ||
1641       change_details->front().type() != expected_type_) {
1642     // Notify the UI thread that there's an error.
1643     std::string error = "Unexpected password store login change details.";
1644     BrowserThread::PostTask(
1645         BrowserThread::UI,
1646         FROM_HERE,
1647         base::Bind(&PasswordStoreLoginsChangedObserver::IndicateError, this,
1648                    error));
1649     return;
1650   }
1651
1652   // Notify the UI thread that we're done listening.
1653   BrowserThread::PostTask(
1654       BrowserThread::UI,
1655       FROM_HERE,
1656       base::Bind(&PasswordStoreLoginsChangedObserver::IndicateDone, this));
1657 }
1658
1659 void PasswordStoreLoginsChangedObserver::IndicateDone() {
1660   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1661   if (automation_.get()) {
1662     if (result_key_.empty()) {
1663       AutomationJSONReply(automation_.get(), reply_message_.release())
1664           .SendSuccess(NULL);
1665     } else {
1666       scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
1667       return_value->SetBoolean(result_key_, true);
1668       AutomationJSONReply(automation_.get(), reply_message_.release())
1669           .SendSuccess(return_value.get());
1670     }
1671   }
1672   Release();
1673 }
1674
1675 void PasswordStoreLoginsChangedObserver::IndicateError(
1676     const std::string& error) {
1677   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1678   if (automation_.get())
1679     AutomationJSONReply(automation_.get(), reply_message_.release())
1680         .SendError(error);
1681   Release();
1682 }
1683
1684 OmniboxAcceptNotificationObserver::OmniboxAcceptNotificationObserver(
1685     NavigationController* controller,
1686     AutomationProvider* automation,
1687     IPC::Message* reply_message)
1688     : automation_(automation->AsWeakPtr()),
1689       reply_message_(reply_message),
1690       controller_(controller) {
1691   content::Source<NavigationController> source(controller_);
1692   registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, source);
1693   // Pages requiring auth don't send LOAD_STOP.
1694   registrar_.Add(this, chrome::NOTIFICATION_AUTH_NEEDED, source);
1695 }
1696
1697 OmniboxAcceptNotificationObserver::~OmniboxAcceptNotificationObserver() {
1698 }
1699
1700 void OmniboxAcceptNotificationObserver::Observe(
1701     int type,
1702     const content::NotificationSource& source,
1703     const content::NotificationDetails& details) {
1704   if (type == content::NOTIFICATION_LOAD_STOP ||
1705       type == chrome::NOTIFICATION_AUTH_NEEDED) {
1706     if (automation_.get()) {
1707       AutomationJSONReply(automation_.get(), reply_message_.release())
1708           .SendSuccess(NULL);
1709     }
1710     delete this;
1711   } else {
1712     NOTREACHED();
1713   }
1714 }
1715
1716 SavePackageNotificationObserver::SavePackageNotificationObserver(
1717     content::DownloadManager* download_manager,
1718     AutomationProvider* automation,
1719     IPC::Message* reply_message)
1720     : download_manager_(download_manager),
1721       automation_(automation->AsWeakPtr()),
1722       reply_message_(reply_message) {
1723   download_manager_->AddObserver(this);
1724 }
1725
1726 SavePackageNotificationObserver::~SavePackageNotificationObserver() {
1727   download_manager_->RemoveObserver(this);
1728 }
1729
1730 void SavePackageNotificationObserver::OnSavePackageSuccessfullyFinished(
1731     content::DownloadManager* manager, content::DownloadItem* item) {
1732   if (automation_.get()) {
1733     AutomationJSONReply(automation_.get(), reply_message_.release())
1734         .SendSuccess(NULL);
1735   }
1736   delete this;
1737 }
1738
1739 void SavePackageNotificationObserver::ManagerGoingDown(
1740     content::DownloadManager* manager) {
1741   delete this;
1742 }
1743
1744 namespace {
1745
1746 // Returns a vector of dictionaries containing information about installed apps,
1747 // as identified from a given list of extensions.  The caller takes ownership
1748 // of the created vector.
1749 std::vector<DictionaryValue*>* GetAppInfoFromExtensions(
1750     const ExtensionSet* extensions,
1751     ExtensionService* ext_service) {
1752   std::vector<DictionaryValue*>* apps_list =
1753       new std::vector<DictionaryValue*>();
1754   for (ExtensionSet::const_iterator ext = extensions->begin();
1755        ext != extensions->end(); ++ext) {
1756     // Only return information about extensions that are actually apps.
1757     if ((*ext)->is_app()) {
1758       DictionaryValue* app_info = new DictionaryValue();
1759       AppLauncherHandler::CreateAppInfo(ext->get(), ext_service, app_info);
1760       app_info->SetBoolean(
1761           "is_component_extension",
1762           (*ext)->location() == extensions::Manifest::COMPONENT);
1763
1764       // Convert the launch_type integer into a more descriptive string.
1765       int launch_type;
1766       const char* kLaunchType = "launch_type";
1767       if (!app_info->GetInteger(kLaunchType, &launch_type)) {
1768         NOTREACHED() << "Can't get integer from key " << kLaunchType;
1769         continue;
1770       }
1771       if (launch_type == extensions::ExtensionPrefs::LAUNCH_PINNED) {
1772         app_info->SetString(kLaunchType, "pinned");
1773       } else if (launch_type == extensions::ExtensionPrefs::LAUNCH_REGULAR) {
1774         app_info->SetString(kLaunchType, "regular");
1775       } else if (launch_type == extensions::ExtensionPrefs::LAUNCH_FULLSCREEN) {
1776         app_info->SetString(kLaunchType, "fullscreen");
1777       } else if (launch_type == extensions::ExtensionPrefs::LAUNCH_WINDOW) {
1778         app_info->SetString(kLaunchType, "window");
1779       } else {
1780         app_info->SetString(kLaunchType, "unknown");
1781       }
1782
1783       apps_list->push_back(app_info);
1784     }
1785   }
1786   return apps_list;
1787 }
1788
1789 }  // namespace
1790
1791 NTPInfoObserver::NTPInfoObserver(AutomationProvider* automation,
1792                                  IPC::Message* reply_message)
1793     : automation_(automation->AsWeakPtr()),
1794       reply_message_(reply_message),
1795       request_(0),
1796       ntp_info_(new DictionaryValue) {
1797   top_sites_ = automation_->profile()->GetTopSites();
1798   if (!top_sites_) {
1799     AutomationJSONReply(automation_.get(), reply_message_.release())
1800         .SendError("Profile does not have service for querying the top sites.");
1801     return;
1802   }
1803   TabRestoreService* service =
1804       TabRestoreServiceFactory::GetForProfile(automation_->profile());
1805   if (!service) {
1806     AutomationJSONReply(automation_.get(), reply_message_.release())
1807         .SendError("No TabRestoreService.");
1808     return;
1809   }
1810
1811   // Collect information about the apps in the new tab page.
1812   ExtensionService* ext_service = extensions::ExtensionSystem::Get(
1813       automation_->profile())->extension_service();
1814   if (!ext_service) {
1815     AutomationJSONReply(automation_.get(), reply_message_.release())
1816         .SendError("No ExtensionService.");
1817     return;
1818   }
1819   // Process enabled extensions.
1820   ListValue* apps_list = new ListValue();
1821   const ExtensionSet* extensions = ext_service->extensions();
1822   std::vector<DictionaryValue*>* enabled_apps = GetAppInfoFromExtensions(
1823       extensions, ext_service);
1824   for (std::vector<DictionaryValue*>::const_iterator app =
1825        enabled_apps->begin(); app != enabled_apps->end(); ++app) {
1826     (*app)->SetBoolean("is_disabled", false);
1827     apps_list->Append(*app);
1828   }
1829   delete enabled_apps;
1830   // Process disabled extensions.
1831   const ExtensionSet* disabled_extensions = ext_service->disabled_extensions();
1832   std::vector<DictionaryValue*>* disabled_apps = GetAppInfoFromExtensions(
1833       disabled_extensions, ext_service);
1834   for (std::vector<DictionaryValue*>::const_iterator app =
1835        disabled_apps->begin(); app != disabled_apps->end(); ++app) {
1836     (*app)->SetBoolean("is_disabled", true);
1837     apps_list->Append(*app);
1838   }
1839   delete disabled_apps;
1840   // Process terminated extensions.
1841   const ExtensionSet* terminated_extensions =
1842       ext_service->terminated_extensions();
1843   std::vector<DictionaryValue*>* terminated_apps = GetAppInfoFromExtensions(
1844       terminated_extensions, ext_service);
1845   for (std::vector<DictionaryValue*>::const_iterator app =
1846        terminated_apps->begin(); app != terminated_apps->end(); ++app) {
1847     (*app)->SetBoolean("is_disabled", true);
1848     apps_list->Append(*app);
1849   }
1850   delete terminated_apps;
1851   ntp_info_->Set("apps", apps_list);
1852
1853   // Get the info that would be displayed in the recently closed section.
1854   ListValue* recently_closed_list = new ListValue;
1855   RecentlyClosedTabsHandler::CreateRecentlyClosedValues(service->entries(),
1856                                                         recently_closed_list);
1857   ntp_info_->Set("recently_closed", recently_closed_list);
1858
1859   // Add default site URLs.
1860   ListValue* default_sites_list = new ListValue;
1861   history::MostVisitedURLList urls = top_sites_->GetPrepopulatePages();
1862   for (size_t i = 0; i < urls.size(); ++i) {
1863     default_sites_list->Append(Value::CreateStringValue(
1864         urls[i].url.possibly_invalid_spec()));
1865   }
1866   ntp_info_->Set("default_sites", default_sites_list);
1867
1868   registrar_.Add(this, chrome::NOTIFICATION_TOP_SITES_UPDATED,
1869                  content::Source<history::TopSites>(top_sites_));
1870   if (top_sites_->loaded()) {
1871     OnTopSitesLoaded();
1872   } else {
1873     registrar_.Add(this, chrome::NOTIFICATION_TOP_SITES_LOADED,
1874                    content::Source<Profile>(automation_->profile()));
1875   }
1876 }
1877
1878 NTPInfoObserver::~NTPInfoObserver() {}
1879
1880 void NTPInfoObserver::Observe(int type,
1881                               const content::NotificationSource& source,
1882                               const content::NotificationDetails& details) {
1883   if (type == chrome::NOTIFICATION_TOP_SITES_LOADED) {
1884     OnTopSitesLoaded();
1885   } else if (type == chrome::NOTIFICATION_TOP_SITES_UPDATED) {
1886     content::Details<CancelableRequestProvider::Handle> request_details(
1887           details);
1888     if (request_ == *request_details.ptr()) {
1889       top_sites_->GetMostVisitedURLs(
1890           base::Bind(&NTPInfoObserver::OnTopSitesReceived,
1891                      base::Unretained(this)));
1892     }
1893   }
1894 }
1895
1896 void NTPInfoObserver::OnTopSitesLoaded() {
1897   request_ = top_sites_->StartQueryForMostVisited();
1898 }
1899
1900 void NTPInfoObserver::OnTopSitesReceived(
1901     const history::MostVisitedURLList& visited_list) {
1902   if (!automation_.get()) {
1903     delete this;
1904     return;
1905   }
1906
1907   ListValue* list_value = new ListValue;
1908   for (size_t i = 0; i < visited_list.size(); ++i) {
1909     const history::MostVisitedURL& visited = visited_list[i];
1910     if (visited.url.spec().empty())
1911       break;  // This is the signal that there are no more real visited sites.
1912     DictionaryValue* dict = new DictionaryValue;
1913     dict->SetString("url", visited.url.spec());
1914     dict->SetString("title", visited.title);
1915     list_value->Append(dict);
1916   }
1917   ntp_info_->Set("most_visited", list_value);
1918   AutomationJSONReply(automation_.get(), reply_message_.release())
1919       .SendSuccess(ntp_info_.get());
1920   delete this;
1921 }
1922
1923 AppLaunchObserver::AppLaunchObserver(
1924     NavigationController* controller,
1925     AutomationProvider* automation,
1926     IPC::Message* reply_message,
1927     extension_misc::LaunchContainer launch_container)
1928     : controller_(controller),
1929       automation_(automation->AsWeakPtr()),
1930       reply_message_(reply_message),
1931       launch_container_(launch_container),
1932       new_window_id_(extension_misc::kUnknownWindowId) {
1933   if (launch_container_ == extension_misc::LAUNCH_TAB) {
1934     // Need to wait for the currently-active tab to reload.
1935     content::Source<NavigationController> source(controller_);
1936     registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, source);
1937   } else {
1938     // Need to wait for a new tab in a new window to load.
1939     registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
1940                    content::NotificationService::AllSources());
1941     registrar_.Add(this, chrome::NOTIFICATION_BROWSER_WINDOW_READY,
1942                    content::NotificationService::AllSources());
1943   }
1944 }
1945
1946 AppLaunchObserver::~AppLaunchObserver() {}
1947
1948 void AppLaunchObserver::Observe(int type,
1949                                 const content::NotificationSource& source,
1950                                 const content::NotificationDetails& details) {
1951   if (type == chrome::NOTIFICATION_BROWSER_WINDOW_READY) {
1952     new_window_id_ =
1953         ExtensionTabUtil::GetWindowId(content::Source<Browser>(source).ptr());
1954     return;
1955   }
1956
1957   DCHECK_EQ(content::NOTIFICATION_LOAD_STOP, type);
1958   SessionTabHelper* session_tab_helper = SessionTabHelper::FromWebContents(
1959       content::Source<NavigationController>(source)->GetWebContents());
1960   if ((launch_container_ == extension_misc::LAUNCH_TAB) ||
1961       (session_tab_helper &&
1962           (session_tab_helper->window_id().id() == new_window_id_))) {
1963     if (automation_.get()) {
1964       AutomationJSONReply(automation_.get(), reply_message_.release())
1965           .SendSuccess(NULL);
1966     }
1967     delete this;
1968   }
1969 }
1970
1971 namespace {
1972
1973 // Returns whether all active notifications have an associated process ID.
1974 bool AreActiveNotificationProcessesReady() {
1975   BalloonNotificationUIManager* manager =
1976       BalloonNotificationUIManager::GetInstanceForTesting();
1977   const BalloonCollection::Balloons& balloons =
1978       manager->balloon_collection()->GetActiveBalloons();
1979   BalloonCollection::Balloons::const_iterator iter;
1980   for (iter = balloons.begin(); iter != balloons.end(); ++iter) {
1981     BalloonHost* balloon_host = (*iter)->balloon_view()->GetHost();
1982     if (balloon_host && !balloon_host->IsRenderViewReady())
1983       return false;
1984   }
1985   return true;
1986 }
1987
1988 }  // namespace
1989
1990 GetAllNotificationsObserver::GetAllNotificationsObserver(
1991     AutomationProvider* automation,
1992     IPC::Message* reply_message)
1993     : automation_(automation->AsWeakPtr()),
1994       reply_message_(reply_message) {
1995   if (AreActiveNotificationProcessesReady()) {
1996     SendMessage();
1997   } else {
1998     registrar_.Add(this, chrome::NOTIFICATION_NOTIFY_BALLOON_CONNECTED,
1999                    content::NotificationService::AllSources());
2000   }
2001 }
2002
2003 GetAllNotificationsObserver::~GetAllNotificationsObserver() {}
2004
2005 void GetAllNotificationsObserver::Observe(
2006     int type,
2007     const content::NotificationSource& source,
2008     const content::NotificationDetails& details) {
2009   if (!automation_.get()) {
2010     delete this;
2011     return;
2012   }
2013   if (AreActiveNotificationProcessesReady())
2014     SendMessage();
2015 }
2016
2017 base::DictionaryValue* GetAllNotificationsObserver::NotificationToJson(
2018     const Notification* note) {
2019   DictionaryValue* dict = new base::DictionaryValue();
2020   dict->SetString("content_url", note->content_url().spec());
2021   dict->SetString("origin_url", note->origin_url().spec());
2022   dict->SetString("display_source", note->display_source());
2023   dict->SetString("id", note->notification_id());
2024   return dict;
2025 }
2026
2027 void GetAllNotificationsObserver::SendMessage() {
2028   BalloonNotificationUIManager* manager =
2029       BalloonNotificationUIManager::GetInstanceForTesting();
2030   const BalloonCollection::Balloons& balloons =
2031       manager->balloon_collection()->GetActiveBalloons();
2032   DictionaryValue return_value;
2033   ListValue* list = new ListValue;
2034   return_value.Set("notifications", list);
2035   BalloonCollection::Balloons::const_iterator balloon_iter;
2036   for (balloon_iter = balloons.begin(); balloon_iter != balloons.end();
2037        ++balloon_iter) {
2038     base::DictionaryValue* note = NotificationToJson(
2039         &(*balloon_iter)->notification());
2040     BalloonHost* balloon_host = (*balloon_iter)->balloon_view()->GetHost();
2041     if (balloon_host) {
2042       int pid = base::GetProcId(balloon_host->web_contents()->
2043                                 GetRenderViewHost()->GetProcess()->GetHandle());
2044       note->SetInteger("pid", pid);
2045     }
2046     list->Append(note);
2047   }
2048   std::vector<const Notification*> queued_notes;
2049   manager->GetQueuedNotificationsForTesting(&queued_notes);
2050   std::vector<const Notification*>::const_iterator queued_iter;
2051   for (queued_iter = queued_notes.begin(); queued_iter != queued_notes.end();
2052        ++queued_iter) {
2053     list->Append(NotificationToJson(*queued_iter));
2054   }
2055   AutomationJSONReply(automation_.get(), reply_message_.release())
2056       .SendSuccess(&return_value);
2057   delete this;
2058 }
2059
2060 NewNotificationBalloonObserver::NewNotificationBalloonObserver(
2061     AutomationProvider* provider,
2062     IPC::Message* reply_message)
2063     : automation_(provider->AsWeakPtr()),
2064       reply_message_(reply_message) {
2065   registrar_.Add(this, chrome::NOTIFICATION_NOTIFY_BALLOON_CONNECTED,
2066                  content::NotificationService::AllSources());
2067 }
2068
2069 NewNotificationBalloonObserver::~NewNotificationBalloonObserver() { }
2070
2071 void NewNotificationBalloonObserver::Observe(
2072     int type,
2073     const content::NotificationSource& source,
2074     const content::NotificationDetails& details) {
2075   if (automation_.get()) {
2076     AutomationJSONReply(automation_.get(), reply_message_.release())
2077         .SendSuccess(NULL);
2078   }
2079   delete this;
2080 }
2081
2082 OnNotificationBalloonCountObserver::OnNotificationBalloonCountObserver(
2083     AutomationProvider* provider,
2084     IPC::Message* reply_message,
2085     int count)
2086     : automation_(provider->AsWeakPtr()),
2087       reply_message_(reply_message),
2088       collection_(BalloonNotificationUIManager::GetInstanceForTesting()->
2089           balloon_collection()),
2090       count_(count) {
2091   registrar_.Add(this, chrome::NOTIFICATION_NOTIFY_BALLOON_CONNECTED,
2092                  content::NotificationService::AllSources());
2093   collection_->set_on_collection_changed_callback(
2094       base::Bind(&OnNotificationBalloonCountObserver::CheckBalloonCount,
2095                  base::Unretained(this)));
2096   CheckBalloonCount();
2097 }
2098
2099 OnNotificationBalloonCountObserver::~OnNotificationBalloonCountObserver() {
2100 }
2101
2102 void OnNotificationBalloonCountObserver::Observe(
2103     int type,
2104     const content::NotificationSource& source,
2105     const content::NotificationDetails& details) {
2106   CheckBalloonCount();
2107 }
2108
2109 void OnNotificationBalloonCountObserver::CheckBalloonCount() {
2110   bool balloon_count_met = AreActiveNotificationProcessesReady() &&
2111       static_cast<int>(collection_->GetActiveBalloons().size()) == count_;
2112
2113   if (balloon_count_met && automation_.get()) {
2114     AutomationJSONReply(automation_.get(), reply_message_.release())
2115         .SendSuccess(NULL);
2116   }
2117
2118   if (balloon_count_met || !automation_.get()) {
2119     collection_->set_on_collection_changed_callback(base::Closure());
2120     delete this;
2121   }
2122 }
2123
2124 RendererProcessClosedObserver::RendererProcessClosedObserver(
2125     AutomationProvider* automation,
2126     IPC::Message* reply_message)
2127     : automation_(automation->AsWeakPtr()),
2128       reply_message_(reply_message) {
2129   registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
2130                  content::NotificationService::AllSources());
2131 }
2132
2133 RendererProcessClosedObserver::~RendererProcessClosedObserver() {}
2134
2135 void RendererProcessClosedObserver::Observe(
2136     int type,
2137     const content::NotificationSource& source,
2138     const content::NotificationDetails& details) {
2139   if (automation_.get()) {
2140     AutomationJSONReply(automation_.get(), reply_message_.release())
2141         .SendSuccess(NULL);
2142   }
2143   delete this;
2144 }
2145
2146 InputEventAckNotificationObserver::InputEventAckNotificationObserver(
2147     AutomationProvider* automation,
2148     IPC::Message* reply_message,
2149     int event_type,
2150     int count)
2151     : automation_(automation->AsWeakPtr()),
2152       reply_message_(reply_message),
2153       event_type_(event_type),
2154       count_(count) {
2155   DCHECK(1 <= count);
2156   registrar_.Add(
2157       this,
2158       content::NOTIFICATION_RENDER_WIDGET_HOST_DID_RECEIVE_INPUT_EVENT_ACK,
2159       content::NotificationService::AllSources());
2160   registrar_.Add(
2161       this,
2162       chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN,
2163       content::NotificationService::AllSources());
2164 }
2165
2166 InputEventAckNotificationObserver::~InputEventAckNotificationObserver() {}
2167
2168 void InputEventAckNotificationObserver::Observe(
2169     int type,
2170     const content::NotificationSource& source,
2171     const content::NotificationDetails& details) {
2172   if (type == chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN) {
2173     AutomationJSONReply(automation_.get(), reply_message_.release())
2174         .SendSuccess(NULL);
2175     delete this;
2176     return;
2177   }
2178
2179   content::Details<int> request_details(details);
2180   // If the event type matches for |count_| times, replies with a JSON message.
2181   if (event_type_ == *request_details.ptr()) {
2182     if (--count_ == 0 && automation_.get()) {
2183       AutomationJSONReply(automation_.get(), reply_message_.release())
2184           .SendSuccess(NULL);
2185       delete this;
2186     }
2187   } else {
2188     LOG(WARNING) << "Ignoring unexpected event type: "
2189                  << *request_details.ptr() << " (expected: " << event_type_
2190                  << ")";
2191   }
2192 }
2193
2194 NewTabObserver::NewTabObserver(AutomationProvider* automation,
2195                                IPC::Message* reply_message,
2196                                bool use_json_interface)
2197     : automation_(automation->AsWeakPtr()),
2198       reply_message_(reply_message),
2199       use_json_interface_(use_json_interface) {
2200   // Use TAB_PARENTED to detect the new tab.
2201   registrar_.Add(this,
2202                  chrome::NOTIFICATION_TAB_PARENTED,
2203                  content::NotificationService::AllSources());
2204 }
2205
2206 void NewTabObserver::Observe(int type,
2207                              const content::NotificationSource& source,
2208                              const content::NotificationDetails& details) {
2209   DCHECK_EQ(chrome::NOTIFICATION_TAB_PARENTED, type);
2210   NavigationController* controller =
2211       &(content::Source<content::WebContents>(source).ptr()->GetController());
2212   if (automation_.get()) {
2213     // TODO(phajdan.jr): Clean up this hack. We write the correct return type
2214     // here, but don't send the message. NavigationNotificationObserver
2215     // will wait properly for the load to finish, and send the message,
2216     // but it will also append its own return value at the end of the reply.
2217     if (!use_json_interface_)
2218       AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_.get(),
2219                                                            true);
2220     new NavigationNotificationObserver(controller,
2221                                        automation_.get(),
2222                                        reply_message_.release(),
2223                                        1,
2224                                        false,
2225                                        use_json_interface_);
2226   }
2227   delete this;
2228 }
2229
2230 NewTabObserver::~NewTabObserver() {
2231 }
2232
2233 WaitForProcessLauncherThreadToGoIdleObserver::
2234 WaitForProcessLauncherThreadToGoIdleObserver(
2235     AutomationProvider* automation, IPC::Message* reply_message)
2236     : automation_(automation->AsWeakPtr()),
2237       reply_message_(reply_message) {
2238   // Balanced in RunOnUIThread.
2239   AddRef();
2240   BrowserThread::PostTask(
2241       BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
2242       base::Bind(
2243           &WaitForProcessLauncherThreadToGoIdleObserver::
2244               RunOnProcessLauncherThread,
2245           this));
2246 }
2247
2248 WaitForProcessLauncherThreadToGoIdleObserver::
2249     ~WaitForProcessLauncherThreadToGoIdleObserver() {
2250 }
2251
2252 void WaitForProcessLauncherThreadToGoIdleObserver::
2253 RunOnProcessLauncherThread() {
2254   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER));
2255   BrowserThread::PostTask(
2256       BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
2257       base::Bind(
2258           &WaitForProcessLauncherThreadToGoIdleObserver::
2259               RunOnProcessLauncherThread2,
2260           this));
2261 }
2262
2263 void WaitForProcessLauncherThreadToGoIdleObserver::
2264 RunOnProcessLauncherThread2() {
2265   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER));
2266   BrowserThread::PostTask(
2267       BrowserThread::UI, FROM_HERE,
2268       base::Bind(&WaitForProcessLauncherThreadToGoIdleObserver::RunOnUIThread,
2269                  this));
2270 }
2271
2272 void WaitForProcessLauncherThreadToGoIdleObserver::RunOnUIThread() {
2273   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2274   if (automation_.get())
2275     automation_->Send(reply_message_.release());
2276   Release();
2277 }
2278
2279 DragTargetDropAckNotificationObserver::DragTargetDropAckNotificationObserver(
2280     AutomationProvider* automation,
2281     IPC::Message* reply_message)
2282     : automation_(automation->AsWeakPtr()),
2283       reply_message_(reply_message) {
2284   registrar_.Add(
2285       this,
2286       content::NOTIFICATION_RENDER_VIEW_HOST_DID_RECEIVE_DRAG_TARGET_DROP_ACK,
2287       content::NotificationService::AllSources());
2288   registrar_.Add(
2289       this,
2290       chrome::NOTIFICATION_APP_MODAL_DIALOG_SHOWN,
2291       content::NotificationService::AllSources());
2292 }
2293
2294 DragTargetDropAckNotificationObserver::
2295     ~DragTargetDropAckNotificationObserver() {}
2296
2297 void DragTargetDropAckNotificationObserver::Observe(
2298     int type,
2299     const content::NotificationSource& source,
2300     const content::NotificationDetails& details) {
2301   if (automation_.get()) {
2302     AutomationJSONReply(automation_.get(), reply_message_.release())
2303         .SendSuccess(NULL);
2304   }
2305   delete this;
2306 }
2307
2308 ProcessInfoObserver::ProcessInfoObserver(
2309     AutomationProvider* automation,
2310     IPC::Message* reply_message)
2311     : automation_(automation->AsWeakPtr()),
2312       reply_message_(reply_message) {}
2313
2314 ProcessInfoObserver::~ProcessInfoObserver() {}
2315
2316 void ProcessInfoObserver::OnDetailsAvailable() {
2317   scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
2318   ListValue* browser_proc_list = new ListValue();
2319   const std::vector<ProcessData>& all_processes = processes();
2320   for (size_t index = 0; index < all_processes.size(); ++index) {
2321     DictionaryValue* browser_data = new DictionaryValue();
2322     browser_data->SetString("name", all_processes[index].name);
2323     browser_data->SetString("process_name", all_processes[index].process_name);
2324
2325     ListValue* proc_list = new ListValue();
2326     for (ProcessMemoryInformationList::const_iterator iterator =
2327              all_processes[index].processes.begin();
2328          iterator != all_processes[index].processes.end(); ++iterator) {
2329       DictionaryValue* proc_data = new DictionaryValue();
2330
2331       proc_data->SetInteger("pid", iterator->pid);
2332
2333       // Working set (resident) memory usage, in KBytes.
2334       DictionaryValue* working_set = new DictionaryValue();
2335       working_set->SetInteger("priv", iterator->working_set.priv);
2336       working_set->SetInteger("shareable", iterator->working_set.shareable);
2337       working_set->SetInteger("shared", iterator->working_set.shared);
2338       proc_data->Set("working_set_mem", working_set);
2339
2340       // Committed (resident + paged) memory usage, in KBytes.
2341       DictionaryValue* committed = new DictionaryValue();
2342       committed->SetInteger("priv", iterator->committed.priv);
2343       committed->SetInteger("mapped", iterator->committed.mapped);
2344       committed->SetInteger("image", iterator->committed.image);
2345       proc_data->Set("committed_mem", committed);
2346
2347       proc_data->SetString("version", iterator->version);
2348       proc_data->SetString("product_name", iterator->product_name);
2349       proc_data->SetInteger("num_processes", iterator->num_processes);
2350       proc_data->SetBoolean("is_diagnostics", iterator->is_diagnostics);
2351
2352       // Process type, if this is a child process of Chrome (e.g., 'plugin').
2353       std::string process_type = "Unknown";
2354       // The following condition avoids a DCHECK in debug builds when the
2355       // process type passed to |GetTypeNameInEnglish| is unknown.
2356       if (iterator->process_type != content::PROCESS_TYPE_UNKNOWN) {
2357         process_type =
2358             content::GetProcessTypeNameInEnglish(iterator->process_type);
2359       }
2360       proc_data->SetString("child_process_type", process_type);
2361
2362       // Renderer type, if this is a renderer process.
2363       std::string renderer_type = "Unknown";
2364       if (iterator->renderer_type !=
2365           ProcessMemoryInformation::RENDERER_UNKNOWN) {
2366         renderer_type = ProcessMemoryInformation::GetRendererTypeNameInEnglish(
2367             iterator->renderer_type);
2368       }
2369       proc_data->SetString("renderer_type", renderer_type);
2370
2371       // Titles associated with this process.
2372       ListValue* titles = new ListValue();
2373       for (size_t title_index = 0; title_index < iterator->titles.size();
2374            ++title_index)
2375         titles->Append(Value::CreateStringValue(iterator->titles[title_index]));
2376       proc_data->Set("titles", titles);
2377
2378       proc_list->Append(proc_data);
2379     }
2380     browser_data->Set("processes", proc_list);
2381
2382     browser_proc_list->Append(browser_data);
2383   }
2384   return_value->Set("browsers", browser_proc_list);
2385
2386   if (automation_.get()) {
2387     AutomationJSONReply(automation_.get(), reply_message_.release())
2388         .SendSuccess(return_value.get());
2389   }
2390 }
2391
2392 V8HeapStatsObserver::V8HeapStatsObserver(
2393     AutomationProvider* automation,
2394     IPC::Message* reply_message,
2395     base::ProcessId renderer_id)
2396     : automation_(automation->AsWeakPtr()),
2397       reply_message_(reply_message),
2398       renderer_id_(renderer_id) {
2399   registrar_.Add(
2400       this,
2401       chrome::NOTIFICATION_RENDERER_V8_HEAP_STATS_COMPUTED,
2402       content::NotificationService::AllSources());
2403 }
2404
2405 V8HeapStatsObserver::~V8HeapStatsObserver() {}
2406
2407 void V8HeapStatsObserver::Observe(
2408     int type,
2409     const content::NotificationSource& source,
2410     const content::NotificationDetails& details) {
2411   DCHECK(type == chrome::NOTIFICATION_RENDERER_V8_HEAP_STATS_COMPUTED);
2412
2413   base::ProcessId updated_renderer_id =
2414       *(content::Source<base::ProcessId>(source).ptr());
2415   // Only return information for the renderer ID we're interested in.
2416   if (renderer_id_ != updated_renderer_id)
2417     return;
2418
2419   ChromeRenderMessageFilter::V8HeapStatsDetails* v8_heap_details =
2420       content::Details<ChromeRenderMessageFilter::V8HeapStatsDetails>(details)
2421           .ptr();
2422   scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
2423   return_value->SetInteger("renderer_id", updated_renderer_id);
2424   return_value->SetInteger("v8_memory_allocated",
2425                            v8_heap_details->v8_memory_allocated());
2426   return_value->SetInteger("v8_memory_used",
2427                            v8_heap_details->v8_memory_used());
2428
2429   if (automation_.get()) {
2430     AutomationJSONReply(automation_.get(), reply_message_.release())
2431         .SendSuccess(return_value.get());
2432   }
2433   delete this;
2434 }
2435
2436 FPSObserver::FPSObserver(
2437     AutomationProvider* automation,
2438     IPC::Message* reply_message,
2439     base::ProcessId renderer_id,
2440     int routing_id)
2441     : automation_(automation->AsWeakPtr()),
2442       reply_message_(reply_message),
2443       renderer_id_(renderer_id),
2444       routing_id_(routing_id) {
2445   registrar_.Add(
2446       this,
2447       chrome::NOTIFICATION_RENDERER_FPS_COMPUTED,
2448       content::NotificationService::AllSources());
2449 }
2450
2451 FPSObserver::~FPSObserver() {}
2452
2453 void FPSObserver::Observe(
2454     int type,
2455     const content::NotificationSource& source,
2456     const content::NotificationDetails& details) {
2457   DCHECK(type == chrome::NOTIFICATION_RENDERER_FPS_COMPUTED);
2458
2459   base::ProcessId updated_renderer_id =
2460       *(content::Source<base::ProcessId>(source).ptr());
2461   // Only return information for the renderer ID we're interested in.
2462   if (renderer_id_ != updated_renderer_id)
2463     return;
2464
2465   ChromeRenderMessageFilter::FPSDetails* fps_details =
2466       content::Details<ChromeRenderMessageFilter::FPSDetails>(details).ptr();
2467   // Only return information for the routing id of the host render view we're
2468   // interested in.
2469   if (routing_id_ != fps_details->routing_id())
2470     return;
2471
2472   scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
2473   return_value->SetInteger("renderer_id", updated_renderer_id);
2474   return_value->SetInteger("routing_id", fps_details->routing_id());
2475   return_value->SetDouble("fps", fps_details->fps());
2476   if (automation_.get()) {
2477     AutomationJSONReply(automation_.get(), reply_message_.release())
2478         .SendSuccess(return_value.get());
2479   }
2480   delete this;
2481 }
2482
2483 BrowserOpenedWithNewProfileNotificationObserver::
2484     BrowserOpenedWithNewProfileNotificationObserver(
2485         AutomationProvider* automation,
2486         IPC::Message* reply_message)
2487         : automation_(automation->AsWeakPtr()),
2488           reply_message_(reply_message),
2489           new_window_id_(extension_misc::kUnknownWindowId) {
2490   registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CREATED,
2491                  content::NotificationService::AllBrowserContextsAndSources());
2492   registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED,
2493                  content::NotificationService::AllBrowserContextsAndSources());
2494   registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
2495                  content::NotificationService::AllBrowserContextsAndSources());
2496 }
2497
2498 BrowserOpenedWithNewProfileNotificationObserver::
2499     ~BrowserOpenedWithNewProfileNotificationObserver() {
2500 }
2501
2502 void BrowserOpenedWithNewProfileNotificationObserver::Observe(
2503     int type,
2504     const content::NotificationSource& source,
2505     const content::NotificationDetails& details) {
2506   if (!automation_.get()) {
2507     delete this;
2508     return;
2509   }
2510
2511   if (type == chrome::NOTIFICATION_PROFILE_CREATED) {
2512     // As part of multi-profile creation, a new browser window will
2513     // automatically be opened.
2514     Profile* profile = content::Source<Profile>(source).ptr();
2515     if (!profile) {
2516       AutomationJSONReply(automation_.get(), reply_message_.release())
2517           .SendError("Profile could not be created.");
2518       return;
2519     }
2520   } else if (type == chrome::NOTIFICATION_BROWSER_OPENED) {
2521     // Store the new browser ID and continue waiting for a new tab within it
2522     // to stop loading.
2523     new_window_id_ = ExtensionTabUtil::GetWindowId(
2524         content::Source<Browser>(source).ptr());
2525   } else {
2526     DCHECK_EQ(content::NOTIFICATION_LOAD_STOP, type);
2527     // Only send the result if the loaded tab is in the new window.
2528     NavigationController* controller =
2529         content::Source<NavigationController>(source).ptr();
2530     SessionTabHelper* session_tab_helper =
2531         SessionTabHelper::FromWebContents(controller->GetWebContents());
2532     int window_id = session_tab_helper ? session_tab_helper->window_id().id()
2533                                        : -1;
2534     if (window_id == new_window_id_) {
2535       if (automation_.get()) {
2536         AutomationJSONReply(automation_.get(), reply_message_.release())
2537             .SendSuccess(NULL);
2538       }
2539       delete this;
2540     }
2541   }
2542 }
2543
2544 ExtensionPopupObserver::ExtensionPopupObserver(
2545     AutomationProvider* automation,
2546     IPC::Message* reply_message,
2547     const std::string& extension_id)
2548     : automation_(automation->AsWeakPtr()),
2549       reply_message_(reply_message),
2550       extension_id_(extension_id) {
2551   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING,
2552                  content::NotificationService::AllSources());
2553 }
2554
2555 ExtensionPopupObserver::~ExtensionPopupObserver() {
2556 }
2557
2558 void ExtensionPopupObserver::Observe(
2559     int type,
2560     const content::NotificationSource& source,
2561     const content::NotificationDetails& details) {
2562   if (!automation_.get()) {
2563     delete this;
2564     return;
2565   }
2566
2567   extensions::ExtensionHost* host =
2568       content::Details<extensions::ExtensionHost>(details).ptr();
2569   if (host->extension_id() == extension_id_ &&
2570       host->extension_host_type() == extensions::VIEW_TYPE_EXTENSION_POPUP) {
2571     AutomationJSONReply(automation_.get(), reply_message_.release())
2572         .SendSuccess(NULL);
2573     delete this;
2574   }
2575 }
2576
2577 #if defined(OS_LINUX)
2578 WindowMaximizedObserver::WindowMaximizedObserver(
2579     AutomationProvider* automation,
2580     IPC::Message* reply_message)
2581     : automation_(automation->AsWeakPtr()),
2582       reply_message_(reply_message) {
2583   registrar_.Add(this, chrome::NOTIFICATION_BROWSER_WINDOW_MAXIMIZED,
2584                  content::NotificationService::AllSources());
2585 }
2586
2587 WindowMaximizedObserver::~WindowMaximizedObserver() {}
2588
2589 void WindowMaximizedObserver::Observe(
2590     int type,
2591     const content::NotificationSource& source,
2592     const content::NotificationDetails& details) {
2593   DCHECK_EQ(chrome::NOTIFICATION_BROWSER_WINDOW_MAXIMIZED, type);
2594
2595   if (automation_.get()) {
2596     AutomationJSONReply(automation_.get(), reply_message_.release())
2597         .SendSuccess(NULL);
2598   }
2599   delete this;
2600 }
2601 #endif  // defined(OS_LINUX)
2602
2603 BrowserOpenedWithExistingProfileNotificationObserver::
2604     BrowserOpenedWithExistingProfileNotificationObserver(
2605         AutomationProvider* automation,
2606         IPC::Message* reply_message,
2607         int num_loads)
2608         : automation_(automation->AsWeakPtr()),
2609           reply_message_(reply_message),
2610           new_window_id_(extension_misc::kUnknownWindowId),
2611           num_loads_(num_loads) {
2612   registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED,
2613                  content::NotificationService::AllBrowserContextsAndSources());
2614   registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
2615                  content::NotificationService::AllBrowserContextsAndSources());
2616 }
2617
2618 BrowserOpenedWithExistingProfileNotificationObserver::
2619     ~BrowserOpenedWithExistingProfileNotificationObserver() {
2620 }
2621
2622 void BrowserOpenedWithExistingProfileNotificationObserver::Observe(
2623     int type,
2624     const content::NotificationSource& source,
2625     const content::NotificationDetails& details) {
2626   if (!automation_.get()) {
2627     delete this;
2628     return;
2629   }
2630
2631   if (type == chrome::NOTIFICATION_BROWSER_OPENED) {
2632     // Store the new browser ID and continue waiting for NOTIFICATION_LOAD_STOP.
2633     new_window_id_ = ExtensionTabUtil::GetWindowId(
2634         content::Source<Browser>(source).ptr());
2635   } else if (type == content::NOTIFICATION_LOAD_STOP) {
2636     // Only consider if the loaded tab is in the new window.
2637     NavigationController* controller =
2638         content::Source<NavigationController>(source).ptr();
2639     SessionTabHelper* session_tab_helper =
2640         SessionTabHelper::FromWebContents(controller->GetWebContents());
2641     int window_id = session_tab_helper ? session_tab_helper->window_id().id()
2642                                        : -1;
2643     if (window_id == new_window_id_ && --num_loads_ == 0) {
2644       if (automation_.get()) {
2645         AutomationJSONReply(automation_.get(), reply_message_.release())
2646             .SendSuccess(NULL);
2647       }
2648       delete this;
2649     }
2650   } else {
2651     NOTREACHED();
2652   }
2653 }