普通文本  |  338行  |  11.64 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/chromeos/login/views_login_display.h"

#include <algorithm>

#include "base/stl_util-inl.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/chromeos/login/help_app_launcher.h"
#include "chrome/browser/chromeos/login/message_bubble.h"
#include "chrome/browser/chromeos/login/wizard_accessibility_helper.h"
#include "chrome/browser/chromeos/view_ids.h"
#include "chrome/browser/chromeos/wm_ipc.h"
#include "chrome/browser/ui/views/window.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "views/widget/widget_gtk.h"
#include "views/window/window.h"

namespace {

// Max number of users we'll show. The true max is the min of this and the
// number of windows that fit on the screen.
const size_t kMaxUsers = 6;

// Minimum number of users we'll show (including Guest and New User pods).
const size_t kMinUsers = 3;

// Used to indicate no user has been selected.
const size_t kNotSelected = -1;

// Offset of cursor in first position from edit left side. It's used to position
// info bubble arrow to cursor.
const int kCursorOffset = 5;

// Checks if display names are unique. If there are duplicates, enables
// tooltips with full emails to let users distinguish their accounts.
// Otherwise, disables the tooltips.
void EnableTooltipsIfNeeded(
    const std::vector<chromeos::UserController*>& controllers) {
  std::map<std::string, int> visible_display_names;
  for (size_t i = 0; i + 1 < controllers.size(); ++i) {
    const std::string& display_name =
        controllers[i]->user().GetDisplayName();
    ++visible_display_names[display_name];
  }
  for (size_t i = 0; i < controllers.size(); ++i) {
    const std::string& display_name =
        controllers[i]->user().GetDisplayName();
    bool show_tooltip = controllers[i]->is_new_user() ||
                        controllers[i]->is_guest() ||
                        visible_display_names[display_name] > 1;
    controllers[i]->EnableNameTooltip(show_tooltip);
  }
}

}  // namespace

