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|.
PopulateAppDict(const KioskAppManager::App & app_data,base::DictionaryValue * app_dict)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.
ExtractsAppIdFromInput(const std::string & input,std::string * app_id)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
KioskAppsHandler()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
~KioskAppsHandler()102 KioskAppsHandler::~KioskAppsHandler() {
103 kiosk_app_manager_->RemoveObserver(this);
104 }
105
RegisterMessages()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
GetLocalizedValues(content::WebUIDataSource * source)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
OnKioskAppDataChanged(const std::string & app_id)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
OnKioskAppDataLoadFailure(const std::string & app_id)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
OnGetConsumerKioskAutoLaunchStatus(chromeos::KioskAppManager::ConsumerKioskAutoLaunchStatus status)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
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
336 } // namespace chromeos
337