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/sys_info.h" 16 #include "base/values.h" 17 #include "chrome/browser/chromeos/login/users/user_manager.h" 18 #include "chrome/browser/chromeos/settings/cros_settings.h" 19 #include "chrome/common/extensions/extension_constants.h" 20 #include "chromeos/chromeos_switches.h" 21 #include "chromeos/settings/cros_settings_names.h" 22 #include "content/public/browser/web_ui.h" 23 #include "content/public/browser/web_ui_data_source.h" 24 #include "extensions/common/extension.h" 25 #include "grit/chromium_strings.h" 26 #include "grit/generated_resources.h" 27 #include "ui/base/l10n/l10n_util.h" 28 #include "ui/base/webui/web_ui_util.h" 29 #include "url/gurl.h" 30 31 namespace chromeos { 32 33 namespace { 34 35 // Populates app info dictionary with |app_data|. 36 void PopulateAppDict(const KioskAppManager::App& app_data, 37 base::DictionaryValue* app_dict) { 38 std::string icon_url("chrome://theme/IDR_APP_DEFAULT_ICON"); 39 40 // TODO(xiyuan): Replace data url with a URLDataSource. 41 if (!app_data.icon.isNull()) 42 icon_url = webui::GetBitmapDataUrl(*app_data.icon.bitmap()); 43 44 app_dict->SetString("id", app_data.app_id); 45 app_dict->SetString("name", app_data.name); 46 app_dict->SetString("iconURL", icon_url); 47 app_dict->SetBoolean( 48 "autoLaunch", 49 KioskAppManager::Get()->GetAutoLaunchApp() == app_data.app_id && 50 (KioskAppManager::Get()->IsAutoLaunchEnabled() || 51 KioskAppManager::Get()->IsAutoLaunchRequested())); 52 app_dict->SetBoolean("isLoading", app_data.is_loading); 53 } 54 55 // Sanitize app id input value and extracts app id out of it. 56 // Returns false if an app id could not be derived out of the input. 57 bool ExtractsAppIdFromInput(const std::string& input, 58 std::string* app_id) { 59 if (extensions::Extension::IdIsValid(input)) { 60 *app_id = input; 61 return true; 62 } 63 64 GURL webstore_url = GURL(input); 65 if (!webstore_url.is_valid()) 66 return false; 67 68 GURL webstore_base_url = 69 GURL(extension_urls::GetWebstoreItemDetailURLPrefix()); 70 71 if (webstore_url.scheme() != webstore_base_url.scheme() || 72 webstore_url.host() != webstore_base_url.host() || 73 !StartsWithASCII( 74 webstore_url.path(), webstore_base_url.path(), true)) { 75 return false; 76 } 77 78 const std::string path = webstore_url.path(); 79 const size_t last_slash = path.rfind('/'); 80 if (last_slash == std::string::npos) 81 return false; 82 83 const std::string candidate_id = path.substr(last_slash + 1); 84 if (!extensions::Extension::IdIsValid(candidate_id)) 85 return false; 86 87 *app_id = candidate_id; 88 return true; 89 } 90 91 } // namespace 92 93 KioskAppsHandler::KioskAppsHandler() 94 : kiosk_app_manager_(KioskAppManager::Get()), 95 initialized_(false), 96 is_kiosk_enabled_(false), 97 is_auto_launch_enabled_(false), 98 weak_ptr_factory_(this) { 99 kiosk_app_manager_->AddObserver(this); 100 } 101 102 KioskAppsHandler::~KioskAppsHandler() { 103 kiosk_app_manager_->RemoveObserver(this); 104 } 105 106 void KioskAppsHandler::RegisterMessages() { 107 web_ui()->RegisterMessageCallback("initializeKioskAppSettings", 108 base::Bind(&KioskAppsHandler::HandleInitializeKioskAppSettings, 109 base::Unretained(this))); 110 web_ui()->RegisterMessageCallback("getKioskAppSettings", 111 base::Bind(&KioskAppsHandler::HandleGetKioskAppSettings, 112 base::Unretained(this))); 113 web_ui()->RegisterMessageCallback("addKioskApp", 114 base::Bind(&KioskAppsHandler::HandleAddKioskApp, 115 base::Unretained(this))); 116 web_ui()->RegisterMessageCallback("removeKioskApp", 117 base::Bind(&KioskAppsHandler::HandleRemoveKioskApp, 118 base::Unretained(this))); 119 web_ui()->RegisterMessageCallback("enableKioskAutoLaunch", 120 base::Bind(&KioskAppsHandler::HandleEnableKioskAutoLaunch, 121 base::Unretained(this))); 122 web_ui()->RegisterMessageCallback("disableKioskAutoLaunch", 123 base::Bind(&KioskAppsHandler::HandleDisableKioskAutoLaunch, 124 base::Unretained(this))); 125 web_ui()->RegisterMessageCallback("setDisableBailoutShortcut", 126 base::Bind(&KioskAppsHandler::HandleSetDisableBailoutShortcut, 127 base::Unretained(this))); 128 } 129 130 void KioskAppsHandler::GetLocalizedValues(content::WebUIDataSource* source) { 131 source->AddString( 132 "addKioskAppButton", 133 l10n_util::GetStringUTF16(IDS_EXTENSIONS_ADD_KIOSK_APP_BUTTON)); 134 source->AddString( 135 "kioskOverlayTitle", 136 l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_OVERLAY_TITLE)); 137 source->AddString( 138 "addKioskApp", 139 l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_ADD_APP)); 140 source->AddString( 141 "kioskAppIdEditHint", 142 l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_ADD_APP_HINT)); 143 source->AddString( 144 "enableAutoLaunchButton", 145 l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_ENABLE_AUTO_LAUNCH)); 146 source->AddString( 147 "disableAutoLaunchButton", 148 l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_DISABLE_AUTO_LAUNCH)); 149 source->AddString( 150 "autoLaunch", 151 l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_AUTO_LAUNCH)); 152 source->AddString( 153 "invalidApp", 154 l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_INVALID_APP)); 155 source->AddString( 156 "kioskDiableBailoutShortcutLabel", 157 l10n_util::GetStringUTF16( 158 IDS_OPTIONS_KIOSK_DISABLE_BAILOUT_SHORTCUT_LABEL)); 159 source->AddString( 160 "kioskDisableBailoutShortcutWarningBold", 161 l10n_util::GetStringUTF16( 162 IDS_OPTIONS_KIOSK_DISABLE_BAILOUT_SHORTCUT_WARNING_BOLD)); 163 const base::string16 product_os_name = 164 l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_OS_NAME); 165 source->AddString( 166 "kioskDisableBailoutShortcutWarning", 167 l10n_util::GetStringFUTF16( 168 IDS_OPTIONS_KIOSK_DISABLE_BAILOUT_SHORTCUT_WARNING_FORMAT, 169 product_os_name)); 170 source->AddString( 171 "kioskDisableBailoutShortcutConfirm", 172 l10n_util::GetStringUTF16(IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL)); 173 source->AddString( 174 "kioskDisableBailoutShortcutCancel", 175 l10n_util::GetStringUTF16(IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL)); 176 source->AddString("done", l10n_util::GetStringUTF16(IDS_DONE)); 177 source->AddString("add", l10n_util::GetStringUTF16(IDS_ADD)); 178 } 179 180 void KioskAppsHandler::OnKioskAppDataChanged(const std::string& app_id) { 181 KioskAppManager::App app_data; 182 if (!kiosk_app_manager_->GetApp(app_id, &app_data)) 183 return; 184 185 base::DictionaryValue app_dict; 186 PopulateAppDict(app_data, &app_dict); 187 188 web_ui()->CallJavascriptFunction("extensions.KioskAppsOverlay.updateApp", 189 app_dict); 190 } 191 192 void KioskAppsHandler::OnKioskAppDataLoadFailure(const std::string& app_id) { 193 base::StringValue app_id_value(app_id); 194 web_ui()->CallJavascriptFunction("extensions.KioskAppsOverlay.showError", 195 app_id_value); 196 197 kiosk_app_manager_->RemoveApp(app_id); 198 } 199 200 201 void KioskAppsHandler::OnGetConsumerKioskAutoLaunchStatus( 202 chromeos::KioskAppManager::ConsumerKioskAutoLaunchStatus status) { 203 initialized_ = true; 204 is_kiosk_enabled_ = 205 chromeos::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 } // namespace chromeos 337