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/ui/webui/options/about_page_handler.h" 6 7#include <vector> 8 9#include "base/basictypes.h" 10#include "base/callback.h" 11#include "base/command_line.h" 12#include "base/i18n/time_formatting.h" 13#include "base/string16.h" 14#include "base/string_number_conversions.h" 15#include "base/time.h" 16#include "base/utf_string_conversions.h" 17#include "base/values.h" 18#include "chrome/browser/google/google_util.h" 19#include "chrome/browser/platform_util.h" 20#include "chrome/common/chrome_version_info.h" 21#include "chrome/common/url_constants.h" 22#include "googleurl/src/gurl.h" 23#include "grit/chromium_strings.h" 24#include "grit/generated_resources.h" 25#include "grit/locale_settings.h" 26#include "grit/theme_resources.h" 27#include "ui/base/l10n/l10n_util.h" 28#include "ui/base/resource/resource_bundle.h" 29#include "webkit/glue/webkit_glue.h" 30 31#if defined(CHROME_V8) 32#include "v8/include/v8.h" 33#endif 34 35#if defined(OS_CHROMEOS) 36#include "chrome/browser/chromeos/cros/cros_library.h" 37#include "chrome/browser/chromeos/cros/power_library.h" 38#include "chrome/browser/chromeos/cros/update_library.h" 39#include "chrome/browser/chromeos/login/user_manager.h" 40#include "chrome/browser/chromeos/login/wizard_controller.h" 41#endif 42 43namespace { 44 45// These are used as placeholder text around the links in the text in the 46// license. 47const char kBeginLink[] = "BEGIN_LINK"; 48const char kEndLink[] = "END_LINK"; 49const char kBeginLinkChr[] = "BEGIN_LINK_CHR"; 50const char kBeginLinkOss[] = "BEGIN_LINK_OSS"; 51const char kEndLinkChr[] = "END_LINK_CHR"; 52const char kEndLinkOss[] = "END_LINK_OSS"; 53#if defined(OS_CHROMEOS) 54const char kBeginLinkCrosOss[] = "BEGIN_LINK_CROS_OSS"; 55const char kEndLinkCrosOss[] = "END_LINK_CROS_OSS"; 56#endif 57 58// Returns a substring [start, end) from |text|. 59std::string StringSubRange(const std::string& text, size_t start, 60 size_t end) { 61 DCHECK(end > start); 62 return text.substr(start, end - start); 63} 64 65} // namespace 66 67#if defined(OS_CHROMEOS) 68 69class AboutPageHandler::UpdateObserver 70 : public chromeos::UpdateLibrary::Observer { 71 public: 72 explicit UpdateObserver(AboutPageHandler* handler) : page_handler_(handler) {} 73 virtual ~UpdateObserver() {} 74 75 AboutPageHandler* page_handler() const { return page_handler_; } 76 77 private: 78 virtual void UpdateStatusChanged(chromeos::UpdateLibrary* object) { 79 page_handler_->UpdateStatus(object->status()); 80 } 81 82 AboutPageHandler* page_handler_; 83 84 DISALLOW_COPY_AND_ASSIGN(UpdateObserver); 85}; 86 87#endif 88 89AboutPageHandler::AboutPageHandler() 90#if defined(OS_CHROMEOS) 91 : progress_(-1), 92 sticky_(false), 93 started_(false) 94#endif 95{} 96 97AboutPageHandler::~AboutPageHandler() { 98#if defined(OS_CHROMEOS) 99 if (update_observer_.get()) { 100 chromeos::CrosLibrary::Get()->GetUpdateLibrary()-> 101 RemoveObserver(update_observer_.get()); 102 } 103#endif 104} 105 106void AboutPageHandler::GetLocalizedValues(DictionaryValue* localized_strings) { 107 DCHECK(localized_strings); 108 109 static OptionsStringResource resources[] = { 110#if defined (OS_CHROMEOS) 111 { "firmware", IDS_ABOUT_PAGE_FIRMWARE }, 112 { "product", IDS_PRODUCT_OS_NAME }, 113 { "os", IDS_PRODUCT_OS_NAME }, 114 { "loading", IDS_ABOUT_PAGE_LOADING }, 115 { "check_now", IDS_ABOUT_PAGE_CHECK_NOW }, 116 { "update_status", IDS_UPGRADE_CHECK_STARTED }, 117 { "restart_now", IDS_RELAUNCH_AND_UPDATE }, 118#else 119 { "product", IDS_PRODUCT_NAME }, 120 { "check_now", IDS_ABOUT_CHROME_UPDATE_CHECK }, 121#endif 122 { "browser", IDS_PRODUCT_NAME }, 123 { "more_info", IDS_ABOUT_PAGE_MORE_INFO }, 124 { "copyright", IDS_ABOUT_VERSION_COPYRIGHT }, 125 { "channel", IDS_ABOUT_PAGE_CHANNEL }, 126 { "stable", IDS_ABOUT_PAGE_CHANNEL_STABLE }, 127 { "beta", IDS_ABOUT_PAGE_CHANNEL_BETA }, 128 { "dev", IDS_ABOUT_PAGE_CHANNEL_DEVELOPMENT }, 129 { "canary", IDS_ABOUT_PAGE_CHANNEL_CANARY }, 130 { "channel_warning_header", IDS_ABOUT_PAGE_CHANNEL_WARNING_HEADER }, 131 { "channel_warning_text", IDS_ABOUT_PAGE_CHANNEL_WARNING_TEXT }, 132 { "user_agent", IDS_ABOUT_VERSION_USER_AGENT }, 133 { "command_line", IDS_ABOUT_VERSION_COMMAND_LINE }, 134 }; 135 136 RegisterStrings(localized_strings, resources, arraysize(resources)); 137 RegisterTitle(localized_strings, "aboutPage", IDS_ABOUT_TAB_TITLE); 138 139 // browser version 140 141 chrome::VersionInfo version_info; 142 DCHECK(version_info.is_valid()); 143 144 std::string browser_version = version_info.Version(); 145 std::string version_modifier = platform_util::GetVersionStringModifier(); 146 if (!version_modifier.empty()) 147 browser_version += " " + version_modifier; 148 149#if !defined(GOOGLE_CHROME_BUILD) 150 browser_version += " ("; 151 browser_version += version_info.LastChange(); 152 browser_version += ")"; 153#endif 154 155 localized_strings->SetString("browser_version", browser_version); 156 157 // license 158 159 std::string text = l10n_util::GetStringUTF8(IDS_ABOUT_VERSION_LICENSE); 160 161 bool chromium_url_appears_first = 162 text.find(kBeginLinkChr) < text.find(kBeginLinkOss); 163 164 size_t link1 = text.find(kBeginLink); 165 DCHECK(link1 != std::string::npos); 166 size_t link1_end = text.find(kEndLink, link1); 167 DCHECK(link1_end != std::string::npos); 168 size_t link2 = text.find(kBeginLink, link1_end); 169 DCHECK(link2 != std::string::npos); 170 size_t link2_end = text.find(kEndLink, link2); 171 DCHECK(link2_end != std::string::npos); 172 173 localized_strings->SetString("license_content_0", text.substr(0, link1)); 174 localized_strings->SetString("license_content_1", 175 StringSubRange(text, link1_end + strlen(kEndLinkOss), link2)); 176 localized_strings->SetString("license_content_2", 177 text.substr(link2_end + strlen(kEndLinkOss))); 178 179 // The Chromium link within the main text of the dialog. 180 localized_strings->SetString(chromium_url_appears_first ? 181 "license_link_content_0" : "license_link_content_1", 182 StringSubRange(text, 183 text.find(kBeginLinkChr) + strlen(kBeginLinkChr), 184 text.find(kEndLinkChr))); 185 GURL url = google_util::AppendGoogleLocaleParam( 186 GURL(chrome::kChromiumProjectURL)); 187 localized_strings->SetString(chromium_url_appears_first ? 188 "license_link_0" : "license_link_1", url.spec()); 189 190 // The Open Source link within the main text of the dialog. We need to use 191 // the chrome:// variant instead of about:credits; the latter will get 192 // rewritten to about:blank. 193 localized_strings->SetString(chromium_url_appears_first ? 194 "license_link_content_1" : "license_link_content_0", 195 StringSubRange(text, 196 text.find(kBeginLinkOss) + strlen(kBeginLinkOss), 197 text.find(kEndLinkOss))); 198 localized_strings->SetString(chromium_url_appears_first ? 199 "license_link_1" : "license_link_0", chrome::kChromeUIAboutCreditsURL); 200 201#if defined(OS_CHROMEOS) 202 std::string cros_text = 203 l10n_util::GetStringUTF8(IDS_ABOUT_CROS_VERSION_LICENSE); 204 205 size_t cros_link = cros_text.find(kBeginLinkCrosOss); 206 DCHECK(cros_link != std::string::npos); 207 size_t cros_link_end = cros_text.find(kEndLinkCrosOss, cros_link); 208 DCHECK(cros_link_end != std::string::npos); 209 210 localized_strings->SetString("cros_license_content_0", 211 cros_text.substr(0, cros_link)); 212 localized_strings->SetString("cros_license_content_1", 213 cros_text.substr(cros_link_end + strlen(kEndLinkCrosOss))); 214 localized_strings->SetString("cros_license_link_content_0", 215 StringSubRange(cros_text, cros_link + strlen(kBeginLinkCrosOss), 216 cros_link_end)); 217 localized_strings->SetString("cros_license_link_0", 218 chrome::kChromeUIAboutOSCreditsURL); 219#endif 220 221 // webkit 222 223 localized_strings->SetString("webkit_version", 224 webkit_glue::GetWebKitVersion()); 225 226 // javascript 227 228#if defined(CHROME_V8) 229 localized_strings->SetString("js_engine", "V8"); 230 localized_strings->SetString("js_engine_version", v8::V8::GetVersion()); 231#else 232 localized_strings->SetString("js_engine", "JavaScriptCore"); 233 localized_strings->SetString("js_engine_version", 234 webkit_glue::GetWebKitVersion()); 235#endif 236 237 // user agent 238 239 localized_strings->SetString("user_agent_info", 240 webkit_glue::GetUserAgent(GURL())); 241 242 // command line 243 244#if defined(OS_WIN) 245 localized_strings->SetString("command_line_info", 246 WideToUTF16(CommandLine::ForCurrentProcess()->command_line_string())); 247#elif defined(OS_POSIX) 248 // TODO(viettrungluu): something horrible might happen if there are non-UTF-8 249 // arguments (since |SetString()| requires Unicode). 250 std::string command_line = ""; 251 typedef std::vector<std::string> ArgvList; 252 const ArgvList& argv = CommandLine::ForCurrentProcess()->argv(); 253 for (ArgvList::const_iterator iter = argv.begin(); iter != argv.end(); iter++) 254 command_line += " " + *iter; 255 localized_strings->SetString("command_line_info", command_line); 256#endif 257} 258 259void AboutPageHandler::RegisterMessages() { 260 web_ui_->RegisterMessageCallback("PageReady", 261 NewCallback(this, &AboutPageHandler::PageReady)); 262 web_ui_->RegisterMessageCallback("SetReleaseTrack", 263 NewCallback(this, &AboutPageHandler::SetReleaseTrack)); 264 265#if defined(OS_CHROMEOS) 266 web_ui_->RegisterMessageCallback("CheckNow", 267 NewCallback(this, &AboutPageHandler::CheckNow)); 268 web_ui_->RegisterMessageCallback("RestartNow", 269 NewCallback(this, &AboutPageHandler::RestartNow)); 270#endif 271} 272 273void AboutPageHandler::PageReady(const ListValue* args) { 274#if defined(OS_CHROMEOS) 275 // Version information is loaded from a callback 276 loader_.EnablePlatformVersions(true); 277 loader_.GetVersion(&consumer_, 278 NewCallback(this, &AboutPageHandler::OnOSVersion), 279 chromeos::VersionLoader::VERSION_FULL); 280 loader_.GetFirmware(&consumer_, 281 NewCallback(this, &AboutPageHandler::OnOSFirmware)); 282 283 chromeos::UpdateLibrary* update_library = 284 chromeos::CrosLibrary::Get()->GetUpdateLibrary(); 285 286 update_observer_.reset(new UpdateObserver(this)); 287 update_library->AddObserver(update_observer_.get()); 288 289 // Update the WebUI page with the current status. See comments below. 290 UpdateStatus(update_library->status()); 291 292 // Initiate update check. UpdateStatus() below will be called when we 293 // get update status via update_observer_. If the update has been 294 // already complete, update_observer_ won't receive a notification. 295 // This is why we manually update the WebUI page above. 296 CheckNow(NULL); 297 298 // Request the channel information. Use the observer to track the about 299 // page handler and ensure it does not get deleted before the callback. 300 update_library->GetReleaseTrack(UpdateSelectedChannel, 301 update_observer_.get()); 302#endif 303} 304 305void AboutPageHandler::SetReleaseTrack(const ListValue* args) { 306#if defined(OS_CHROMEOS) 307 if (!chromeos::UserManager::Get()->current_user_is_owner()) { 308 LOG(WARNING) << "Non-owner tried to change release track."; 309 return; 310 } 311 const std::string channel = UTF16ToUTF8(ExtractStringValue(args)); 312 chromeos::CrosLibrary::Get()->GetUpdateLibrary()->SetReleaseTrack(channel); 313#endif 314} 315 316#if defined(OS_CHROMEOS) 317 318void AboutPageHandler::CheckNow(const ListValue* args) { 319 // Make sure that libcros is loaded and OOBE is complete. 320 if (chromeos::CrosLibrary::Get()->EnsureLoaded() && 321 (!WizardController::default_controller() || 322 WizardController::IsDeviceRegistered())) { 323 chromeos::CrosLibrary::Get()->GetUpdateLibrary()-> 324 RequestUpdateCheck(NULL, // no callback 325 NULL); // no userdata 326 } 327} 328 329void AboutPageHandler::RestartNow(const ListValue* args) { 330 chromeos::CrosLibrary::Get()->GetPowerLibrary()->RequestRestart(); 331} 332 333void AboutPageHandler::UpdateStatus( 334 const chromeos::UpdateLibrary::Status& status) { 335 string16 message; 336 std::string image = "up-to-date"; 337 bool enabled = false; 338 339 switch (status.status) { 340 case chromeos::UPDATE_STATUS_IDLE: 341 if (!sticky_) { 342 message = l10n_util::GetStringFUTF16(IDS_UPGRADE_ALREADY_UP_TO_DATE, 343 l10n_util::GetStringUTF16(IDS_PRODUCT_OS_NAME)); 344 enabled = true; 345 } 346 break; 347 case chromeos::UPDATE_STATUS_CHECKING_FOR_UPDATE: 348 message = l10n_util::GetStringUTF16(IDS_UPGRADE_CHECK_STARTED); 349 sticky_ = false; 350 break; 351 case chromeos::UPDATE_STATUS_UPDATE_AVAILABLE: 352 message = l10n_util::GetStringUTF16(IDS_UPDATE_AVAILABLE); 353 started_ = true; 354 break; 355 case chromeos::UPDATE_STATUS_DOWNLOADING: 356 { 357 int progress = static_cast<int>(status.download_progress * 100.0); 358 if (progress != progress_) { 359 progress_ = progress; 360 message = l10n_util::GetStringFUTF16Int(IDS_UPDATE_DOWNLOADING, 361 progress_); 362 } 363 started_ = true; 364 } 365 break; 366 case chromeos::UPDATE_STATUS_VERIFYING: 367 message = l10n_util::GetStringUTF16(IDS_UPDATE_VERIFYING); 368 started_ = true; 369 break; 370 case chromeos::UPDATE_STATUS_FINALIZING: 371 message = l10n_util::GetStringUTF16(IDS_UPDATE_FINALIZING); 372 started_ = true; 373 break; 374 case chromeos::UPDATE_STATUS_UPDATED_NEED_REBOOT: 375 message = l10n_util::GetStringUTF16(IDS_UPDATE_COMPLETED); 376 image = "available"; 377 sticky_ = true; 378 break; 379 default: 380 // case UPDATE_STATUS_ERROR: 381 // case UPDATE_STATUS_REPORTING_ERROR_EVENT: 382 383 // The error is only displayed if we were able to determine an 384 // update was available. 385 if (started_) { 386 message = l10n_util::GetStringUTF16(IDS_UPDATE_ERROR); 387 image = "fail"; 388 enabled = true; 389 sticky_ = true; 390 started_ = false; 391 } 392 break; 393 } 394 if (message.size()) { 395 scoped_ptr<Value> update_message(Value::CreateStringValue(message)); 396 // "Checking for update..." needs to be shown for a while, so users 397 // can read it, hence insert delay for this. 398 scoped_ptr<Value> insert_delay(Value::CreateBooleanValue( 399 status.status == chromeos::UPDATE_STATUS_CHECKING_FOR_UPDATE)); 400 web_ui_->CallJavascriptFunction("AboutPage.updateStatusCallback", 401 *update_message, *insert_delay); 402 403 scoped_ptr<Value> enabled_value(Value::CreateBooleanValue(enabled)); 404 web_ui_->CallJavascriptFunction("AboutPage.updateEnableCallback", 405 *enabled_value); 406 407 scoped_ptr<Value> image_string(Value::CreateStringValue(image)); 408 web_ui_->CallJavascriptFunction("AboutPage.setUpdateImage", 409 *image_string); 410 } 411 // We'll change the "Check For Update" button to "Restart" button. 412 if (status.status == chromeos::UPDATE_STATUS_UPDATED_NEED_REBOOT) { 413 web_ui_->CallJavascriptFunction("AboutPage.changeToRestartButton"); 414 } 415} 416 417void AboutPageHandler::OnOSVersion(chromeos::VersionLoader::Handle handle, 418 std::string version) { 419 if (version.size()) { 420 scoped_ptr<Value> version_string(Value::CreateStringValue(version)); 421 web_ui_->CallJavascriptFunction("AboutPage.updateOSVersionCallback", 422 *version_string); 423 } 424} 425 426void AboutPageHandler::OnOSFirmware(chromeos::VersionLoader::Handle handle, 427 std::string firmware) { 428 if (firmware.size()) { 429 scoped_ptr<Value> firmware_string(Value::CreateStringValue(firmware)); 430 web_ui_->CallJavascriptFunction("AboutPage.updateOSFirmwareCallback", 431 *firmware_string); 432 } 433} 434 435// Callback from UpdateEngine with channel information. 436// static 437void AboutPageHandler::UpdateSelectedChannel(void* user_data, 438 const char* channel) { 439 if (!user_data || !channel) { 440 LOG(WARNING) << "UpdateSelectedChannel returned NULL."; 441 return; 442 } 443 UpdateObserver* observer = static_cast<UpdateObserver*>(user_data); 444 if (chromeos::CrosLibrary::Get()->GetUpdateLibrary()->HasObserver(observer)) { 445 // If UpdateLibrary still has the observer, then the page handler is valid. 446 AboutPageHandler* handler = observer->page_handler(); 447 scoped_ptr<Value> channel_string(Value::CreateStringValue(channel)); 448 handler->web_ui_->CallJavascriptFunction( 449 "AboutPage.updateSelectedOptionCallback", *channel_string); 450 } 451} 452 453#endif // defined(OS_CHROMEOS) 454