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