普通文本  |  454行  |  16.05 KB

// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/webui/options/about_page_handler.h"

#include <vector>

#include "base/basictypes.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/i18n/time_formatting.h"
#include "base/string16.h"
#include "base/string_number_conversions.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/google/google_util.h"
#include "chrome/browser/platform_util.h"
#include "chrome/common/chrome_version_info.h"
#include "chrome/common/url_constants.h"
#include "googleurl/src/gurl.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "grit/theme_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "webkit/glue/webkit_glue.h"

#if defined(CHROME_V8)
#include "v8/include/v8.h"
#endif

#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/cros/power_library.h"
#include "chrome/browser/chromeos/cros/update_library.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/chromeos/login/wizard_controller.h"
#endif

namespace {

// These are used as placeholder text around the links in the text in the
// license.
const char kBeginLink[] = "BEGIN_LINK";
const char kEndLink[] = "END_LINK";
const char kBeginLinkChr[] = "BEGIN_LINK_CHR";
const char kBeginLinkOss[] = "BEGIN_LINK_OSS";
const char kEndLinkChr[] = "END_LINK_CHR";
const char kEndLinkOss[] = "END_LINK_OSS";
#if defined(OS_CHROMEOS)
const char kBeginLinkCrosOss[] = "BEGIN_LINK_CROS_OSS";
const char kEndLinkCrosOss[] = "END_LINK_CROS_OSS";
#endif

// Returns a substring [start, end) from |text|.
std::string StringSubRange(const std::string& text, size_t start,
                           size_t end) {
  DCHECK(end > start);
  return text.substr(start, end - start);
}

}  // namespace

#if defined(OS_CHROMEOS)

class AboutPageHandler::UpdateObserver
    : public chromeos::UpdateLibrary::Observer {
 public:
  explicit UpdateObserver(AboutPageHandler* handler) : page_handler_(handler) {}
  virtual ~UpdateObserver() {}

  AboutPageHandler* page_handler() const { return page_handler_; }

 private:
  virtual void UpdateStatusChanged(chromeos::UpdateLibrary* object) {
    page_handler_->UpdateStatus(object->status());
  }

  AboutPageHandler* page_handler_;

  DISALLOW_COPY_AND_ASSIGN(UpdateObserver);
};

#endif

AboutPageHandler::AboutPageHandler()
#if defined(OS_CHROMEOS)
    : progress_(-1),
      sticky_(false),
      started_(false)
#endif
{}

AboutPageHandler::~AboutPageHandler() {
#if defined(OS_CHROMEOS)
  if (update_observer_.get()) {
    chromeos::CrosLibrary::Get()->GetUpdateLibrary()->
        RemoveObserver(update_observer_.get());
  }
#endif
}

