Home | History | Annotate | Download | only in login
      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/chromeos/login/network_screen_handler.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/bind.h"
     10 #include "base/bind_helpers.h"
     11 #include "base/command_line.h"
     12 #include "base/logging.h"
     13 #include "base/memory/weak_ptr.h"
     14 #include "base/prefs/pref_registry_simple.h"
     15 #include "base/prefs/pref_service.h"
     16 #include "base/strings/stringprintf.h"
     17 #include "base/strings/utf_string_conversions.h"
     18 #include "base/sys_info.h"
     19 #include "base/values.h"
     20 #include "chrome/browser/browser_process.h"
     21 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
     22 #include "chrome/browser/chromeos/base/locale_util.h"
     23 #include "chrome/browser/chromeos/customization_document.h"
     24 #include "chrome/browser/chromeos/idle_detector.h"
     25 #include "chrome/browser/chromeos/input_method/input_method_util.h"
     26 #include "chrome/browser/chromeos/login/screens/core_oobe_actor.h"
     27 #include "chrome/browser/chromeos/login/ui/input_events_blocker.h"
     28 #include "chrome/browser/chromeos/system/input_device_settings.h"
     29 #include "chrome/browser/chromeos/system/timezone_util.h"
     30 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
     31 #include "chrome/browser/ui/webui/options/chromeos/cros_language_options_handler.h"
     32 #include "chrome/common/pref_names.h"
     33 #include "chromeos/chromeos_switches.h"
     34 #include "chromeos/ime/extension_ime_util.h"
     35 #include "chromeos/network/network_handler.h"
     36 #include "chromeos/network/network_state_handler.h"
     37 #include "grit/chromium_strings.h"
     38 #include "grit/generated_resources.h"
     39 #include "ui/base/l10n/l10n_util.h"
     40 #include "ui/gfx/rect.h"
     41 #include "ui/views/layout/fill_layout.h"
     42 #include "ui/views/widget/widget.h"
     43 
     44 namespace {
     45 
     46 const char kJsScreenPath[] = "login.NetworkScreen";
     47 
     48 // JS API callbacks names.
     49 const char kJsApiNetworkOnExit[] = "networkOnExit";
     50 const char kJsApiNetworkOnLanguageChanged[] = "networkOnLanguageChanged";
     51 const char kJsApiNetworkOnInputMethodChanged[] = "networkOnInputMethodChanged";
     52 const char kJsApiNetworkOnTimezoneChanged[] = "networkOnTimezoneChanged";
     53 
     54 // Returns true if element was inserted.
     55 bool InsertString(const std::string& str, std::set<std::string>& to) {
     56   const std::pair<std::set<std::string>::iterator, bool> result =
     57       to.insert(str);
     58   return result.second;
     59 }
     60 
     61 void AddOptgroupOtherLayouts(base::ListValue* input_methods_list) {
     62   base::DictionaryValue* optgroup = new base::DictionaryValue;
     63   optgroup->SetString(
     64       "optionGroupName",
     65       l10n_util::GetStringUTF16(IDS_OOBE_OTHER_KEYBOARD_LAYOUTS));
     66   input_methods_list->Append(optgroup);
     67 }
     68 
     69 // For "UI Language" drop-down menu at OOBE screen we need to decide which
     70 // entry to mark "selected". If user has just selected "requested_locale",
     71 // but "loaded_locale" was actually loaded, we mark original user choice
     72 // "selected" only if loaded_locale is a backup for "requested_locale".
     73 std::string CalculateSelectedLanguage(const std::string& requested_locale,
     74                                       const std::string& loaded_locale) {
     75 
     76   std::string resolved_locale;
     77   if (!l10n_util::CheckAndResolveLocale(requested_locale, &resolved_locale))
     78     return loaded_locale;
     79 
     80   if (resolved_locale == loaded_locale)
     81     return requested_locale;
     82 
     83   return loaded_locale;
     84 }
     85 
     86 }  // namespace
     87 
     88 namespace chromeos {
     89 
     90 // NetworkScreenHandler, public: -----------------------------------------------
     91 
     92 NetworkScreenHandler::NetworkScreenHandler(CoreOobeActor* core_oobe_actor)
     93     : BaseScreenHandler(kJsScreenPath),
     94       screen_(NULL),
     95       core_oobe_actor_(core_oobe_actor),
     96       is_continue_enabled_(false),
     97       show_on_init_(false),
     98       should_reinitialize_language_keyboard_list_(false),
     99       weak_ptr_factory_(this) {
    100   DCHECK(core_oobe_actor_);
    101 
    102   input_method::InputMethodManager* manager =
    103       input_method::InputMethodManager::Get();
    104   manager->AddObserver(this);
    105   manager->GetComponentExtensionIMEManager()->AddObserver(this);
    106 }
    107 
    108 NetworkScreenHandler::~NetworkScreenHandler() {
    109   if (screen_)
    110     screen_->OnActorDestroyed(this);
    111 
    112   input_method::InputMethodManager* manager =
    113       input_method::InputMethodManager::Get();
    114   manager->RemoveObserver(this);
    115   manager->GetComponentExtensionIMEManager()->RemoveObserver(this);
    116 }
    117 
    118 // NetworkScreenHandler, NetworkScreenActor implementation: --------------------
    119 
    120 void NetworkScreenHandler::SetDelegate(NetworkScreenActor::Delegate* screen) {
    121   screen_ = screen;
    122 }
    123 
    124 void NetworkScreenHandler::PrepareToShow() {
    125 }
    126 
    127 void NetworkScreenHandler::Show() {
    128   if (!page_is_ready()) {
    129     show_on_init_ = true;
    130     return;
    131   }
    132 
    133   // Here we should handle default locales, for which we do not have UI
    134   // resources. This would load fallback, but properly show "selected" locale
    135   // in the UI.
    136   if (selected_language_code_.empty()) {
    137     const StartupCustomizationDocument* startup_manifest =
    138         StartupCustomizationDocument::GetInstance();
    139     HandleOnLanguageChanged(startup_manifest->initial_locale_default());
    140   }
    141 
    142   PrefService* prefs = g_browser_process->local_state();
    143   if (prefs->GetBoolean(prefs::kFactoryResetRequested)) {
    144     if (core_oobe_actor_)
    145       core_oobe_actor_->ShowDeviceResetScreen();
    146     return;
    147   }
    148 
    149   // Make sure all our network technologies are turned on. On OOBE, the user
    150   // should be able to select any of the available networks on the device.
    151   NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
    152   handler->SetTechnologyEnabled(NetworkTypePattern::NonVirtual(),
    153                                 true,
    154                                 chromeos::network_handler::ErrorCallback());
    155   ShowScreen(OobeUI::kScreenOobeNetwork, NULL);
    156 
    157   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableDemoMode))
    158     return;
    159   if (base::SysInfo::IsRunningOnChromeOS()) {
    160     std::string track;
    161     // We're running on an actual device; if we cannot find our release track
    162     // value or if the track contains "testimage", don't start demo mode.
    163     if (!base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_TRACK", &track) ||
    164         track.find("testimage") != std::string::npos)
    165       return;
    166   }
    167   core_oobe_actor_->InitDemoModeDetection();
    168 }
    169 
    170 void NetworkScreenHandler::Hide() {
    171 }
    172 
    173 void NetworkScreenHandler::ShowError(const base::string16& message) {
    174   CallJS("showError", message);
    175 }
    176 
    177 void NetworkScreenHandler::ClearErrors() {
    178   if (page_is_ready())
    179     core_oobe_actor_->ClearErrors();
    180 }
    181 
    182 void NetworkScreenHandler::ShowConnectingStatus(
    183     bool connecting,
    184     const base::string16& network_id) {
    185 }
    186 
    187 void NetworkScreenHandler::EnableContinue(bool enabled) {
    188   is_continue_enabled_ = enabled;
    189   if (page_is_ready())
    190     CallJS("enableContinueButton", enabled);
    191 }
    192 
    193 // NetworkScreenHandler, BaseScreenHandler implementation: --------------------
    194 
    195 void NetworkScreenHandler::DeclareLocalizedValues(
    196     LocalizedValuesBuilder* builder) {
    197   if (system::InputDeviceSettings::Get()->ForceKeyboardDrivenUINavigation())
    198     builder->Add("networkScreenGreeting", IDS_REMORA_CONFIRM_MESSAGE);
    199   else
    200     builder->Add("networkScreenGreeting", IDS_WELCOME_SCREEN_GREETING);
    201 
    202   builder->Add("networkScreenTitle", IDS_WELCOME_SCREEN_TITLE);
    203   builder->Add("networkScreenAccessibleTitle",
    204                IDS_NETWORK_SCREEN_ACCESSIBLE_TITLE);
    205   builder->Add("selectLanguage", IDS_LANGUAGE_SELECTION_SELECT);
    206   builder->Add("selectKeyboard", IDS_KEYBOARD_SELECTION_SELECT);
    207   builder->Add("selectNetwork", IDS_NETWORK_SELECTION_SELECT);
    208   builder->Add("selectTimezone", IDS_OPTIONS_SETTINGS_TIMEZONE_DESCRIPTION);
    209   builder->Add("proxySettings", IDS_OPTIONS_PROXIES_CONFIGURE_BUTTON);
    210   builder->Add("continueButton", IDS_NETWORK_SELECTION_CONTINUE_BUTTON);
    211 }
    212 
    213 void NetworkScreenHandler::GetAdditionalParameters(
    214     base::DictionaryValue* dict) {
    215   dict->Set("languageList", GetLanguageList());
    216   dict->Set("inputMethodsList", GetInputMethods());
    217   dict->Set("timezoneList", GetTimezoneList());
    218 }
    219 
    220 void NetworkScreenHandler::Initialize() {
    221   EnableContinue(is_continue_enabled_);
    222   if (show_on_init_) {
    223     show_on_init_ = false;
    224     Show();
    225   }
    226 
    227   if (should_reinitialize_language_keyboard_list_) {
    228     should_reinitialize_language_keyboard_list_ = false;
    229     ReloadLocalizedContent();
    230   }
    231 
    232   timezone_subscription_ = CrosSettings::Get()->AddSettingsObserver(
    233       kSystemTimezone,
    234       base::Bind(&NetworkScreenHandler::OnSystemTimezoneChanged,
    235                  base::Unretained(this)));
    236   OnSystemTimezoneChanged();
    237 }
    238 
    239 // NetworkScreenHandler, WebUIMessageHandler implementation: -------------------
    240 
    241 void NetworkScreenHandler::RegisterMessages() {
    242   AddCallback(kJsApiNetworkOnExit, &NetworkScreenHandler::HandleOnExit);
    243   AddCallback(kJsApiNetworkOnLanguageChanged,
    244               &NetworkScreenHandler::HandleOnLanguageChanged);
    245   AddCallback(kJsApiNetworkOnInputMethodChanged,
    246               &NetworkScreenHandler::HandleOnInputMethodChanged);
    247   AddCallback(kJsApiNetworkOnTimezoneChanged,
    248               &NetworkScreenHandler::HandleOnTimezoneChanged);
    249 }
    250 
    251 
    252 // static
    253 void NetworkScreenHandler::RegisterPrefs(PrefRegistrySimple* registry) {
    254   registry->RegisterInt64Pref(prefs::kTimeOnOobe, 0);
    255 }
    256 
    257 // NetworkScreenHandler, private: ----------------------------------------------
    258 
    259 void NetworkScreenHandler::HandleOnExit() {
    260   core_oobe_actor_->StopDemoModeDetection();
    261   ClearErrors();
    262   if (screen_)
    263     screen_->OnContinuePressed();
    264 }
    265 
    266 struct NetworkScreenHandlerOnLanguageChangedCallbackData {
    267   explicit NetworkScreenHandlerOnLanguageChangedCallbackData(
    268       base::WeakPtr<NetworkScreenHandler>& handler)
    269       : handler_(handler) {}
    270 
    271   base::WeakPtr<NetworkScreenHandler> handler_;
    272 
    273   // Block UI while resource bundle is being reloaded.
    274   chromeos::InputEventsBlocker input_events_blocker;
    275 };
    276 
    277 // static
    278 void NetworkScreenHandler::OnLanguageChangedCallback(
    279     scoped_ptr<NetworkScreenHandlerOnLanguageChangedCallbackData> context,
    280     const std::string& requested_locale,
    281     const std::string& loaded_locale,
    282     const bool success) {
    283   if (!context or !context->handler_)
    284     return;
    285 
    286   NetworkScreenHandler* const self = context->handler_.get();
    287 
    288   if (success) {
    289     if (requested_locale == loaded_locale) {
    290       self->selected_language_code_ = requested_locale;
    291     } else {
    292       self->selected_language_code_ =
    293           CalculateSelectedLanguage(requested_locale, loaded_locale);
    294     }
    295   } else {
    296     self->selected_language_code_ = loaded_locale;
    297   }
    298 
    299   self->ReloadLocalizedContent();
    300 
    301   // We still do not have device owner, so owner settings are not applied.
    302   // But Guest session can be started before owner is created, so we need to
    303   // save locale settings directly here.
    304   g_browser_process->local_state()->SetString(prefs::kApplicationLocale,
    305                                               self->selected_language_code_);
    306 
    307   AccessibilityManager::Get()->OnLocaleChanged();
    308 }
    309 
    310 void NetworkScreenHandler::HandleOnLanguageChanged(const std::string& locale) {
    311   const std::string app_locale = g_browser_process->GetApplicationLocale();
    312   if (app_locale == locale)
    313     return;
    314 
    315   base::WeakPtr<NetworkScreenHandler> weak_self =
    316       weak_ptr_factory_.GetWeakPtr();
    317   scoped_ptr<NetworkScreenHandlerOnLanguageChangedCallbackData> callback_data(
    318       new NetworkScreenHandlerOnLanguageChangedCallbackData(weak_self));
    319   scoped_ptr<locale_util::SwitchLanguageCallback> callback(
    320       new locale_util::SwitchLanguageCallback(
    321           base::Bind(&NetworkScreenHandler::OnLanguageChangedCallback,
    322                      base::Passed(callback_data.Pass()))));
    323   locale_util::SwitchLanguage(locale,
    324                               true /* enableLocaleKeyboardLayouts */,
    325                               true /* login_layouts_only */,
    326                               callback.Pass());
    327 }
    328 
    329 void NetworkScreenHandler::HandleOnInputMethodChanged(const std::string& id) {
    330   input_method::InputMethodManager::Get()->ChangeInputMethod(id);
    331 }
    332 
    333 void NetworkScreenHandler::HandleOnTimezoneChanged(
    334     const std::string& timezone_id) {
    335   std::string current_timezone_id;
    336   CrosSettings::Get()->GetString(kSystemTimezone, &current_timezone_id);
    337   if (current_timezone_id == timezone_id)
    338     return;
    339 
    340   CrosSettings::Get()->SetString(kSystemTimezone, timezone_id);
    341 }
    342 
    343 void NetworkScreenHandler::OnSystemTimezoneChanged() {
    344   std::string current_timezone_id;
    345   CrosSettings::Get()->GetString(kSystemTimezone, &current_timezone_id);
    346   CallJS("setTimezone", current_timezone_id);
    347 }
    348 
    349 base::ListValue* NetworkScreenHandler::GetLanguageList() {
    350   const std::string app_locale = g_browser_process->GetApplicationLocale();
    351   input_method::InputMethodManager* manager =
    352       input_method::InputMethodManager::Get();
    353   ComponentExtensionIMEManager* comp_manager =
    354       manager->GetComponentExtensionIMEManager();
    355   input_method::InputMethodDescriptors descriptors;
    356   if (comp_manager->IsInitialized())
    357     descriptors = comp_manager->GetXkbIMEAsInputMethodDescriptor();
    358   base::ListValue* languages_list =
    359       options::CrosLanguageOptionsHandler::GetUILanguageList(descriptors);
    360   for (size_t i = 0; i < languages_list->GetSize(); ++i) {
    361     base::DictionaryValue* language_info = NULL;
    362     if (!languages_list->GetDictionary(i, &language_info))
    363       NOTREACHED();
    364 
    365     std::string value;
    366     language_info->GetString("code", &value);
    367     std::string display_name;
    368     language_info->GetString("displayName", &display_name);
    369     std::string native_name;
    370     language_info->GetString("nativeDisplayName", &native_name);
    371 
    372     // If it's option group divider, add field name.
    373     if (value == options::kVendorOtherLanguagesListDivider) {
    374       language_info->SetString(
    375           "optionGroupName",
    376           l10n_util::GetStringUTF16(IDS_OOBE_OTHER_LANGUAGES));
    377     }
    378     if (display_name != native_name) {
    379       display_name = base::StringPrintf("%s - %s",
    380                                         display_name.c_str(),
    381                                         native_name.c_str());
    382     }
    383 
    384     language_info->SetString("value", value);
    385     language_info->SetString("title", display_name);
    386     if (selected_language_code_.empty()) {
    387       if (value == app_locale)
    388         language_info->SetBoolean("selected", true);
    389     } else {
    390       if (value == selected_language_code_)
    391         language_info->SetBoolean("selected", true);
    392     }
    393   }
    394   return languages_list;
    395 }
    396 
    397 base::DictionaryValue* CreateInputMethodsEntry(
    398     const input_method::InputMethodDescriptor& method,
    399     const std::string current_input_method_id) {
    400   input_method::InputMethodUtil* util =
    401       input_method::InputMethodManager::Get()->GetInputMethodUtil();
    402   const std::string& ime_id = method.id();
    403   scoped_ptr<base::DictionaryValue> input_method(new base::DictionaryValue);
    404   input_method->SetString("value", ime_id);
    405   input_method->SetString("title", util->GetInputMethodLongName(method));
    406   input_method->SetBoolean("selected", ime_id == current_input_method_id);
    407   return input_method.release();
    408 }
    409 
    410 void NetworkScreenHandler::OnImeComponentExtensionInitialized() {
    411   // Refreshes the language and keyboard list once the component extension
    412   // IMEs are initialized.
    413   if (page_is_ready())
    414     ReloadLocalizedContent();
    415   else
    416     should_reinitialize_language_keyboard_list_ = true;
    417 }
    418 
    419 void NetworkScreenHandler::InputMethodChanged(
    420     input_method::InputMethodManager* manager, bool show_message) {
    421   CallJS("setInputMethod", manager->GetCurrentInputMethod().id());
    422 }
    423 
    424 void NetworkScreenHandler::ReloadLocalizedContent() {
    425   base::DictionaryValue localized_strings;
    426   static_cast<OobeUI*>(web_ui()->GetController())
    427       ->GetLocalizedStrings(&localized_strings);
    428   core_oobe_actor_->ReloadContent(localized_strings);
    429 
    430   // Buttons are recreated, updated "Continue" button state.
    431   EnableContinue(is_continue_enabled_);
    432 }
    433 
    434 // static
    435 base::ListValue* NetworkScreenHandler::GetInputMethods() {
    436   base::ListValue* input_methods_list = new base::ListValue;
    437   input_method::InputMethodManager* manager =
    438       input_method::InputMethodManager::Get();
    439   input_method::InputMethodUtil* util = manager->GetInputMethodUtil();
    440   ComponentExtensionIMEManager* comp_manager =
    441       manager->GetComponentExtensionIMEManager();
    442   if (!comp_manager->IsInitialized()) {
    443     input_method::InputMethodDescriptor fallback =
    444         util->GetFallbackInputMethodDescriptor();
    445     input_methods_list->Append(
    446         CreateInputMethodsEntry(fallback, fallback.id()));
    447     return input_methods_list;
    448   }
    449 
    450   const std::vector<std::string>& hardware_login_input_methods =
    451       util->GetHardwareLoginInputMethodIds();
    452   manager->EnableLoginLayouts(g_browser_process->GetApplicationLocale(),
    453                               hardware_login_input_methods);
    454 
    455   scoped_ptr<input_method::InputMethodDescriptors> input_methods(
    456       manager->GetActiveInputMethods());
    457   const std::string current_input_method_id =
    458       manager->GetCurrentInputMethod().id();
    459   std::set<std::string> input_methods_added;
    460 
    461   for (std::vector<std::string>::const_iterator i =
    462            hardware_login_input_methods.begin();
    463        i != hardware_login_input_methods.end();
    464        ++i) {
    465     const input_method::InputMethodDescriptor* ime =
    466         util->GetInputMethodDescriptorFromId(*i);
    467     DCHECK(ime != NULL);
    468     // Do not crash in case of misconfiguration.
    469     if (ime != NULL) {
    470       input_methods_added.insert(*i);
    471       input_methods_list->Append(
    472           CreateInputMethodsEntry(*ime, current_input_method_id));
    473     }
    474   }
    475 
    476   bool optgroup_added = false;
    477   for (size_t i = 0; i < input_methods->size(); ++i) {
    478     // Makes sure the id is in legacy xkb id format.
    479     const std::string& ime_id = (*input_methods)[i].id();
    480     if (!InsertString(ime_id, input_methods_added))
    481       continue;
    482     if (!optgroup_added) {
    483       optgroup_added = true;
    484       AddOptgroupOtherLayouts(input_methods_list);
    485     }
    486     input_methods_list->Append(
    487         CreateInputMethodsEntry((*input_methods)[i], current_input_method_id));
    488   }
    489   // "xkb:us::eng" should always be in the list of available layouts.
    490   const std::string us_keyboard_id =
    491       util->GetFallbackInputMethodDescriptor().id();
    492   if (input_methods_added.find(us_keyboard_id) == input_methods_added.end()) {
    493     const input_method::InputMethodDescriptor* us_eng_descriptor =
    494         util->GetInputMethodDescriptorFromId(us_keyboard_id);
    495     DCHECK(us_eng_descriptor != NULL);
    496     if (!optgroup_added) {
    497       optgroup_added = true;
    498       AddOptgroupOtherLayouts(input_methods_list);
    499     }
    500     input_methods_list->Append(
    501         CreateInputMethodsEntry(*us_eng_descriptor, current_input_method_id));
    502   }
    503   return input_methods_list;
    504 }
    505 
    506 // static
    507 base::ListValue* NetworkScreenHandler::GetTimezoneList() {
    508   std::string current_timezone_id;
    509   CrosSettings::Get()->GetString(kSystemTimezone, &current_timezone_id);
    510 
    511   scoped_ptr<base::ListValue> timezone_list(new base::ListValue);
    512   scoped_ptr<base::ListValue> timezones = system::GetTimezoneList().Pass();
    513   for (size_t i = 0; i < timezones->GetSize(); ++i) {
    514     const base::ListValue* timezone = NULL;
    515     CHECK(timezones->GetList(i, &timezone));
    516 
    517     std::string timezone_id;
    518     CHECK(timezone->GetString(0, &timezone_id));
    519 
    520     std::string timezone_name;
    521     CHECK(timezone->GetString(1, &timezone_name));
    522 
    523     scoped_ptr<base::DictionaryValue> timezone_option(
    524         new base::DictionaryValue);
    525     timezone_option->SetString("value", timezone_id);
    526     timezone_option->SetString("title", timezone_name);
    527     timezone_option->SetBoolean("selected", timezone_id == current_timezone_id);
    528     timezone_list->Append(timezone_option.release());
    529   }
    530 
    531   return timezone_list.release();
    532 }
    533 
    534 }  // namespace chromeos
    535