Home | History | Annotate | Download | only in chromeos
      1 // Copyright 2013 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/extensions/chromeos/kiosk_apps_handler.h"
      6 
      7 #include <algorithm>
      8 #include <set>
      9 #include <string>
     10 #include <vector>
     11 
     12 #include "base/bind.h"
     13 #include "base/command_line.h"
     14 #include "base/memory/scoped_ptr.h"
     15 #include "base/strings/string_util.h"
     16 #include "base/sys_info.h"
     17 #include "base/values.h"
     18 #include "chrome/browser/chromeos/settings/cros_settings.h"
     19 #include "chrome/grit/chromium_strings.h"
     20 #include "chrome/grit/generated_resources.h"
     21 #include "chromeos/chromeos_switches.h"
     22 #include "chromeos/settings/cros_settings_names.h"
     23 #include "components/crx_file/id_util.h"
     24 #include "components/user_manager/user_manager.h"
     25 #include "content/public/browser/web_ui.h"
     26 #include "content/public/browser/web_ui_data_source.h"
     27 #include "extensions/common/extension_urls.h"
     28 #include "ui/base/l10n/l10n_util.h"
     29 #include "ui/base/webui/web_ui_util.h"
     30 #include "url/gurl.h"
     31 
     32 namespace chromeos {
     33 
     34 namespace {
     35 
     36 // Populates app info dictionary with |app_data|.
     37 void PopulateAppDict(const KioskAppManager::App& app_data,
     38                      base::DictionaryValue* app_dict) {
     39   std::string icon_url("chrome://theme/IDR_APP_DEFAULT_ICON");
     40 
     41   // TODO(xiyuan): Replace data url with a URLDataSource.
     42   if (!app_data.icon.isNull())
     43     icon_url = webui::GetBitmapDataUrl(*app_data.icon.bitmap());
     44 
     45   // The items which are to be written into app_dict are also described in
     46   // chrome/browser/resources/extensions/chromeos/kiosk_app_list.js in @typedef
     47   // for AppDict. Please update it whenever you add or remove any keys here.
     48   app_dict->SetString("id", app_data.app_id);
     49   app_dict->SetString("name", app_data.name);
     50   app_dict->SetString("iconURL", icon_url);
     51   app_dict->SetBoolean(
     52       "autoLaunch",
     53       KioskAppManager::Get()->GetAutoLaunchApp() == app_data.app_id &&
     54       (KioskAppManager::Get()->IsAutoLaunchEnabled() ||
     55           KioskAppManager::Get()->IsAutoLaunchRequested()));
     56   app_dict->SetBoolean("isLoading", app_data.is_loading);
     57 }
     58 
     59 // Sanitize app id input value and extracts app id out of it.
     60 // Returns false if an app id could not be derived out of the input.
     61 bool ExtractsAppIdFromInput(const std::string& input,
     62                             std::string* app_id) {
     63   if (crx_file::id_util::IdIsValid(input)) {
     64     *app_id = input;
     65     return true;
     66   }
     67 
     68   GURL webstore_url = GURL(input);
     69   if (!webstore_url.is_valid())
     70     return false;
     71 
     72   GURL webstore_base_url =
     73       GURL(extension_urls::GetWebstoreItemDetailURLPrefix());
     74 
     75   if (webstore_url.scheme() != webstore_base_url.scheme() ||
     76       webstore_url.host() != webstore_base_url.host() ||
     77       !StartsWithASCII(
     78           webstore_url.path(), webstore_base_url.path(), true)) {
     79     return false;
     80   }
     81 
     82   const std::string path = webstore_url.path();
     83   const size_t last_slash = path.rfind('/');
     84   if (last_slash == std::string::npos)
     85     return false;
     86 
     87   const std::string candidate_id = path.substr(last_slash + 1);
     88   if (!crx_file::id_util::IdIsValid(candidate_id))
     89     return false;
     90 
     91   *app_id = candidate_id;
     92   return true;
     93 }
     94 
     95 }  // namespace
     96 
     97 KioskAppsHandler::KioskAppsHandler()
     98     : kiosk_app_manager_(KioskAppManager::Get()),
     99       initialized_(false),
    100       is_kiosk_enabled_(false),
    101       is_auto_launch_enabled_(false),
    102       weak_ptr_factory_(this) {
    103   kiosk_app_manager_->AddObserver(this);
    104 }
    105 
    106 KioskAppsHandler::~KioskAppsHandler() {
    107   kiosk_app_manager_->RemoveObserver(this);
    108 }
    109 
    110 void KioskAppsHandler::RegisterMessages() {
    111   web_ui()->RegisterMessageCallback("initializeKioskAppSettings",
    112       base::Bind(&KioskAppsHandler::HandleInitializeKioskAppSettings,
    113                  base::Unretained(this)));
    114   web_ui()->RegisterMessageCallback("getKioskAppSettings",
    115       base::Bind(&KioskAppsHandler::HandleGetKioskAppSettings,
    116                  base::Unretained(this)));
    117   web_ui()->RegisterMessageCallback("addKioskApp",
    118       base::Bind(&KioskAppsHandler::HandleAddKioskApp,
    119                  base::Unretained(this)));
    120   web_ui()->RegisterMessageCallback("removeKioskApp",
    121       base::Bind(&KioskAppsHandler::HandleRemoveKioskApp,
    122                  base::Unretained(this)));
    123   web_ui()->RegisterMessageCallback("enableKioskAutoLaunch",
    124       base::Bind(&KioskAppsHandler::HandleEnableKioskAutoLaunch,
    125                  base::Unretained(this)));
    126   web_ui()->RegisterMessageCallback("disableKioskAutoLaunch",
    127       base::Bind(&KioskAppsHandler::HandleDisableKioskAutoLaunch,
    128                  base::Unretained(this)));
    129   web_ui()->RegisterMessageCallback("setDisableBailoutShortcut",
    130       base::Bind(&KioskAppsHandler::HandleSetDisableBailoutShortcut,
    131                  base::Unretained(this)));
    132 }
    133 
    134 void KioskAppsHandler::GetLocalizedValues(content::WebUIDataSource* source) {
    135   source->AddString(
    136       "addKioskAppButton",
    137       l10n_util::GetStringUTF16(IDS_EXTENSIONS_ADD_KIOSK_APP_BUTTON));
    138   source->AddString(
    139       "kioskOverlayTitle",
    140       l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_OVERLAY_TITLE));
    141   source->AddString(
    142       "addKioskApp",
    143       l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_ADD_APP));
    144   source->AddString(
    145       "kioskAppIdEditHint",
    146       l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_ADD_APP_HINT));
    147   source->AddString(
    148       "enableAutoLaunchButton",
    149       l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_ENABLE_AUTO_LAUNCH));
    150   source->AddString(
    151       "disableAutoLaunchButton",
    152       l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_DISABLE_AUTO_LAUNCH));
    153   source->AddString(
    154       "autoLaunch",
    155       l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_AUTO_LAUNCH));
    156   source->AddString(
    157       "invalidApp",
    158       l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_INVALID_APP));
    159   source->AddString(
    160       "kioskDiableBailoutShortcutLabel",
    161       l10n_util::GetStringUTF16(
    162           IDS_OPTIONS_KIOSK_DISABLE_BAILOUT_SHORTCUT_LABEL));
    163   source->AddString(
    164       "kioskDisableBailoutShortcutWarningBold",
    165       l10n_util::GetStringUTF16(
    166           IDS_OPTIONS_KIOSK_DISABLE_BAILOUT_SHORTCUT_WARNING_BOLD));
    167   const base::string16 product_os_name =
    168       l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_OS_NAME);
    169   source->AddString(
    170       "kioskDisableBailoutShortcutWarning",
    171       l10n_util::GetStringFUTF16(
    172           IDS_OPTIONS_KIOSK_DISABLE_BAILOUT_SHORTCUT_WARNING_FORMAT,
    173           product_os_name));
    174   source->AddString(
    175       "kioskDisableBailoutShortcutConfirm",
    176       l10n_util::GetStringUTF16(IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL));
    177   source->AddString(
    178       "kioskDisableBailoutShortcutCancel",
    179       l10n_util::GetStringUTF16(IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL));
    180   source->AddString("done", l10n_util::GetStringUTF16(IDS_DONE));
    181   source->AddString("add", l10n_util::GetStringUTF16(IDS_ADD));
    182 }
    183 
    184 void KioskAppsHandler::OnKioskAppDataChanged(const std::string& app_id) {
    185   UpdateApp(app_id);
    186 }
    187 
    188 void KioskAppsHandler::OnKioskAppDataLoadFailure(const std::string& app_id) {
    189   ShowError(app_id);
    190 }
    191 
    192 void KioskAppsHandler::OnKioskExtensionLoadedInCache(
    193     const std::string& app_id) {
    194   UpdateApp(app_id);
    195 }
    196 
    197 void KioskAppsHandler::OnKioskExtensionDownloadFailed(
    198     const std::string& app_id) {
    199   ShowError(app_id);
    200 }
    201 
    202 void KioskAppsHandler::OnGetConsumerKioskAutoLaunchStatus(
    203     chromeos::KioskAppManager::ConsumerKioskAutoLaunchStatus status) {
    204   initialized_ = true;
    205   is_kiosk_enabled_ = user_manager::UserManager::Get()->IsCurrentUserOwner() ||
    206                       !base::SysInfo::IsRunningOnChromeOS();
    207 
    208   is_auto_launch_enabled_ =
    209       status == KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_ENABLED ||
    210       !base::SysInfo::IsRunningOnChromeOS();
    211 
    212   if (is_kiosk_enabled_) {
    213     base::DictionaryValue kiosk_params;
    214     kiosk_params.SetBoolean("kioskEnabled", is_kiosk_enabled_);
    215     kiosk_params.SetBoolean("autoLaunchEnabled", is_auto_launch_enabled_);
    216     web_ui()->CallJavascriptFunction("extensions.KioskAppsOverlay.enableKiosk",
    217                                      kiosk_params);
    218   }
    219 }
    220 
    221 
    222 void KioskAppsHandler::OnKioskAppsSettingsChanged() {
    223   SendKioskAppSettings();
    224 }
    225 
    226 void KioskAppsHandler::SendKioskAppSettings() {
    227   if (!initialized_ || !is_kiosk_enabled_)
    228     return;
    229 
    230   bool enable_bailout_shortcut;
    231   if (!CrosSettings::Get()->GetBoolean(
    232           kAccountsPrefDeviceLocalAccountAutoLoginBailoutEnabled,
    233           &enable_bailout_shortcut)) {
    234     enable_bailout_shortcut = true;
    235   }
    236 
    237   base::DictionaryValue settings;
    238   settings.SetBoolean("disableBailout", !enable_bailout_shortcut);
    239   settings.SetBoolean("hasAutoLaunchApp",
    240                       !kiosk_app_manager_->GetAutoLaunchApp().empty());
    241 
    242   KioskAppManager::Apps apps;
    243   kiosk_app_manager_->GetApps(&apps);
    244 
    245   scoped_ptr<base::ListValue> apps_list(new base::ListValue);
    246   for (size_t i = 0; i < apps.size(); ++i) {
    247     const KioskAppManager::App& app_data = apps[i];
    248 
    249     scoped_ptr<base::DictionaryValue> app_info(new base::DictionaryValue);
    250     PopulateAppDict(app_data, app_info.get());
    251     apps_list->Append(app_info.release());
    252   }
    253   settings.SetWithoutPathExpansion("apps", apps_list.release());
    254 
    255   web_ui()->CallJavascriptFunction("extensions.KioskAppsOverlay.setSettings",
    256                                    settings);
    257 }
    258 
    259 void KioskAppsHandler::HandleInitializeKioskAppSettings(
    260     const base::ListValue* args) {
    261   KioskAppManager::Get()->GetConsumerKioskAutoLaunchStatus(
    262       base::Bind(&KioskAppsHandler::OnGetConsumerKioskAutoLaunchStatus,
    263                  weak_ptr_factory_.GetWeakPtr()));
    264 }
    265 
    266 void KioskAppsHandler::HandleGetKioskAppSettings(const base::ListValue* args) {
    267   SendKioskAppSettings();
    268 }
    269 
    270 
    271 void KioskAppsHandler::HandleAddKioskApp(const base::ListValue* args) {
    272   if (!initialized_ || !is_kiosk_enabled_)
    273     return;
    274 
    275   std::string input;
    276   CHECK(args->GetString(0, &input));
    277 
    278   std::string app_id;
    279   if (!ExtractsAppIdFromInput(input, &app_id)) {
    280     OnKioskAppDataLoadFailure(input);
    281     return;
    282   }
    283 
    284   kiosk_app_manager_->AddApp(app_id);
    285 }
    286 
    287 void KioskAppsHandler::HandleRemoveKioskApp(const base::ListValue* args) {
    288   if (!initialized_ || !is_kiosk_enabled_)
    289     return;
    290 
    291   std::string app_id;
    292   CHECK(args->GetString(0, &app_id));
    293 
    294   kiosk_app_manager_->RemoveApp(app_id);
    295 }
    296 
    297 void KioskAppsHandler::HandleEnableKioskAutoLaunch(
    298     const base::ListValue* args) {
    299   if (!initialized_ || !is_kiosk_enabled_ || !is_auto_launch_enabled_)
    300     return;
    301 
    302   std::string app_id;
    303   CHECK(args->GetString(0, &app_id));
    304 
    305   kiosk_app_manager_->SetAutoLaunchApp(app_id);
    306 }
    307 
    308 void KioskAppsHandler::HandleDisableKioskAutoLaunch(
    309     const base::ListValue* args) {
    310   if (!initialized_ || !is_kiosk_enabled_ || !is_auto_launch_enabled_)
    311     return;
    312 
    313   std::string app_id;
    314   CHECK(args->GetString(0, &app_id));
    315 
    316   std::string startup_app_id = kiosk_app_manager_->GetAutoLaunchApp();
    317   if (startup_app_id != app_id)
    318     return;
    319 
    320   kiosk_app_manager_->SetAutoLaunchApp("");
    321 }
    322 
    323 void KioskAppsHandler::HandleSetDisableBailoutShortcut(
    324     const base::ListValue* args) {
    325   if (!initialized_ || !is_kiosk_enabled_)
    326     return;
    327 
    328   bool disable_bailout_shortcut;
    329   CHECK(args->GetBoolean(0, &disable_bailout_shortcut));
    330 
    331   CrosSettings::Get()->SetBoolean(
    332       kAccountsPrefDeviceLocalAccountAutoLoginBailoutEnabled,
    333       !disable_bailout_shortcut);
    334 }
    335 
    336 void KioskAppsHandler::UpdateApp(const std::string& app_id) {
    337   KioskAppManager::App app_data;
    338   if (!kiosk_app_manager_->GetApp(app_id, &app_data))
    339     return;
    340 
    341   base::DictionaryValue app_dict;
    342   PopulateAppDict(app_data, &app_dict);
    343 
    344   web_ui()->CallJavascriptFunction("extensions.KioskAppsOverlay.updateApp",
    345                                    app_dict);
    346 }
    347 
    348 void KioskAppsHandler::ShowError(const std::string& app_id) {
    349   base::StringValue app_id_value(app_id);
    350   web_ui()->CallJavascriptFunction("extensions.KioskAppsOverlay.showError",
    351                                    app_id_value);
    352 
    353   kiosk_app_manager_->RemoveApp(app_id);
    354 }
    355 
    356 }  // namespace chromeos
    357