namespace chromeos {

ViewsLoginDisplay::ViewsLoginDisplay(LoginDisplay::Delegate* delegate,
                                     const gfx::Rect& background_bounds)
    : LoginDisplay(delegate, background_bounds),
      bubble_(NULL),
      controller_for_removal_(NULL),
      selected_view_index_(kNotSelected) {
}

ViewsLoginDisplay::~ViewsLoginDisplay() {
  ClearErrors();
  STLDeleteElements(&controllers_);
  STLDeleteElements(&invisible_controllers_);
}

////////////////////////////////////////////////////////////////////////////////
// ViewsLoginDisplay, LoginDisplay implementation:
//

void ViewsLoginDisplay::Init(const std::vector<UserManager::User>& users,
                             bool show_guest,
                             bool show_new_user) {
  size_t max_users = kMaxUsers;
  if (width() > 0) {
    size_t users_per_screen = (width() - login::kUserImageSize) /
        (UserController::kUnselectedSize + UserController::kPadding);
    max_users = std::max(kMinUsers, std::min(kMaxUsers, users_per_screen));
  }

  size_t visible_users_count = std::min(users.size(), max_users -
      static_cast<int>(show_guest) - static_cast<int>(show_new_user));
  for (size_t i = 0; i < users.size(); ++i) {
    UserController* user_controller = new UserController(this, users[i]);
    if (controllers_.size() < visible_users_count) {
      controllers_.push_back(user_controller);
    } else if (user_controller->is_owner()) {
      // Make sure that owner of the device is always visible on login screen.
      invisible_controllers_.insert(invisible_controllers_.begin(),
                                    controllers_.back());
      controllers_.back() = user_controller;
    } else {
      invisible_controllers_.push_back(user_controller);
    }
  }
  if (show_guest)
    controllers_.push_back(new UserController(this, true));

  if (show_new_user)
    controllers_.push_back(new UserController(this, false));

  // If there's only new user pod, show the guest session link on it.
  bool show_guest_link = controllers_.size() == 1;
  for (size_t i = 0; i < controllers_.size(); ++i) {
    (controllers_[i])->Init(static_cast<int>(i),
                            static_cast<int>(controllers_.size()),
                            show_guest_link);
  }
  EnableTooltipsIfNeeded(controllers_);
}

void ViewsLoginDisplay::OnBeforeUserRemoved(const std::string& username) {
  controller_for_removal_ = controllers_[selected_view_index_];
  controllers_.erase(controllers_.begin() + selected_view_index_);
  EnableTooltipsIfNeeded(controllers_);

  // Update user count before unmapping windows, otherwise window manager won't
  // be in the right state.
  int new_size = static_cast<int>(controllers_.size());
  for (int i = 0; i < new_size; ++i)
    controllers_[i]->UpdateUserCount(i, new_size);
}

void ViewsLoginDisplay::OnUserImageChanged(UserManager::User* user) {
  UserController* controller = GetUserControllerByEmail(user->email());
  if (controller)
    controller->OnUserImageChanged(user);
}

void ViewsLoginDisplay::OnUserRemoved(const std::string& username) {
  // We need to unmap entry windows, the windows will be unmapped in destructor.
  MessageLoop::current()->DeleteSoon(FROM_HERE, controller_for_removal_);
  controller_for_removal_ = NULL;

  // Nothing to insert.
  if (invisible_controllers_.empty())
    return;

  // Insert just before guest or add new user pods if any.
  size_t new_size = controllers_.size();
  size_t insert_position = new_size;
  while (insert_position > 0 &&
         (controllers_[insert_position - 1]->is_new_user() ||
          controllers_[insert_position - 1]->is_guest())) {
    --insert_position;
  }

  controllers_.insert(controllers_.begin() + insert_position,
                      invisible_controllers_[0]);
  invisible_controllers_.erase(invisible_controllers_.begin());

  // Update counts for exiting pods.
  new_size = controllers_.size();
  for (size_t i = 0; i < new_size; ++i) {
    if (i != insert_position)
      controllers_[i]->UpdateUserCount(i, new_size);
  }

  // And initialize new one that was invisible.
  controllers_[insert_position]->Init(insert_position, new_size, false);

  EnableTooltipsIfNeeded(controllers_);
}

void ViewsLoginDisplay::OnFadeOut() {
  controllers_[selected_view_index_]->StopThrobber();
}

void ViewsLoginDisplay::SetUIEnabled(bool is_enabled) {
  // Send message to WM to enable/disable click on windows.
  WmIpc::Message message(WM_IPC_MESSAGE_WM_SET_LOGIN_STATE);
  message.set_param(0, is_enabled);
  WmIpc::instance()->SendMessage(message);

  if (is_enabled)
    controllers_[selected_view_index_]->ClearAndEnablePassword();
}

void ViewsLoginDisplay::ShowError(int error_msg_id,
                                  int login_attempts,
                                  HelpAppLauncher::HelpTopic help_topic_id) {
  ClearErrors();
  string16 error_text;
  error_msg_id_ = error_msg_id;
  help_topic_id_ = help_topic_id;

  // GetStringF fails on debug build if there's no replacement in the string.
  if (error_msg_id == IDS_LOGIN_ERROR_AUTHENTICATING_HOSTED) {
    error_text = l10n_util::GetStringFUTF16(
        error_msg_id, l10n_util::GetStringUTF16(IDS_PRODUCT_OS_NAME));
  } else if (error_msg_id == IDS_LOGIN_ERROR_CAPTIVE_PORTAL) {
    error_text = l10n_util::GetStringFUTF16(
        error_msg_id, delegate()->GetConnectedNetworkName());
  } else {
    error_text = l10n_util::GetStringUTF16(error_msg_id);
  }

  gfx::Rect bounds =
      controllers_[selected_view_index_]->GetMainInputScreenBounds();
  BubbleBorder::ArrowLocation arrow;
  if (controllers_[selected_view_index_]->is_new_user()) {
    arrow = BubbleBorder::LEFT_TOP;
  } else {
    // Point info bubble arrow to cursor position (approximately).
    bounds.set_width(kCursorOffset * 2);
    arrow = BubbleBorder::BOTTOM_LEFT;
  }

  string16 help_link;
  if (error_msg_id == IDS_LOGIN_ERROR_CAPTIVE_PORTAL) {
    help_link = l10n_util::GetStringUTF16(IDS_LOGIN_FIX_CAPTIVE_PORTAL);
  } else if (error_msg_id == IDS_LOGIN_ERROR_CAPTIVE_PORTAL_NO_GUEST_MODE) {
    // No help link is needed.
  } else if (error_msg_id == IDS_LOGIN_ERROR_AUTHENTICATING_HOSTED ||
             login_attempts > 1) {
    help_link = l10n_util::GetStringUTF16(IDS_LEARN_MORE);
  }

  bubble_ = MessageBubble::Show(
      controllers_[selected_view_index_]->controls_window(),
      bounds,
      arrow,
      ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_WARNING),
      UTF16ToWide(error_text),
      UTF16ToWide(help_link),
      this);
  WizardAccessibilityHelper::GetInstance()->MaybeSpeak(
      UTF16ToUTF8(error_text).c_str(), false, false);
}

