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|.
PopulateAppDict(const KioskAppManager::App & app_data,base::DictionaryValue * app_dict)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.
ExtractsAppIdFromInput(const std::string & input,std::string * app_id)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
KioskAppsHandler()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
~KioskAppsHandler()106 KioskAppsHandler::~KioskAppsHandler() {
107 kiosk_app_manager_->RemoveObserver(this);
108 }
109
RegisterMessages()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
GetLocalizedValues(content::WebUIDataSource * source)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
OnKioskAppDataChanged(const std::string & app_id)184 void KioskAppsHandler::OnKioskAppDataChanged(const std::string& app_id) {
185 UpdateApp(app_id);
186 }
187
OnKioskAppDataLoadFailure(const std::string & app_id)188 void KioskAppsHandler::OnKioskAppDataLoadFailure(const std::string& app_id) {
189 ShowError(app_id);
190 }
191
OnKioskExtensionLoadedInCache(const std::string & app_id)192 void KioskAppsHandler::OnKioskExtensionLoadedInCache(
193 const std::string& app_id) {
194 UpdateApp(app_id);
195 }
196
OnKioskExtensionDownloadFailed(const std::string & app_id)197 void KioskAppsHandler::OnKioskExtensionDownloadFailed(
198 const std::string& app_id) {
199 ShowError(app_id);
200 }
201
OnGetConsumerKioskAutoLaunchStatus(chromeos::KioskAppManager::ConsumerKioskAutoLaunchStatus status)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
OnKioskAppsSettingsChanged()222 void KioskAppsHandler::OnKioskAppsSettingsChanged() {
223 SendKioskAppSettings();
224 }
225
SendKioskAppSettings()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
HandleInitializeKioskAppSettings(const base::ListValue * args)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
HandleGetKioskAppSettings(const base::ListValue * args)266 void KioskAppsHandler::HandleGetKioskAppSettings(const base::ListValue* args) {
267 SendKioskAppSettings();
268 }
269
270
HandleAddKioskApp(const base::ListValue * args)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
HandleRemoveKioskApp(const base::ListValue * args)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
HandleEnableKioskAutoLaunch(const base::ListValue * args)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
HandleDisableKioskAutoLaunch(const base::ListValue * args)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
HandleSetDisableBailoutShortcut(const base::ListValue * args)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
UpdateApp(const std::string & app_id)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
ShowError(const std::string & app_id)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