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