void AboutPageHandler::GetLocalizedValues(DictionaryValue* localized_strings) {
  DCHECK(localized_strings);

  static OptionsStringResource resources[] = {
#if defined (OS_CHROMEOS)
    { "firmware", IDS_ABOUT_PAGE_FIRMWARE },
    { "product", IDS_PRODUCT_OS_NAME },
    { "os", IDS_PRODUCT_OS_NAME },
    { "loading", IDS_ABOUT_PAGE_LOADING },
    { "check_now", IDS_ABOUT_PAGE_CHECK_NOW },
    { "update_status", IDS_UPGRADE_CHECK_STARTED },
    { "restart_now", IDS_RELAUNCH_AND_UPDATE },
#else
    { "product", IDS_PRODUCT_NAME },
    { "check_now", IDS_ABOUT_CHROME_UPDATE_CHECK },
#endif
    { "browser", IDS_PRODUCT_NAME },
    { "more_info", IDS_ABOUT_PAGE_MORE_INFO },
    { "copyright", IDS_ABOUT_VERSION_COPYRIGHT },
    { "channel", IDS_ABOUT_PAGE_CHANNEL },
    { "stable", IDS_ABOUT_PAGE_CHANNEL_STABLE },
    { "beta", IDS_ABOUT_PAGE_CHANNEL_BETA },
    { "dev", IDS_ABOUT_PAGE_CHANNEL_DEVELOPMENT },
    { "canary", IDS_ABOUT_PAGE_CHANNEL_CANARY },
    { "channel_warning_header", IDS_ABOUT_PAGE_CHANNEL_WARNING_HEADER },
    { "channel_warning_text", IDS_ABOUT_PAGE_CHANNEL_WARNING_TEXT },
    { "user_agent", IDS_ABOUT_VERSION_USER_AGENT },
    { "command_line", IDS_ABOUT_VERSION_COMMAND_LINE },
  };

  RegisterStrings(localized_strings, resources, arraysize(resources));
  RegisterTitle(localized_strings, "aboutPage", IDS_ABOUT_TAB_TITLE);

  // browser version

  chrome::VersionInfo version_info;
  DCHECK(version_info.is_valid());

  std::string browser_version = version_info.Version();
  std::string version_modifier = platform_util::GetVersionStringModifier();
  if (!version_modifier.empty())
    browser_version += " " + version_modifier;

#if !defined(GOOGLE_CHROME_BUILD)
  browser_version += " (";
  browser_version += version_info.LastChange();
  browser_version += ")";
#endif

  localized_strings->SetString("browser_version", browser_version);

  // license

  std::string text = l10n_util::GetStringUTF8(IDS_ABOUT_VERSION_LICENSE);

  bool chromium_url_appears_first =
      text.find(kBeginLinkChr) < text.find(kBeginLinkOss);

  size_t link1 = text.find(kBeginLink);
  DCHECK(link1 != std::string::npos);
  size_t link1_end = text.find(kEndLink, link1);
  DCHECK(link1_end != std::string::npos);
  size_t link2 = text.find(kBeginLink, link1_end);
  DCHECK(link2 != std::string::npos);
  size_t link2_end = text.find(kEndLink, link2);
  DCHECK(link2_end != std::string::npos);

  localized_strings->SetString("license_content_0", text.substr(0, link1));
  localized_strings->SetString("license_content_1",
      StringSubRange(text, link1_end + strlen(kEndLinkOss), link2));
  localized_strings->SetString("license_content_2",
      text.substr(link2_end + strlen(kEndLinkOss)));

  // The Chromium link within the main text of the dialog.
  localized_strings->SetString(chromium_url_appears_first ?
      "license_link_content_0" : "license_link_content_1",
      StringSubRange(text,
                     text.find(kBeginLinkChr) + strlen(kBeginLinkChr),
                     text.find(kEndLinkChr)));
  GURL url = google_util::AppendGoogleLocaleParam(
      GURL(chrome::kChromiumProjectURL));
  localized_strings->SetString(chromium_url_appears_first ?
      "license_link_0" : "license_link_1", url.spec());

  // The Open Source link within the main text of the dialog.  We need to use
  // the chrome:// variant instead of about:credits; the latter will get
  // rewritten to about:blank.
  localized_strings->SetString(chromium_url_appears_first ?
      "license_link_content_1" : "license_link_content_0",
      StringSubRange(text,
                     text.find(kBeginLinkOss) + strlen(kBeginLinkOss),
                     text.find(kEndLinkOss)));
  localized_strings->SetString(chromium_url_appears_first ?
      "license_link_1" : "license_link_0", chrome::kChromeUIAboutCreditsURL);

#if defined(OS_CHROMEOS)
  std::string cros_text =
      l10n_util::GetStringUTF8(IDS_ABOUT_CROS_VERSION_LICENSE);

  size_t cros_link = cros_text.find(kBeginLinkCrosOss);
  DCHECK(cros_link != std::string::npos);
  size_t cros_link_end = cros_text.find(kEndLinkCrosOss, cros_link);
  DCHECK(cros_link_end != std::string::npos);

  localized_strings->SetString("cros_license_content_0",
      cros_text.substr(0, cros_link));
  localized_strings->SetString("cros_license_content_1",
      cros_text.substr(cros_link_end + strlen(kEndLinkCrosOss)));
  localized_strings->SetString("cros_license_link_content_0",
      StringSubRange(cros_text, cros_link + strlen(kBeginLinkCrosOss),
                     cros_link_end));
  localized_strings->SetString("cros_license_link_0",
      chrome::kChromeUIAboutOSCreditsURL);
#endif

  // webkit

  localized_strings->SetString("webkit_version",
                               webkit_glue::GetWebKitVersion());

  // javascript

#if defined(CHROME_V8)
  localized_strings->SetString("js_engine", "V8");
  localized_strings->SetString("js_engine_version", v8::V8::GetVersion());
#else
  localized_strings->SetString("js_engine", "JavaScriptCore");
  localized_strings->SetString("js_engine_version",
                               webkit_glue::GetWebKitVersion());
#endif

  // user agent

  localized_strings->SetString("user_agent_info",
                               webkit_glue::GetUserAgent(GURL()));

  // command line

#if defined(OS_WIN)
  localized_strings->SetString("command_line_info",
      WideToUTF16(CommandLine::ForCurrentProcess()->command_line_string()));
#elif defined(OS_POSIX)
  // TODO(viettrungluu): something horrible might happen if there are non-UTF-8
  // arguments (since |SetString()| requires Unicode).
  std::string command_line = "";
  typedef std::vector<std::string> ArgvList;
  const ArgvList& argv = CommandLine::ForCurrentProcess()->argv();
  for (ArgvList::const_iterator iter = argv.begin(); iter != argv.end(); iter++)
    command_line += " " + *iter;
  localized_strings->SetString("command_line_info", command_line);
#endif
}

