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