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/ui/webui/help/help_handler.h" 6 7 #include <string> 8 9 #include "base/basictypes.h" 10 #include "base/bind.h" 11 #include "base/bind_helpers.h" 12 #include "base/command_line.h" 13 #include "base/strings/string16.h" 14 #include "base/strings/string_number_conversions.h" 15 #include "base/strings/utf_string_conversions.h" 16 #include "base/values.h" 17 #include "chrome/browser/browser_process.h" 18 #include "chrome/browser/chrome_notification_types.h" 19 #include "chrome/browser/google/google_util.h" 20 #include "chrome/browser/policy/browser_policy_connector.h" 21 #include "chrome/browser/ui/browser.h" 22 #include "chrome/browser/ui/browser_commands.h" 23 #include "chrome/browser/ui/browser_finder.h" 24 #include "chrome/browser/ui/chrome_pages.h" 25 #include "chrome/common/chrome_version_info.h" 26 #include "chrome/common/pref_names.h" 27 #include "chrome/common/url_constants.h" 28 #include "content/public/browser/browser_thread.h" 29 #include "content/public/browser/notification_service.h" 30 #include "content/public/browser/web_ui.h" 31 #include "content/public/browser/web_ui_data_source.h" 32 #include "content/public/common/content_client.h" 33 #include "grit/chromium_strings.h" 34 #include "grit/generated_resources.h" 35 #include "grit/google_chrome_strings.h" 36 #include "ui/base/l10n/l10n_util.h" 37 #include "ui/base/resource/resource_bundle.h" 38 #include "v8/include/v8.h" 39 #include "webkit/common/user_agent/user_agent_util.h" 40 41 #if defined(OS_CHROMEOS) 42 #include "base/files/file_util_proxy.h" 43 #include "base/i18n/time_formatting.h" 44 #include "base/prefs/pref_service.h" 45 #include "base/sys_info.h" 46 #include "chrome/browser/chromeos/login/user_manager.h" 47 #include "chrome/browser/chromeos/settings/cros_settings.h" 48 #include "chrome/browser/profiles/profile.h" 49 #include "chrome/browser/ui/webui/help/help_utils_chromeos.h" 50 #include "chromeos/chromeos_switches.h" 51 #include "chromeos/dbus/dbus_thread_manager.h" 52 #include "chromeos/dbus/power_manager_client.h" 53 #include "content/public/browser/browser_thread.h" 54 #endif 55 56 using base::ListValue; 57 using content::BrowserThread; 58 59 namespace { 60 61 // Returns the browser version as a string. 62 base::string16 BuildBrowserVersionString() { 63 chrome::VersionInfo version_info; 64 DCHECK(version_info.is_valid()); 65 66 std::string browser_version = version_info.Version(); 67 std::string version_modifier = 68 chrome::VersionInfo::GetVersionStringModifier(); 69 if (!version_modifier.empty()) 70 browser_version += " " + version_modifier; 71 72 #if !defined(GOOGLE_CHROME_BUILD) 73 browser_version += " ("; 74 browser_version += version_info.LastChange(); 75 browser_version += ")"; 76 #endif 77 78 return UTF8ToUTF16(browser_version); 79 } 80 81 #if defined(OS_CHROMEOS) 82 83 // Returns message that informs user that for update it's better to 84 // connect to a network of one of the allowed types. 85 base::string16 GetAllowedConnectionTypesMessage() { 86 if (help_utils_chromeos::IsUpdateOverCellularAllowed()) { 87 return l10n_util::GetStringUTF16(IDS_UPGRADE_NETWORK_LIST_CELLULAR_ALLOWED); 88 } else { 89 return l10n_util::GetStringUTF16( 90 IDS_UPGRADE_NETWORK_LIST_CELLULAR_DISALLOWED); 91 } 92 } 93 94 // Returns true if the device is enterprise managed, false otherwise. 95 bool IsEnterpriseManaged() { 96 return g_browser_process->browser_policy_connector()->IsEnterpriseManaged(); 97 } 98 99 // Returns true if current user can change channel, false otherwise. 100 bool CanChangeChannel() { 101 bool value = false; 102 chromeos::CrosSettings::Get()->GetBoolean(chromeos::kReleaseChannelDelegated, 103 &value); 104 105 // On a managed machine we delegate this setting to the users of the same 106 // domain only if the policy value is "domain". 107 if (IsEnterpriseManaged()) { 108 if (!value) 109 return false; 110 // Get the currently logged in user and strip the domain part only. 111 std::string domain = ""; 112 std::string user = chromeos::UserManager::Get()->GetLoggedInUser()->email(); 113 size_t at_pos = user.find('@'); 114 if (at_pos != std::string::npos && at_pos + 1 < user.length()) 115 domain = user.substr(user.find('@') + 1); 116 return domain == g_browser_process->browser_policy_connector()-> 117 GetEnterpriseDomain(); 118 } else if (chromeos::UserManager::Get()->IsCurrentUserOwner()) { 119 // On non managed machines we have local owner who is the only one to change 120 // anything. Ensure that ReleaseChannelDelegated is false. 121 return !value; 122 } 123 return false; 124 } 125 126 #endif // defined(OS_CHROMEOS) 127 128 } // namespace 129 130 HelpHandler::HelpHandler() 131 : version_updater_(VersionUpdater::Create()), 132 weak_factory_(this) { 133 } 134 135 HelpHandler::~HelpHandler() { 136 } 137 138 void HelpHandler::GetLocalizedValues(content::WebUIDataSource* source) { 139 struct L10nResources { 140 const char* name; 141 int ids; 142 }; 143 144 static L10nResources resources[] = { 145 { "helpTitle", IDS_HELP_TITLE }, 146 { "aboutTitle", IDS_ABOUT_TAB_TITLE }, 147 #if defined(OS_CHROMEOS) 148 { "aboutProductTitle", IDS_PRODUCT_OS_NAME }, 149 #else 150 { "aboutProductTitle", IDS_PRODUCT_NAME }, 151 #endif 152 { "aboutProductDescription", IDS_ABOUT_PRODUCT_DESCRIPTION }, 153 { "relaunch", IDS_RELAUNCH_BUTTON }, 154 #if defined(OS_CHROMEOS) 155 { "relaunchAndPowerwash", IDS_RELAUNCH_AND_POWERWASH_BUTTON }, 156 #endif 157 { "productName", IDS_PRODUCT_NAME }, 158 { "updateCheckStarted", IDS_UPGRADE_CHECK_STARTED }, 159 { "upToDate", IDS_UPGRADE_UP_TO_DATE }, 160 { "updating", IDS_UPGRADE_UPDATING }, 161 #if defined(OS_CHROMEOS) 162 { "updatingChannelSwitch", IDS_UPGRADE_UPDATING_CHANNEL_SWITCH }, 163 #endif 164 { "updateAlmostDone", IDS_UPGRADE_SUCCESSFUL_RELAUNCH }, 165 #if defined(OS_CHROMEOS) 166 { "successfulChannelSwitch", IDS_UPGRADE_SUCCESSFUL_CHANNEL_SWITCH }, 167 #endif 168 { "getHelpWithChrome", IDS_GET_HELP_USING_CHROME }, 169 { "reportAnIssue", IDS_REPORT_AN_ISSUE }, 170 #if defined(OS_CHROMEOS) 171 { "platform", IDS_PLATFORM_LABEL }, 172 { "firmware", IDS_ABOUT_PAGE_FIRMWARE }, 173 { "showMoreInfo", IDS_SHOW_MORE_INFO }, 174 { "hideMoreInfo", IDS_HIDE_MORE_INFO }, 175 { "channel", IDS_ABOUT_PAGE_CHANNEL }, 176 { "stable", IDS_ABOUT_PAGE_CHANNEL_STABLE }, 177 { "beta", IDS_ABOUT_PAGE_CHANNEL_BETA }, 178 { "dev", IDS_ABOUT_PAGE_CHANNEL_DEVELOPMENT }, 179 { "channel-changed", IDS_ABOUT_PAGE_CHANNEL_CHANGED }, 180 { "currentChannelStable", IDS_ABOUT_PAGE_CURRENT_CHANNEL_STABLE }, 181 { "currentChannelBeta", IDS_ABOUT_PAGE_CURRENT_CHANNEL_BETA }, 182 { "currentChannelDev", IDS_ABOUT_PAGE_CURRENT_CHANNEL_DEV }, 183 { "currentChannel", IDS_ABOUT_PAGE_CURRENT_CHANNEL }, 184 { "channelChangeButton", IDS_ABOUT_PAGE_CHANNEL_CHANGE_BUTTON }, 185 { "channelChangeDisallowedMessage", 186 IDS_ABOUT_PAGE_CHANNEL_CHANGE_DISALLOWED_MESSAGE }, 187 { "channelChangePageTitle", IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_TITLE }, 188 { "channelChangePagePowerwashTitle", 189 IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_POWERWASH_TITLE }, 190 { "channelChangePagePowerwashMessage", 191 IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_POWERWASH_MESSAGE }, 192 { "channelChangePageDelayedChangeTitle", 193 IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_DELAYED_CHANGE_TITLE }, 194 { "channelChangePageUnstableTitle", 195 IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_UNSTABLE_TITLE }, 196 { "channelChangePagePowerwashButton", 197 IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_POWERWASH_BUTTON }, 198 { "channelChangePageChangeButton", 199 IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_CHANGE_BUTTON }, 200 { "channelChangePageCancelButton", 201 IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_CANCEL_BUTTON }, 202 { "webkit", IDS_WEBKIT }, 203 { "userAgent", IDS_ABOUT_VERSION_USER_AGENT }, 204 { "commandLine", IDS_ABOUT_VERSION_COMMAND_LINE }, 205 { "buildDate", IDS_ABOUT_VERSION_BUILD_DATE }, 206 #endif 207 #if defined(OS_MACOSX) 208 { "promote", IDS_ABOUT_CHROME_PROMOTE_UPDATER }, 209 { "learnMore", IDS_LEARN_MORE }, 210 #endif 211 }; 212 213 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(resources); ++i) { 214 source->AddString(resources[i].name, 215 l10n_util::GetStringUTF16(resources[i].ids)); 216 } 217 218 source->AddString( 219 "browserVersion", 220 l10n_util::GetStringFUTF16(IDS_ABOUT_PRODUCT_VERSION, 221 BuildBrowserVersionString())); 222 223 base::Time::Exploded exploded_time; 224 base::Time::Now().LocalExplode(&exploded_time); 225 source->AddString( 226 "productCopyright", 227 l10n_util::GetStringFUTF16(IDS_ABOUT_VERSION_COPYRIGHT, 228 base::IntToString16(exploded_time.year))); 229 230 base::string16 license = l10n_util::GetStringFUTF16( 231 IDS_ABOUT_VERSION_LICENSE, 232 ASCIIToUTF16(chrome::kChromiumProjectURL), 233 ASCIIToUTF16(chrome::kChromeUICreditsURL)); 234 source->AddString("productLicense", license); 235 236 #if defined(OS_CHROMEOS) 237 base::string16 os_license = l10n_util::GetStringFUTF16( 238 IDS_ABOUT_CROS_VERSION_LICENSE, 239 ASCIIToUTF16(chrome::kChromeUIOSCreditsURL)); 240 source->AddString("productOsLicense", os_license); 241 242 base::string16 product_name = l10n_util::GetStringUTF16(IDS_PRODUCT_OS_NAME); 243 source->AddString( 244 "channelChangePageDelayedChangeMessage", 245 l10n_util::GetStringFUTF16( 246 IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_DELAYED_CHANGE_MESSAGE, 247 product_name)); 248 source->AddString( 249 "channelChangePageUnstableMessage", 250 l10n_util::GetStringFUTF16( 251 IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_UNSTABLE_MESSAGE, 252 product_name)); 253 254 if (CommandLine::ForCurrentProcess()-> 255 HasSwitch(chromeos::switches::kDisableNewChannelSwitcherUI)) { 256 source->AddBoolean("disableNewChannelSwitcherUI", true); 257 } 258 #endif 259 260 base::string16 tos = l10n_util::GetStringFUTF16( 261 IDS_ABOUT_TERMS_OF_SERVICE, UTF8ToUTF16(chrome::kChromeUITermsURL)); 262 source->AddString("productTOS", tos); 263 264 source->AddString("webkitVersion", webkit_glue::GetWebKitVersion()); 265 266 source->AddString("jsEngine", "V8"); 267 source->AddString("jsEngineVersion", v8::V8::GetVersion()); 268 269 source->AddString("userAgentInfo", content::GetUserAgent(GURL())); 270 271 CommandLine::StringType command_line = 272 CommandLine::ForCurrentProcess()->GetCommandLineString(); 273 source->AddString("commandLineInfo", command_line); 274 } 275 276 void HelpHandler::RegisterMessages() { 277 registrar_.Add(this, chrome::NOTIFICATION_UPGRADE_RECOMMENDED, 278 content::NotificationService::AllSources()); 279 280 web_ui()->RegisterMessageCallback("onPageLoaded", 281 base::Bind(&HelpHandler::OnPageLoaded, base::Unretained(this))); 282 web_ui()->RegisterMessageCallback("relaunchNow", 283 base::Bind(&HelpHandler::RelaunchNow, base::Unretained(this))); 284 web_ui()->RegisterMessageCallback("openFeedbackDialog", 285 base::Bind(&HelpHandler::OpenFeedbackDialog, base::Unretained(this))); 286 web_ui()->RegisterMessageCallback("openHelpPage", 287 base::Bind(&HelpHandler::OpenHelpPage, base::Unretained(this))); 288 #if defined(OS_CHROMEOS) 289 web_ui()->RegisterMessageCallback("setChannel", 290 base::Bind(&HelpHandler::SetChannel, base::Unretained(this))); 291 web_ui()->RegisterMessageCallback("relaunchAndPowerwash", 292 base::Bind(&HelpHandler::RelaunchAndPowerwash, base::Unretained(this))); 293 #endif 294 #if defined(OS_MACOSX) 295 web_ui()->RegisterMessageCallback("promoteUpdater", 296 base::Bind(&HelpHandler::PromoteUpdater, base::Unretained(this))); 297 #endif 298 } 299 300 void HelpHandler::Observe(int type, const content::NotificationSource& source, 301 const content::NotificationDetails& details) { 302 switch (type) { 303 case chrome::NOTIFICATION_UPGRADE_RECOMMENDED: { 304 // A version update is installed and ready to go. Refresh the UI so the 305 // correct state will be shown. 306 version_updater_->CheckForUpdate( 307 base::Bind(&HelpHandler::SetUpdateStatus, base::Unretained(this)) 308 #if defined(OS_MACOSX) 309 , base::Bind(&HelpHandler::SetPromotionState, base::Unretained(this)) 310 #endif 311 ); 312 break; 313 } 314 default: 315 NOTREACHED(); 316 } 317 } 318 319 void HelpHandler::OnPageLoaded(const ListValue* args) { 320 #if defined(OS_CHROMEOS) 321 // Version information is loaded from a callback 322 loader_.GetVersion( 323 chromeos::VersionLoader::VERSION_FULL, 324 base::Bind(&HelpHandler::OnOSVersion, base::Unretained(this)), 325 &tracker_); 326 loader_.GetFirmware( 327 base::Bind(&HelpHandler::OnOSFirmware, base::Unretained(this)), 328 &tracker_); 329 330 web_ui()->CallJavascriptFunction( 331 "help.HelpPage.updateEnableReleaseChannel", 332 base::FundamentalValue(CanChangeChannel())); 333 334 base::Time build_time = base::SysInfo::GetLsbReleaseTime(); 335 base::string16 build_date = base::TimeFormatFriendlyDate(build_time); 336 web_ui()->CallJavascriptFunction("help.HelpPage.setBuildDate", 337 base::StringValue(build_date)); 338 #endif // defined(OS_CHROMEOS) 339 340 version_updater_->CheckForUpdate( 341 base::Bind(&HelpHandler::SetUpdateStatus, base::Unretained(this)) 342 #if defined(OS_MACOSX) 343 , base::Bind(&HelpHandler::SetPromotionState, base::Unretained(this)) 344 #endif 345 ); 346 347 #if defined(OS_CHROMEOS) 348 web_ui()->CallJavascriptFunction( 349 "help.HelpPage.updateIsEnterpriseManaged", 350 base::FundamentalValue(IsEnterpriseManaged())); 351 // First argument to GetChannel() is a flag that indicates whether 352 // current channel should be returned (if true) or target channel 353 // (otherwise). 354 version_updater_->GetChannel(true, 355 base::Bind(&HelpHandler::OnCurrentChannel, weak_factory_.GetWeakPtr())); 356 version_updater_->GetChannel(false, 357 base::Bind(&HelpHandler::OnTargetChannel, weak_factory_.GetWeakPtr())); 358 #endif 359 } 360 361 #if defined(OS_MACOSX) 362 void HelpHandler::PromoteUpdater(const ListValue* args) { 363 version_updater_->PromoteUpdater(); 364 } 365 #endif 366 367 void HelpHandler::RelaunchNow(const ListValue* args) { 368 DCHECK(args->empty()); 369 version_updater_->RelaunchBrowser(); 370 } 371 372 void HelpHandler::OpenFeedbackDialog(const ListValue* args) { 373 DCHECK(args->empty()); 374 Browser* browser = chrome::FindBrowserWithWebContents( 375 web_ui()->GetWebContents()); 376 chrome::OpenFeedbackDialog(browser); 377 } 378 379 void HelpHandler::OpenHelpPage(const base::ListValue* args) { 380 DCHECK(args->empty()); 381 Browser* browser = chrome::FindBrowserWithWebContents( 382 web_ui()->GetWebContents()); 383 chrome::ShowHelp(browser, chrome::HELP_SOURCE_WEBUI); 384 } 385 386 #if defined(OS_CHROMEOS) 387 388 void HelpHandler::SetChannel(const ListValue* args) { 389 DCHECK(args->GetSize() == 2); 390 391 if (!CanChangeChannel()) { 392 LOG(WARNING) << "Non-owner tried to change release track."; 393 return; 394 } 395 396 base::string16 channel; 397 bool is_powerwash_allowed; 398 if (!args->GetString(0, &channel) || 399 !args->GetBoolean(1, &is_powerwash_allowed)) { 400 LOG(ERROR) << "Can't parse SetChannel() args"; 401 return; 402 } 403 404 version_updater_->SetChannel(UTF16ToUTF8(channel), is_powerwash_allowed); 405 if (chromeos::UserManager::Get()->IsCurrentUserOwner()) { 406 // Check for update after switching release channel. 407 version_updater_->CheckForUpdate(base::Bind(&HelpHandler::SetUpdateStatus, 408 base::Unretained(this))); 409 } 410 } 411 412 void HelpHandler::RelaunchAndPowerwash(const ListValue* args) { 413 DCHECK(args->empty()); 414 415 if (IsEnterpriseManaged()) 416 return; 417 418 PrefService* prefs = g_browser_process->local_state(); 419 prefs->SetBoolean(prefs::kFactoryResetRequested, true); 420 prefs->CommitPendingWrite(); 421 422 // Perform sign out. Current chrome process will then terminate, new one will 423 // be launched (as if it was a restart). 424 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RequestRestart(); 425 } 426 427 #endif // defined(OS_CHROMEOS) 428 429 void HelpHandler::SetUpdateStatus(VersionUpdater::Status status, 430 int progress, const base::string16& message) { 431 // Only UPDATING state should have progress set. 432 DCHECK(status == VersionUpdater::UPDATING || progress == 0); 433 434 std::string status_str; 435 switch (status) { 436 case VersionUpdater::CHECKING: 437 status_str = "checking"; 438 break; 439 case VersionUpdater::UPDATING: 440 status_str = "updating"; 441 break; 442 case VersionUpdater::NEARLY_UPDATED: 443 status_str = "nearly_updated"; 444 break; 445 case VersionUpdater::UPDATED: 446 status_str = "updated"; 447 break; 448 case VersionUpdater::FAILED: 449 case VersionUpdater::FAILED_OFFLINE: 450 case VersionUpdater::FAILED_CONNECTION_TYPE_DISALLOWED: 451 status_str = "failed"; 452 break; 453 case VersionUpdater::DISABLED: 454 status_str = "disabled"; 455 break; 456 } 457 458 web_ui()->CallJavascriptFunction("help.HelpPage.setUpdateStatus", 459 base::StringValue(status_str), 460 base::StringValue(message)); 461 462 if (status == VersionUpdater::UPDATING) { 463 web_ui()->CallJavascriptFunction("help.HelpPage.setProgress", 464 base::FundamentalValue(progress)); 465 } 466 467 #if defined(OS_CHROMEOS) 468 if (status == VersionUpdater::FAILED_OFFLINE || 469 status == VersionUpdater::FAILED_CONNECTION_TYPE_DISALLOWED) { 470 base::string16 types_msg = GetAllowedConnectionTypesMessage(); 471 if (!types_msg.empty()) { 472 web_ui()->CallJavascriptFunction( 473 "help.HelpPage.setAndShowAllowedConnectionTypesMsg", 474 base::StringValue(types_msg)); 475 } else { 476 web_ui()->CallJavascriptFunction( 477 "help.HelpPage.showAllowedConnectionTypesMsg", 478 base::FundamentalValue(false)); 479 } 480 } else { 481 web_ui()->CallJavascriptFunction( 482 "help.HelpPage.showAllowedConnectionTypesMsg", 483 base::FundamentalValue(false)); 484 } 485 #endif // defined(OS_CHROMEOS) 486 } 487 488 #if defined(OS_MACOSX) 489 void HelpHandler::SetPromotionState(VersionUpdater::PromotionState state) { 490 std::string state_str; 491 switch (state) { 492 case VersionUpdater::PROMOTE_HIDDEN: 493 state_str = "hidden"; 494 break; 495 case VersionUpdater::PROMOTE_ENABLED: 496 state_str = "enabled"; 497 break; 498 case VersionUpdater::PROMOTE_DISABLED: 499 state_str = "disabled"; 500 break; 501 } 502 503 web_ui()->CallJavascriptFunction("help.HelpPage.setPromotionState", 504 base::StringValue(state_str)); 505 } 506 #endif // defined(OS_MACOSX) 507 508 #if defined(OS_CHROMEOS) 509 void HelpHandler::OnOSVersion(const std::string& version) { 510 web_ui()->CallJavascriptFunction("help.HelpPage.setOSVersion", 511 base::StringValue(version)); 512 } 513 514 void HelpHandler::OnOSFirmware(const std::string& firmware) { 515 web_ui()->CallJavascriptFunction("help.HelpPage.setOSFirmware", 516 base::StringValue(firmware)); 517 } 518 519 void HelpHandler::OnCurrentChannel(const std::string& channel) { 520 web_ui()->CallJavascriptFunction( 521 "help.HelpPage.updateCurrentChannel", base::StringValue(channel)); 522 } 523 524 void HelpHandler::OnTargetChannel(const std::string& channel) { 525 web_ui()->CallJavascriptFunction( 526 "help.HelpPage.updateTargetChannel", base::StringValue(channel)); 527 } 528 529 #endif // defined(OS_CHROMEOS) 530