void AboutPageHandler::RegisterMessages() {
  web_ui_->RegisterMessageCallback("PageReady",
      NewCallback(this, &AboutPageHandler::PageReady));
  web_ui_->RegisterMessageCallback("SetReleaseTrack",
      NewCallback(this, &AboutPageHandler::SetReleaseTrack));

#if defined(OS_CHROMEOS)
  web_ui_->RegisterMessageCallback("CheckNow",
      NewCallback(this, &AboutPageHandler::CheckNow));
  web_ui_->RegisterMessageCallback("RestartNow",
      NewCallback(this, &AboutPageHandler::RestartNow));
#endif
}

void AboutPageHandler::PageReady(const ListValue* args) {
#if defined(OS_CHROMEOS)
  // Version information is loaded from a callback
  loader_.EnablePlatformVersions(true);
  loader_.GetVersion(&consumer_,
                     NewCallback(this, &AboutPageHandler::OnOSVersion),
                     chromeos::VersionLoader::VERSION_FULL);
  loader_.GetFirmware(&consumer_,
                      NewCallback(this, &AboutPageHandler::OnOSFirmware));

  chromeos::UpdateLibrary* update_library =
      chromeos::CrosLibrary::Get()->GetUpdateLibrary();

  update_observer_.reset(new UpdateObserver(this));
  update_library->AddObserver(update_observer_.get());

  // Update the WebUI page with the current status. See comments below.
  UpdateStatus(update_library->status());

  // Initiate update check. UpdateStatus() below will be called when we
  // get update status via update_observer_. If the update has been
  // already complete, update_observer_ won't receive a notification.
  // This is why we manually update the WebUI page above.
  CheckNow(NULL);

  // Request the channel information. Use the observer to track the about
  // page handler and ensure it does not get deleted before the callback.
  update_library->GetReleaseTrack(UpdateSelectedChannel,
                                  update_observer_.get());
#endif
}

void AboutPageHandler::SetReleaseTrack(const ListValue* args) {
#if defined(OS_CHROMEOS)
  if (!chromeos::UserManager::Get()->current_user_is_owner()) {
    LOG(WARNING) << "Non-owner tried to change release track.";
    return;
  }
  const std::string channel = UTF16ToUTF8(ExtractStringValue(args));
  chromeos::CrosLibrary::Get()->GetUpdateLibrary()->SetReleaseTrack(channel);
#endif
}

#if defined(OS_CHROMEOS)

void AboutPageHandler::CheckNow(const ListValue* args) {
  // Make sure that libcros is loaded and OOBE is complete.
  if (chromeos::CrosLibrary::Get()->EnsureLoaded() &&
      (!WizardController::default_controller() ||
        WizardController::IsDeviceRegistered())) {
    chromeos::CrosLibrary::Get()->GetUpdateLibrary()->
        RequestUpdateCheck(NULL,   // no callback
                           NULL);  // no userdata
  }
}

void AboutPageHandler::RestartNow(const ListValue* args) {
  chromeos::CrosLibrary::Get()->GetPowerLibrary()->RequestRestart();
}