////////////////////////////////////////////////////////////////////////////////
// ViewsLoginDisplay, UserController::Delegate implementation:
//

void ViewsLoginDisplay::CreateAccount() {
  delegate()->CreateAccount();
}

void ViewsLoginDisplay::Login(UserController* source,
                              const string16& password) {
  delegate()->Login(source->user().email(), UTF16ToUTF8(password));
}

void ViewsLoginDisplay::LoginAsGuest() {
  delegate()->LoginAsGuest();
}

void ViewsLoginDisplay::ClearErrors() {
  // bubble_ will be set to NULL in callback.
  if (bubble_)
    bubble_->Close();
}

void ViewsLoginDisplay::OnUserSelected(UserController* source) {
  std::vector<UserController*>::const_iterator i =
      std::find(controllers_.begin(), controllers_.end(), source);
  DCHECK(i != controllers_.end());
  size_t new_selected_index = i - controllers_.begin();
  if (new_selected_index != selected_view_index_ &&
      selected_view_index_ != kNotSelected) {
    controllers_[selected_view_index_]->ClearAndEnableFields();
    controllers_[new_selected_index]->ClearAndEnableFields();
    delegate()->OnUserSelected(source->user().email());
  }
  selected_view_index_ = new_selected_index;
  WizardAccessibilityHelper::GetInstance()->MaybeSpeak(
      source->GetAccessibleUserLabel().c_str(), false, true);
}

void ViewsLoginDisplay::RemoveUser(UserController* source) {
  ClearErrors();
  UserManager::Get()->RemoveUser(source->user().email(), this);
}

void ViewsLoginDisplay::SelectUser(int index) {
  if (index >= 0 && index < static_cast<int>(controllers_.size()) &&
      index != static_cast<int>(selected_view_index_)) {
    WmIpc::Message message(WM_IPC_MESSAGE_WM_SELECT_LOGIN_USER);
    message.set_param(0, index);
    WmIpc::instance()->SendMessage(message);
  }
}

void ViewsLoginDisplay::StartEnterpriseEnrollment() {
  delegate()->OnStartEnterpriseEnrollment();
}

////////////////////////////////////////////////////////////////////////////////
// ViewsLoginDisplay, views::MessageBubbleDelegate implementation:
//

void ViewsLoginDisplay::OnHelpLinkActivated() {
  ClearErrors();
  if (error_msg_id_ == IDS_LOGIN_ERROR_CAPTIVE_PORTAL) {
    delegate()->FixCaptivePortal();
    return;
  }
  if (!parent_window())
    return;
  if (!help_app_.get())
    help_app_ = new HelpAppLauncher(parent_window());
  help_app_->ShowHelpTopic(help_topic_id_);
}

////////////////////////////////////////////////////////////////////////////////
// ViewsLoginDisplay, private:
//

UserController* ViewsLoginDisplay::GetUserControllerByEmail(
    const std::string& email) {
  std::vector<UserController*>::iterator i;
  for (i = controllers_.begin(); i != controllers_.end(); ++i) {
    if ((*i)->user().email() == email)
      return *i;
  }
  for (i = invisible_controllers_.begin();
       i != invisible_controllers_.end(); ++i) {
    if ((*i)->user().email() == email)
      return *i;
  }
  return NULL;
}

}  // namespace chromeos