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/testing_automation_provider.h" 6 7 #include "ash/new_window_delegate.h" 8 #include "ash/shell.h" 9 #include "ash/system/tray/system_tray_delegate.h" 10 #include "base/command_line.h" 11 #include "base/i18n/time_formatting.h" 12 #include "base/prefs/pref_service.h" 13 #include "base/strings/stringprintf.h" 14 #include "base/strings/utf_string_conversions.h" 15 #include "base/time/time.h" 16 #include "chrome/browser/automation/automation_provider_json.h" 17 #include "chrome/browser/automation/automation_provider_observers.h" 18 #include "chrome/browser/automation/automation_util.h" 19 #include "chrome/browser/browser_process.h" 20 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h" 21 #include "chrome/browser/chromeos/accessibility/accessibility_util.h" 22 #include "chrome/browser/chromeos/login/default_user_images.h" 23 #include "chrome/browser/chromeos/login/enrollment/enrollment_screen.h" 24 #include "chrome/browser/chromeos/login/existing_user_controller.h" 25 #include "chrome/browser/chromeos/login/login_display.h" 26 #include "chrome/browser/chromeos/login/login_display_host_impl.h" 27 #include "chrome/browser/chromeos/login/screen_locker.h" 28 #include "chrome/browser/chromeos/login/screens/eula_screen.h" 29 #include "chrome/browser/chromeos/login/screens/network_screen.h" 30 #include "chrome/browser/chromeos/login/screens/update_screen.h" 31 #include "chrome/browser/chromeos/login/screens/user_image_screen.h" 32 #include "chrome/browser/chromeos/login/startup_utils.h" 33 #include "chrome/browser/chromeos/login/webui_login_display.h" 34 #include "chrome/browser/chromeos/login/wizard_controller.h" 35 #include "chrome/browser/chromeos/net/proxy_config_handler.h" 36 #include "chrome/browser/chromeos/settings/cros_settings.h" 37 #include "chrome/browser/prefs/proxy_config_dictionary.h" 38 #include "chrome/browser/ui/browser.h" 39 #include "chrome/browser/ui/browser_window.h" 40 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h" 41 #include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h" 42 #include "chrome/common/pref_names.h" 43 #include "chromeos/audio/cras_audio_handler.h" 44 #include "chromeos/dbus/dbus_thread_manager.h" 45 #include "chromeos/dbus/session_manager_client.h" 46 #include "chromeos/dbus/update_engine_client.h" 47 #include "chromeos/settings/cros_settings_names.h" 48 #include "chromeos/settings/timezone_settings.h" 49 #include "content/public/browser/web_contents.h" 50 #include "policy/policy_constants.h" 51 #include "ui/views/widget/widget.h" 52 53 using chromeos::DBusThreadManager; 54 using chromeos::ExistingUserController; 55 using chromeos::UpdateEngineClient; 56 using chromeos::User; 57 using chromeos::UserManager; 58 using chromeos::WizardController; 59 60 namespace { 61 62 void UpdateCheckCallback(AutomationJSONReply* reply, 63 UpdateEngineClient::UpdateCheckResult result) { 64 if (result == UpdateEngineClient::UPDATE_RESULT_SUCCESS) 65 reply->SendSuccess(NULL); 66 else 67 reply->SendError("update check failed"); 68 delete reply; 69 } 70 71 } // namespace 72 73 #if defined(OS_CHROMEOS) 74 void TestingAutomationProvider::PowerChanged( 75 const power_manager::PowerSupplyProperties& proto) { 76 power_supply_properties_ = proto; 77 } 78 #endif 79 80 void TestingAutomationProvider::AcceptOOBENetworkScreen( 81 DictionaryValue* args, 82 IPC::Message* reply_message) { 83 WizardController* wizard_controller = WizardController::default_controller(); 84 if (!wizard_controller || wizard_controller->current_screen()->GetName() != 85 WizardController::kNetworkScreenName) { 86 AutomationJSONReply(this, reply_message).SendError( 87 "Network screen not active."); 88 return; 89 } 90 // Observer will delete itself. 91 new WizardControllerObserver(wizard_controller, this, reply_message); 92 wizard_controller->GetNetworkScreen()->OnContinuePressed(); 93 } 94 95 void TestingAutomationProvider::AcceptOOBEEula(DictionaryValue* args, 96 IPC::Message* reply_message) { 97 bool accepted; 98 bool usage_stats_reporting; 99 if (!args->GetBoolean("accepted", &accepted) || 100 !args->GetBoolean("usage_stats_reporting", &usage_stats_reporting)) { 101 AutomationJSONReply(this, reply_message).SendError( 102 "Invalid or missing args."); 103 return; 104 } 105 106 WizardController* wizard_controller = WizardController::default_controller(); 107 if (!wizard_controller || wizard_controller->current_screen()->GetName() != 108 WizardController::kEulaScreenName) { 109 AutomationJSONReply(this, reply_message).SendError( 110 "EULA screen not active."); 111 return; 112 } 113 // Observer will delete itself. 114 new WizardControllerObserver(wizard_controller, this, reply_message); 115 wizard_controller->GetEulaScreen()->OnExit(accepted, usage_stats_reporting); 116 } 117 118 void TestingAutomationProvider::CancelOOBEUpdate(DictionaryValue* args, 119 IPC::Message* reply_message) { 120 if (chromeos::StartupUtils::IsOobeCompleted()) { 121 // Update already finished. 122 scoped_ptr<DictionaryValue> return_value(new DictionaryValue); 123 return_value->SetString("next_screen", 124 WizardController::kLoginScreenName); 125 AutomationJSONReply(this, reply_message).SendSuccess(return_value.get()); 126 return; 127 } 128 WizardController* wizard_controller = WizardController::default_controller(); 129 if (!wizard_controller || wizard_controller->current_screen()->GetName() != 130 WizardController::kUpdateScreenName) { 131 AutomationJSONReply(this, reply_message).SendError( 132 "Update screen not active."); 133 return; 134 } 135 // Observer will delete itself. 136 new WizardControllerObserver(wizard_controller, this, reply_message); 137 wizard_controller->GetUpdateScreen()->CancelUpdate(); 138 } 139 140 void TestingAutomationProvider::GetLoginInfo(DictionaryValue* args, 141 IPC::Message* reply_message) { 142 AutomationJSONReply reply(this, reply_message); 143 scoped_ptr<DictionaryValue> return_value(new DictionaryValue); 144 145 const UserManager* user_manager = UserManager::Get(); 146 if (!user_manager) 147 reply.SendError("No user manager!"); 148 const chromeos::ScreenLocker* screen_locker = 149 chromeos::ScreenLocker::default_screen_locker(); 150 151 return_value->SetString("login_ui_type", "webui"); 152 return_value->SetBoolean("is_owner", user_manager->IsCurrentUserOwner()); 153 return_value->SetBoolean("is_logged_in", user_manager->IsUserLoggedIn()); 154 return_value->SetBoolean("is_screen_locked", screen_locker); 155 if (user_manager->IsUserLoggedIn()) { 156 const User* user = user_manager->GetLoggedInUser(); 157 return_value->SetBoolean("is_guest", user_manager->IsLoggedInAsGuest()); 158 return_value->SetString("email", user->email()); 159 return_value->SetString("display_email", user->display_email()); 160 switch (user->image_index()) { 161 case User::kExternalImageIndex: 162 return_value->SetString("user_image", "file"); 163 break; 164 165 case User::kProfileImageIndex: 166 return_value->SetString("user_image", "profile"); 167 break; 168 169 default: 170 return_value->SetInteger("user_image", user->image_index()); 171 break; 172 } 173 } 174 175 reply.SendSuccess(return_value.get()); 176 } 177 178 // See the note under LoginAsGuest(). CreateAccount() causes a login as guest. 179 void TestingAutomationProvider::ShowCreateAccountUI( 180 DictionaryValue* args, IPC::Message* reply_message) { 181 ExistingUserController* controller = 182 ExistingUserController::current_controller(); 183 // Return immediately, since we're going to die before the login is finished. 184 AutomationJSONReply(this, reply_message).SendSuccess(NULL); 185 controller->CreateAccount(); 186 } 187 188 // Logging in as guest will cause session_manager to restart Chrome with new 189 // flags. If you used EnableChromeTesting, you will have to call it again. 190 void TestingAutomationProvider::LoginAsGuest(DictionaryValue* args, 191 IPC::Message* reply_message) { 192 ExistingUserController* controller = 193 ExistingUserController::current_controller(); 194 // Return immediately, since we're going to die before the login is finished. 195 AutomationJSONReply(this, reply_message).SendSuccess(NULL); 196 controller->LoginAsGuest(); 197 } 198 199 void TestingAutomationProvider::SubmitLoginForm(DictionaryValue* args, 200 IPC::Message* reply_message) { 201 AutomationJSONReply reply(this, reply_message); 202 203 std::string username, password; 204 if (!args->GetString("username", &username) || 205 !args->GetString("password", &password)) { 206 reply.SendError("Invalid or missing args."); 207 return; 208 } 209 210 chromeos::ExistingUserController* controller = 211 chromeos::ExistingUserController::current_controller(); 212 if (!controller) { 213 reply.SendError("Unable to access ExistingUserController"); 214 return; 215 } 216 217 // WebUI login. 218 chromeos::WebUILoginDisplay* webui_login_display = 219 static_cast<chromeos::WebUILoginDisplay*>(controller->login_display()); 220 VLOG(2) << "TestingAutomationProvider::SubmitLoginForm " 221 << "ShowSigninScreenForCreds(" << username << ", " << password << ")"; 222 223 webui_login_display->ShowSigninScreenForCreds(username, password); 224 reply.SendSuccess(NULL); 225 } 226 227 void TestingAutomationProvider::AddLoginEventObserver( 228 DictionaryValue* args, IPC::Message* reply_message) { 229 ExistingUserController* controller = 230 ExistingUserController::current_controller(); 231 AutomationJSONReply reply(this, reply_message); 232 if (!controller) { 233 // This may happen due to SkipToLogin not being called. 234 reply.SendError("Unable to access ExistingUserController"); 235 return; 236 } 237 238 if (!automation_event_queue_.get()) 239 automation_event_queue_.reset(new AutomationEventQueue); 240 241 int observer_id = automation_event_queue_->AddObserver( 242 new LoginEventObserver(automation_event_queue_.get(), this)); 243 244 // Return the observer's id. 245 DictionaryValue return_value; 246 return_value.SetInteger("observer_id", observer_id); 247 reply.SendSuccess(&return_value); 248 } 249 250 void TestingAutomationProvider::SignOut(DictionaryValue* args, 251 IPC::Message* reply_message) { 252 ash::Shell::GetInstance()->system_tray_delegate()->SignOut(); 253 // Sign out has the side effect of restarting the session_manager 254 // and chrome, thereby severing the automation channel, so it's 255 // not really necessary to send a reply back. The next line is 256 // for consistency with other methods. 257 AutomationJSONReply(this, reply_message).SendSuccess(NULL); 258 } 259 260 void TestingAutomationProvider::PickUserImage(DictionaryValue* args, 261 IPC::Message* reply_message) { 262 std::string image_type; 263 int image_number = -1; 264 if (!args->GetString("image", &image_type) 265 && !args->GetInteger("image", &image_number)) { 266 AutomationJSONReply(this, reply_message).SendError( 267 "Invalid or missing args."); 268 return; 269 } 270 WizardController* wizard_controller = WizardController::default_controller(); 271 if (!wizard_controller || wizard_controller->current_screen()->GetName() != 272 WizardController::kUserImageScreenName) { 273 AutomationJSONReply(this, reply_message).SendError( 274 "User image screen not active."); 275 return; 276 } 277 chromeos::UserImageScreen* image_screen = 278 wizard_controller->GetUserImageScreen(); 279 // Observer will delete itself unless error is returned. 280 WizardControllerObserver* observer = 281 new WizardControllerObserver(wizard_controller, this, reply_message); 282 if (image_type == "profile") { 283 image_screen->OnImageSelected("", image_type, true); 284 image_screen->OnImageAccepted(); 285 } else if (image_type.empty() && image_number >= 0 && 286 image_number < chromeos::kDefaultImagesCount) { 287 image_screen->OnImageSelected( 288 chromeos::GetDefaultImageUrl(image_number), image_type, true); 289 image_screen->OnImageAccepted(); 290 } else { 291 AutomationJSONReply(this, reply_message).SendError( 292 "Invalid or missing args."); 293 delete observer; 294 return; 295 } 296 } 297 298 void TestingAutomationProvider::SkipToLogin(DictionaryValue* args, 299 IPC::Message* reply_message) { 300 bool skip_post_login_screens; 301 // The argument name is a legacy. If set to |true|, this argument causes any 302 // screens that may otherwise be shown after login (registration, Terms of 303 // Service, user image selection) to be skipped. 304 if (!args->GetBoolean("skip_image_selection", &skip_post_login_screens)) { 305 AutomationJSONReply reply(this, reply_message); 306 reply.SendError("Invalid or missing args."); 307 return; 308 } 309 if (skip_post_login_screens) 310 WizardController::SkipPostLoginScreensForTesting(); 311 312 WizardController* wizard_controller = WizardController::default_controller(); 313 if (!wizard_controller) { 314 AutomationJSONReply reply(this, reply_message); 315 if (ExistingUserController::current_controller()) { 316 // Already at login screen. 317 scoped_ptr<DictionaryValue> return_value(new DictionaryValue); 318 return_value->SetString("next_screen", 319 WizardController::kLoginScreenName); 320 reply.SendSuccess(return_value.get()); 321 } else { 322 reply.SendError("OOBE not active."); 323 } 324 return; 325 } 326 327 // Observer will delete itself. 328 WizardControllerObserver* observer = 329 new WizardControllerObserver(wizard_controller, this, reply_message); 330 observer->set_screen_to_wait_for(WizardController::kLoginScreenName); 331 wizard_controller->SkipToLoginForTesting(chromeos::LoginScreenContext()); 332 } 333 334 void TestingAutomationProvider::GetOOBEScreenInfo(DictionaryValue* args, 335 IPC::Message* reply_message) { 336 static const char kScreenNameKey[] = "screen_name"; 337 AutomationJSONReply reply(this, reply_message); 338 scoped_ptr<DictionaryValue> return_value(new DictionaryValue); 339 340 WizardController* wizard_controller = WizardController::default_controller(); 341 if (wizard_controller) { 342 if (wizard_controller->login_screen_started()) { 343 return_value->SetString(kScreenNameKey, 344 WizardController::kLoginScreenName); 345 } else { 346 return_value->SetString(kScreenNameKey, 347 wizard_controller->current_screen()->GetName()); 348 } 349 } else if (ExistingUserController::current_controller()) { 350 return_value->SetString(kScreenNameKey, WizardController::kLoginScreenName); 351 } else { 352 // Already logged in. 353 reply.SendSuccess(NULL); 354 return; 355 } 356 reply.SendSuccess(return_value.get()); 357 } 358 359 void TestingAutomationProvider::LockScreen(DictionaryValue* args, 360 IPC::Message* reply_message) { 361 new ScreenLockUnlockObserver(this, reply_message, true); 362 DBusThreadManager::Get()->GetSessionManagerClient()->RequestLockScreen(); 363 } 364 365 void TestingAutomationProvider::UnlockScreen(DictionaryValue* args, 366 IPC::Message* reply_message) { 367 std::string password; 368 if (!args->GetString("password", &password)) { 369 AutomationJSONReply(this, reply_message).SendError( 370 "Invalid or missing args."); 371 return; 372 } 373 374 chromeos::ScreenLocker* screen_locker = 375 chromeos::ScreenLocker::default_screen_locker(); 376 if (!screen_locker) { 377 AutomationJSONReply(this, reply_message).SendError( 378 "No default screen locker. Are you sure the screen is locked?"); 379 return; 380 } 381 382 new ScreenUnlockObserver(this, reply_message); 383 screen_locker->AuthenticateByPassword(password); 384 } 385 386 // Signing out could have undesirable side effects: session_manager is 387 // killed, so its children, including chrome and the window manager, will 388 // also be killed. Anything owned by chronos will probably be killed. 389 void TestingAutomationProvider::SignoutInScreenLocker( 390 DictionaryValue* args, IPC::Message* reply_message) { 391 AutomationJSONReply reply(this, reply_message); 392 chromeos::ScreenLocker* screen_locker = 393 chromeos::ScreenLocker::default_screen_locker(); 394 if (!screen_locker) { 395 reply.SendError( 396 "No default screen locker. Are you sure the screen is locked?"); 397 return; 398 } 399 400 // Send success before stopping session because if we're a child of 401 // session manager then we'll die when the session is stopped. 402 reply.SendSuccess(NULL); 403 screen_locker->Signout(); 404 } 405 406 void TestingAutomationProvider::GetBatteryInfo(DictionaryValue* args, 407 IPC::Message* reply_message) { 408 scoped_ptr<DictionaryValue> return_value(new DictionaryValue); 409 410 const bool battery_is_present = power_supply_properties_.battery_state() != 411 power_manager::PowerSupplyProperties_BatteryState_NOT_PRESENT; 412 const bool line_power_on = power_supply_properties_.external_power() != 413 power_manager::PowerSupplyProperties_ExternalPower_DISCONNECTED; 414 415 return_value->SetBoolean("battery_is_present", battery_is_present); 416 return_value->SetBoolean("line_power_on", line_power_on); 417 418 if (battery_is_present) { 419 const bool battery_is_full = power_supply_properties_.battery_state() == 420 power_manager::PowerSupplyProperties_BatteryState_FULL; 421 return_value->SetBoolean("battery_fully_charged", battery_is_full); 422 return_value->SetDouble("battery_percentage", 423 power_supply_properties_.battery_percent()); 424 if (line_power_on) { 425 int64 time = power_supply_properties_.battery_time_to_full_sec(); 426 if (time > 0 || battery_is_full) 427 return_value->SetInteger("battery_seconds_to_full", time); 428 } else { 429 int64 time = power_supply_properties_.battery_time_to_empty_sec(); 430 if (time > 0) 431 return_value->SetInteger("battery_seconds_to_empty", time); 432 } 433 } 434 435 AutomationJSONReply(this, reply_message).SendSuccess(return_value.get()); 436 } 437 438 void TestingAutomationProvider::ExecuteJavascriptInOOBEWebUI( 439 DictionaryValue* args, IPC::Message* reply_message) { 440 std::string javascript, frame_xpath; 441 if (!args->GetString("javascript", &javascript)) { 442 AutomationJSONReply(this, reply_message) 443 .SendError("'javascript' missing or invalid"); 444 return; 445 } 446 if (!args->GetString("frame_xpath", &frame_xpath)) { 447 AutomationJSONReply(this, reply_message) 448 .SendError("'frame_xpath' missing or invalid"); 449 return; 450 } 451 const UserManager* user_manager = UserManager::Get(); 452 if (!user_manager) { 453 AutomationJSONReply(this, reply_message).SendError( 454 "No user manager!"); 455 return; 456 } 457 if (user_manager->IsUserLoggedIn()) { 458 AutomationJSONReply(this, reply_message).SendError( 459 "User is already logged in."); 460 return; 461 } 462 ExistingUserController* controller = 463 ExistingUserController::current_controller(); 464 if (!controller) { 465 AutomationJSONReply(this, reply_message).SendError( 466 "Unable to access ExistingUserController"); 467 return; 468 } 469 chromeos::LoginDisplayHostImpl* webui_host = 470 static_cast<chromeos::LoginDisplayHostImpl*>( 471 controller->login_display_host()); 472 content::WebContents* web_contents = 473 webui_host->GetOobeUI()->web_ui()->GetWebContents(); 474 475 new DomOperationMessageSender(this, reply_message, true); 476 ExecuteJavascriptInRenderViewFrame(ASCIIToUTF16(frame_xpath), 477 ASCIIToUTF16(javascript), 478 reply_message, 479 web_contents->GetRenderViewHost()); 480 } 481 482 void TestingAutomationProvider::EnableSpokenFeedback( 483 DictionaryValue* args, IPC::Message* reply_message) { 484 AutomationJSONReply reply(this, reply_message); 485 scoped_ptr<DictionaryValue> return_value(new DictionaryValue); 486 bool enabled; 487 if (!args->GetBoolean("enabled", &enabled)) { 488 reply.SendError("Invalid or missing args."); 489 return; 490 } 491 chromeos::AccessibilityManager::Get()->EnableSpokenFeedback( 492 enabled, ash::A11Y_NOTIFICATION_NONE); 493 494 reply.SendSuccess(return_value.get()); 495 } 496 497 void TestingAutomationProvider::IsSpokenFeedbackEnabled( 498 DictionaryValue* args, IPC::Message* reply_message) { 499 AutomationJSONReply reply(this, reply_message); 500 scoped_ptr<DictionaryValue> return_value(new DictionaryValue); 501 return_value->SetBoolean( 502 "spoken_feedback", 503 chromeos::AccessibilityManager::Get()->IsSpokenFeedbackEnabled()); 504 reply.SendSuccess(return_value.get()); 505 } 506 507 void TestingAutomationProvider::GetTimeInfo(Browser* browser, 508 DictionaryValue* args, 509 IPC::Message* reply_message) { 510 scoped_ptr<DictionaryValue> return_value(new DictionaryValue); 511 base::Time time(base::Time::Now()); 512 bool use_24hour_clock = browser && browser->profile()->GetPrefs()->GetBoolean( 513 prefs::kUse24HourClock); 514 base::HourClockType hour_clock_type = 515 use_24hour_clock ? base::k24HourClock : base::k12HourClock; 516 base::string16 display_time = base::TimeFormatTimeOfDayWithHourClockType( 517 time, hour_clock_type, base::kDropAmPm); 518 base::string16 timezone = 519 chromeos::system::TimezoneSettings::GetInstance()->GetCurrentTimezoneID(); 520 return_value->SetString("display_time", display_time); 521 return_value->SetString("display_date", base::TimeFormatFriendlyDate(time)); 522 return_value->SetString("timezone", timezone); 523 AutomationJSONReply(this, reply_message).SendSuccess(return_value.get()); 524 } 525 526 void TestingAutomationProvider::GetTimeInfo(DictionaryValue* args, 527 IPC::Message* reply_message) { 528 GetTimeInfo(NULL, args, reply_message); 529 } 530 531 void TestingAutomationProvider::SetTimezone(DictionaryValue* args, 532 IPC::Message* reply_message) { 533 AutomationJSONReply reply(this, reply_message); 534 std::string timezone_id; 535 if (!args->GetString("timezone", &timezone_id)) { 536 reply.SendError("Invalid or missing args."); 537 return; 538 } 539 chromeos::CrosSettings* settings = chromeos::CrosSettings::Get(); 540 settings->SetString(chromeos::kSystemTimezone, timezone_id); 541 reply.SendSuccess(NULL); 542 } 543 544 void TestingAutomationProvider::UpdateCheck( 545 DictionaryValue* args, 546 IPC::Message* reply_message) { 547 AutomationJSONReply* reply = new AutomationJSONReply(this, reply_message); 548 DBusThreadManager::Get()->GetUpdateEngineClient() 549 ->RequestUpdateCheck(base::Bind(UpdateCheckCallback, reply)); 550 } 551 552 void TestingAutomationProvider::GetVolumeInfo(DictionaryValue* args, 553 IPC::Message* reply_message) { 554 AutomationJSONReply reply(this, reply_message); 555 scoped_ptr<DictionaryValue> return_value(new DictionaryValue); 556 chromeos::CrasAudioHandler* audio_handler = chromeos::CrasAudioHandler::Get(); 557 if (!audio_handler) { 558 reply.SendError("CrasAudioHandler not initialized."); 559 return; 560 } 561 return_value->SetDouble("volume", audio_handler->GetOutputVolumePercent()); 562 return_value->SetBoolean("is_mute", audio_handler->IsOutputMuted()); 563 reply.SendSuccess(return_value.get()); 564 } 565 566 void TestingAutomationProvider::SetVolume(DictionaryValue* args, 567 IPC::Message* reply_message) { 568 AutomationJSONReply reply(this, reply_message); 569 double volume_percent; 570 if (!args->GetDouble("volume", &volume_percent)) { 571 reply.SendError("Invalid or missing args."); 572 return; 573 } 574 chromeos::CrasAudioHandler* audio_handler = chromeos::CrasAudioHandler::Get(); 575 if (!audio_handler) { 576 reply.SendError("CrasAudioHandler not initialized."); 577 return; 578 } 579 audio_handler->SetOutputVolumePercent(volume_percent); 580 reply.SendSuccess(NULL); 581 } 582 583 void TestingAutomationProvider::SetMute(DictionaryValue* args, 584 IPC::Message* reply_message) { 585 AutomationJSONReply reply(this, reply_message); 586 bool mute; 587 if (!args->GetBoolean("mute", &mute)) { 588 reply.SendError("Invalid or missing args."); 589 return; 590 } 591 chromeos::CrasAudioHandler* audio_handler = chromeos::CrasAudioHandler::Get(); 592 if (!audio_handler) { 593 reply.SendError("CrasAudioHandler not initialized."); 594 return; 595 } 596 audio_handler->SetOutputMute(mute); 597 reply.SendSuccess(NULL); 598 } 599 600 void TestingAutomationProvider::OpenCrosh(DictionaryValue* args, 601 IPC::Message* reply_message) { 602 new NavigationNotificationObserver( 603 NULL, this, reply_message, 1, false, true); 604 ash::Shell::GetInstance()->new_window_delegate()->OpenCrosh(); 605 } 606 607 void TestingAutomationProvider::AddChromeosObservers() { 608 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()-> 609 AddObserver(this); 610 } 611 612 void TestingAutomationProvider::RemoveChromeosObservers() { 613 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()-> 614 RemoveObserver(this); 615 } 616