void AboutPageHandler::UpdateStatus(
    const chromeos::UpdateLibrary::Status& status) {
  string16 message;
  std::string image = "up-to-date";
  bool enabled = false;

  switch (status.status) {
    case chromeos::UPDATE_STATUS_IDLE:
      if (!sticky_) {
        message = l10n_util::GetStringFUTF16(IDS_UPGRADE_ALREADY_UP_TO_DATE,
            l10n_util::GetStringUTF16(IDS_PRODUCT_OS_NAME));
        enabled = true;
      }
      break;
    case chromeos::UPDATE_STATUS_CHECKING_FOR_UPDATE:
      message = l10n_util::GetStringUTF16(IDS_UPGRADE_CHECK_STARTED);
      sticky_ = false;
      break;
    case chromeos::UPDATE_STATUS_UPDATE_AVAILABLE:
      message = l10n_util::GetStringUTF16(IDS_UPDATE_AVAILABLE);
      started_ = true;
      break;
    case chromeos::UPDATE_STATUS_DOWNLOADING:
    {
      int progress = static_cast<int>(status.download_progress * 100.0);
      if (progress != progress_) {
        progress_ = progress;
        message = l10n_util::GetStringFUTF16Int(IDS_UPDATE_DOWNLOADING,
                                                progress_);
      }
      started_ = true;
    }
      break;
    case chromeos::UPDATE_STATUS_VERIFYING:
      message = l10n_util::GetStringUTF16(IDS_UPDATE_VERIFYING);
      started_ = true;
      break;
    case chromeos::UPDATE_STATUS_FINALIZING:
      message = l10n_util::GetStringUTF16(IDS_UPDATE_FINALIZING);
      started_ = true;
      break;
    case chromeos::UPDATE_STATUS_UPDATED_NEED_REBOOT:
      message = l10n_util::GetStringUTF16(IDS_UPDATE_COMPLETED);
      image = "available";
      sticky_ = true;
      break;
    default:
    // case UPDATE_STATUS_ERROR:
    // case UPDATE_STATUS_REPORTING_ERROR_EVENT:

    // The error is only displayed if we were able to determine an
    // update was available.
      if (started_) {
        message = l10n_util::GetStringUTF16(IDS_UPDATE_ERROR);
        image = "fail";
        enabled = true;
        sticky_ = true;
        started_ = false;
      }
      break;
  }
  if (message.size()) {
    scoped_ptr<Value> update_message(Value::CreateStringValue(message));
    // "Checking for update..." needs to be shown for a while, so users
    // can read it, hence insert delay for this.
    scoped_ptr<Value> insert_delay(Value::CreateBooleanValue(
        status.status == chromeos::UPDATE_STATUS_CHECKING_FOR_UPDATE));
    web_ui_->CallJavascriptFunction("AboutPage.updateStatusCallback",
                                    *update_message, *insert_delay);

    scoped_ptr<Value> enabled_value(Value::CreateBooleanValue(enabled));
    web_ui_->CallJavascriptFunction("AboutPage.updateEnableCallback",
                                    *enabled_value);

    scoped_ptr<Value> image_string(Value::CreateStringValue(image));
    web_ui_->CallJavascriptFunction("AboutPage.setUpdateImage",
                                    *image_string);
  }
  // We'll change the "Check For Update" button to "Restart" button.
  if (status.status == chromeos::UPDATE_STATUS_UPDATED_NEED_REBOOT) {
    web_ui_->CallJavascriptFunction("AboutPage.changeToRestartButton");
  }
}

void AboutPageHandler::OnOSVersion(chromeos::VersionLoader::Handle handle,
                                   std::string version) {
  if (version.size()) {
    scoped_ptr<Value> version_string(Value::CreateStringValue(version));
    web_ui_->CallJavascriptFunction("AboutPage.updateOSVersionCallback",
                                    *version_string);
  }
}

void AboutPageHandler::OnOSFirmware(chromeos::VersionLoader::Handle handle,
                                    std::string firmware) {
  if (firmware.size()) {
    scoped_ptr<Value> firmware_string(Value::CreateStringValue(firmware));
    web_ui_->CallJavascriptFunction("AboutPage.updateOSFirmwareCallback",
                                    *firmware_string);
  }
}

// Callback from UpdateEngine with channel information.
// static
void AboutPageHandler::UpdateSelectedChannel(void* user_data,
                                             const char* channel) {
  if (!user_data || !channel) {
    LOG(WARNING) << "UpdateSelectedChannel returned NULL.";
    return;
  }
  UpdateObserver* observer = static_cast<UpdateObserver*>(user_data);
  if (chromeos::CrosLibrary::Get()->GetUpdateLibrary()->HasObserver(observer)) {
    // If UpdateLibrary still has the observer, then the page handler is valid.
    AboutPageHandler* handler = observer->page_handler();
    scoped_ptr<Value> channel_string(Value::CreateStringValue(channel));
    handler->web_ui_->CallJavascriptFunction(
        "AboutPage.updateSelectedOptionCallback", *channel_string);
  }
}

#endif  // defined(OS_CHROMEOS)