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