summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorxiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-16 18:15:32 +0000
committerxiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-16 18:15:32 +0000
commit94c30ca27a105548898b46d4d9ff07c998b92282 (patch)
tree21fc151b8e9eb913e40791e66d7ec692d3595b2b
parentbde6bc5d302dba589b8e9a20dea48e98e25c47e3 (diff)
downloadchromium_src-94c30ca27a105548898b46d4d9ff07c998b92282.zip
chromium_src-94c30ca27a105548898b46d4d9ff07c998b92282.tar.gz
chromium_src-94c30ca27a105548898b46d4d9ff07c998b92282.tar.bz2
cros: Kiosk apps settings UI.
- Add a kiosk section in browser options when app mode is enabled and current user is owner (or running on dev box); - Click on the manage button brings up a kiosk app settings overlay; - The overlay allows user to add, remove and set app to be auto launched. And a simple error handling by showing bad app in an error banner for 5 seconds; BUG=173749 TEST=Verify kiosk apps could be added, removed and set as auto launch. Only one could be auto launched. Invalid app should show up in an error banner. R=zelidrag@chromium.org,dbeam@chromium.org TBR=sky@chromium.org for gyp changes Review URL: https://chromiumcodereview.appspot.com/12213033 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@182985 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/resources/options/browser_options.html8
-rw-r--r--chrome/browser/resources/options/browser_options.js12
-rw-r--r--chrome/browser/resources/options/chromeos/kiosk_app_list.js146
-rw-r--r--chrome/browser/resources/options/chromeos/kiosk_apps.css78
-rw-r--r--chrome/browser/resources/options/chromeos/kiosk_apps.html41
-rw-r--r--chrome/browser/resources/options/chromeos/kiosk_apps.js112
-rw-r--r--chrome/browser/resources/options/options.html2
-rw-r--r--chrome/browser/resources/options/options.js3
-rw-r--r--chrome/browser/resources/options/options_bundle.js2
-rw-r--r--chrome/browser/ui/webui/options/browser_options_handler.cc11
-rw-r--r--chrome/browser/ui/webui/options/chromeos/kiosk_apps_browsertest.js149
-rw-r--r--chrome/browser/ui/webui/options/chromeos/kiosk_apps_handler.cc236
-rw-r--r--chrome/browser/ui/webui/options/chromeos/kiosk_apps_handler.h54
-rw-r--r--chrome/browser/ui/webui/options/options_ui.cc3
-rw-r--r--chrome/chrome_browser_ui.gypi2
-rw-r--r--chrome/chrome_tests.gypi1
16 files changed, 858 insertions, 2 deletions
diff --git a/chrome/browser/resources/options/browser_options.html b/chrome/browser/resources/options/browser_options.html
index d6286da..10adb0a 100644
--- a/chrome/browser/resources/options/browser_options.html
+++ b/chrome/browser/resources/options/browser_options.html
@@ -679,6 +679,14 @@
</button>
</div>
</section>
+ <section id="kiosk-section" hidden>
+ <h3 i18n-content="advancedSectionTitleKiosk"></h3>
+ <div>
+ <button id="manage-kiosk-apps-button"
+ i18n-content="manageKioskAppsButton">
+ </button>
+ </div>
+ </section>
</if>
</if>
<if expr="not is_macosx and not pp_ifdef('chromeos')">
diff --git a/chrome/browser/resources/options/browser_options.js b/chrome/browser/resources/options/browser_options.js
index 391c792..0a42050 100644
--- a/chrome/browser/resources/options/browser_options.js
+++ b/chrome/browser/resources/options/browser_options.js
@@ -438,6 +438,17 @@ cr.define('options', function() {
OptionsPage.navigateToPage('factoryResetData');
};
}
+
+ // Kiosk section (CrOS only).
+ if (cr.isChromeOS) {
+ if (loadTimeData.getBoolean('enableKioskSection')) {
+ $('kiosk-section').hidden = false;
+
+ $('manage-kiosk-apps-button').onclick = function(event) {
+ OptionsPage.navigateToPage('kioskAppsOverlay');
+ };
+ }
+ }
},
/** @override */
@@ -1337,7 +1348,6 @@ cr.define('options', function() {
$('bluetooth-paired-devices-list').deleteItemAtIndex(index);
}
}
-
};
//Forward public APIs to private implementations.
diff --git a/chrome/browser/resources/options/chromeos/kiosk_app_list.js b/chrome/browser/resources/options/chromeos/kiosk_app_list.js
new file mode 100644
index 0000000..8500106
--- /dev/null
+++ b/chrome/browser/resources/options/chromeos/kiosk_app_list.js
@@ -0,0 +1,146 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+cr.define('options', function() {
+ /** @const */ var List = cr.ui.List;
+ /** @const */ var ListItem = cr.ui.ListItem;
+ /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel;
+
+ /**
+ * Creates a list for showing kiosk apps.
+ * @constructor
+ * @extends {cr.ui.List}
+ */
+ var KioskAppList = cr.ui.define('list');
+
+ KioskAppList.prototype = {
+ __proto__: List.prototype,
+
+ /** @override */
+ createItem: function(app) {
+ var item = new KioskAppListItem();
+ item.data = app;
+ return item;
+ },
+
+ /**
+ * Loads the given list of apps.
+ * @param {!Array.<!Object>} apps An array of app info objects.
+ */
+ setApps: function(apps) {
+ this.dataModel = new ArrayDataModel(apps);
+ },
+
+ /**
+ * Updates the given app.
+ * @param {!Object} app An app info object.
+ */
+ updateApp: function(app) {
+ for (var i = 0; i < this.items.length; ++i) {
+ if (this.items[i].data.id == app.id) {
+ this.items[i].data = app;
+ break;
+ }
+ }
+ }
+ };
+
+ /**
+ * Creates a list item for a kiosk app.
+ * @constructor
+ * @extends {cr.ui.ListItem}
+ */
+ var KioskAppListItem = cr.ui.define(function() {
+ var el = $('kiosk-app-list-item-template').cloneNode(true);
+ el.removeAttribute('id');
+ el.hidden = false;
+ return el;
+ });
+
+ KioskAppListItem.prototype = {
+ __proto__: ListItem.prototype,
+
+ /**
+ * Data object to hold app info.
+ * @type {Object}
+ * @private
+ */
+ data_: null,
+ get data() {
+ assert(this.data_);
+ return this.data_;
+ },
+ set data(data) {
+ this.data_ = data;
+ this.redraw();
+ },
+
+ /**
+ * Getter for the icon element.
+ * @type {Element}
+ */
+ get icon() {
+ return this.querySelector('.kiosk-app-icon');
+ },
+
+ /**
+ * Getter for the name element.
+ * @type {Element}
+ */
+ get name() {
+ return this.querySelector('.kiosk-app-name');
+ },
+
+ /**
+ * Getter for the status text element.
+ * @type {Element}
+ */
+ get status() {
+ return this.querySelector('.kiosk-app-status');
+ },
+
+ /** @override */
+ decorate: function() {
+ ListItem.prototype.decorate.call(this);
+
+ var sendMessageWithId = function(msg) {
+ return function() {
+ chrome.send(msg, [this.data.id]);
+ }.bind(this);
+ }.bind(this);
+
+ this.querySelector('.enable-auto-launch-button').onclick =
+ sendMessageWithId('enableKioskAutoLaunch');
+ this.querySelector('.disable-auto-launch-button').onclick =
+ sendMessageWithId('disableKioskAutoLaunch');
+ this.querySelector('.row-delete-button').onclick =
+ sendMessageWithId('removeKioskApp');
+ },
+
+ /**
+ * Updates UI from app info data.
+ */
+ redraw: function() {
+ this.icon.classList.toggle('spinner', this.data.isLoading);
+ this.icon.style.backgroundImage = 'url(' + this.data.iconURL + ')';
+
+ this.name.textContent = this.data.name || this.data.id;
+ this.status.textContent = this.data.autoLaunch ?
+ loadTimeData.getString('autoLaunch') : '';
+
+ this.autoLaunch = this.data.autoLaunch;
+ }
+ };
+
+ /*
+ * True if the app represented by this item will auto launch.
+ * @type {boolean}
+ */
+ cr.defineProperty(KioskAppListItem, 'autoLaunch', cr.PropertyKind.BOOL_ATTR);
+
+ // Export
+ return {
+ KioskAppList: KioskAppList
+ };
+});
diff --git a/chrome/browser/resources/options/chromeos/kiosk_apps.css b/chrome/browser/resources/options/chromeos/kiosk_apps.css
new file mode 100644
index 0000000..30608c5
--- /dev/null
+++ b/chrome/browser/resources/options/chromeos/kiosk_apps.css
@@ -0,0 +1,78 @@
+/* Copyright 2013 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+#kiosk-app-list {
+ border: 1px solid lightgrey;
+ margin-bottom: 5px;
+}
+
+#kiosk-app-id-edit {
+ width: 510px;
+}
+
+#kiosk-apps-error-banner {
+ -webkit-transition: opacity 150ms;
+ background-color: rgb(223, 165, 165);
+ margin: 2px 0;
+ opacity: 0;
+ padding: 5px;
+ visibility: hidden;
+ white-space: nowrap;
+ width: 510px;
+}
+
+#kiosk-apps-error-banner.visible {
+ opacity: 1;
+ visibility: visible;
+}
+
+.kiosk-app-list-item {
+ white-space: nowrap;
+}
+
+.kiosk-app-list-item .space-filler {
+ -webkit-box-flex: 1;
+}
+
+.kiosk-app-icon,
+.kiosk-app-name,
+.kiosk-app-status {
+ display: inline-block;
+ vertical-align: middle;
+}
+
+.kiosk-app-icon {
+ background-size: 100%;
+ height: 16px;
+ width: 16px;
+}
+
+.kiosk-app-icon.spinner {
+ background-image: url('chrome://resources/images/spinner.svg') !important;
+}
+
+.kiosk-app-name,
+.kiosk-app-status {
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.kiosk-app-name {
+ max-width: 250px;
+}
+
+.kiosk-app-status {
+ -webkit-margin-start: 8px;
+ max-width: 120px;
+}
+
+.disable-auto-launch-button,
+.enable-auto-launch-button {
+ display: none;
+}
+
+.kiosk-app-list-item[auto-launch]:hover .disable-auto-launch-button,
+.kiosk-app-list-item:not([auto-launch]):hover .enable-auto-launch-button {
+ display: inline-block;
+}
diff --git a/chrome/browser/resources/options/chromeos/kiosk_apps.html b/chrome/browser/resources/options/chromeos/kiosk_apps.html
new file mode 100644
index 0000000..a97c92a
--- /dev/null
+++ b/chrome/browser/resources/options/chromeos/kiosk_apps.html
@@ -0,0 +1,41 @@
+<div id="kiosk-apps-page" class="page" hidden>
+ <div class="close-button"></div>
+ <h1 i18n-content="kioskOverlayTitle"></h1>
+ <div class="content-area">
+ <div class="option">
+ <list id="kiosk-app-list"></list>
+ <div id="kiosk-apps-error-banner">
+ <span class="kiosk-app-name"></span>
+ <span class="kiosk-app-status" i18n-content="invalidApp"></span>
+ </div>
+ <label>
+ <span i18n-content="addKioskApp"></span><br>
+ <input id="kiosk-app-id-edit" type="text"
+ i18n-values="placeholder:kioskAppIdEditHint">
+ </label>
+ </div>
+ </div>
+ <div class="action-area">
+ <div class="button-strip">
+ <button id="kiosk-options-overlay-confirm" i18n-content="done">
+ </button>
+ </div>
+ </div>
+
+ <div id="kiosk-app-list-item-template" class="kiosk-app-list-item" hidden>
+ <div class="content">
+ <span class="kiosk-app-icon"></span>
+ <span class="kiosk-app-name"></span>
+ <span class="kiosk-app-status"></span>
+ </div>
+ <div class="space-filler"></div>
+ <button class="enable-auto-launch-button"
+ i18n-content="enableAutoLaunchButton">
+ </button>
+ <button class="disable-auto-launch-button"
+ i18n-content="disableAutoLaunchButton">
+ </button>
+ <button class="raw-button custom-appearance row-delete-button">
+ </button>
+ </div>
+</div>
diff --git a/chrome/browser/resources/options/chromeos/kiosk_apps.js b/chrome/browser/resources/options/chromeos/kiosk_apps.js
new file mode 100644
index 0000000..24b47899
--- /dev/null
+++ b/chrome/browser/resources/options/chromeos/kiosk_apps.js
@@ -0,0 +1,112 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+cr.define('options', function() {
+ var OptionsPage = options.OptionsPage;
+
+ /**
+ * Encapsulated handling of ChromeOS kiosk apps options page.
+ * @extends {options.OptionsPage}
+ * @constructor
+ */
+ function KioskAppsOverlay() {
+ OptionsPage.call(this,
+ 'kioskAppsOverlay',
+ loadTimeData.getString('kioskOverlayTitle'),
+ 'kiosk-apps-page');
+ }
+
+ cr.addSingletonGetter(KioskAppsOverlay);
+
+ KioskAppsOverlay.prototype = {
+ __proto__: OptionsPage.prototype,
+
+ /**
+ * Clear error timer id.
+ * @type {?number}
+ */
+ clearErrorTimer_: null,
+
+ /** @override */
+ initializePage: function() {
+ // Call base class implementation to starts preference initialization.
+ OptionsPage.prototype.initializePage.call(this);
+
+ options.KioskAppList.decorate($('kiosk-app-list'));
+
+ $('kiosk-options-overlay-confirm').onclick =
+ OptionsPage.closeOverlay.bind(OptionsPage);
+ $('kiosk-app-id-edit').addEventListener('keypress',
+ this.handleAppIdInputKeyPressed_);
+ },
+
+ /** @override */
+ didShowPage: function() {
+ chrome.send('getKioskApps');
+ $('kiosk-app-id-edit').focus();
+ },
+
+ /**
+ * Shows error for given app name/id and schedules it to cleared.
+ * @param {!string} appName App name/id to show in error banner.
+ */
+ showError: function(appName) {
+ var errorBanner = $('kiosk-apps-error-banner');
+ var appNameElement = errorBanner.querySelector('.kiosk-app-name');
+ appNameElement.textContent = appName;
+ errorBanner.classList.add('visible');
+
+ if (this.clearErrorTimer_)
+ window.clearTimeout(this.clearErrorTimer_);
+
+ // Sets a timer to clear out error banner after 5 seconds.
+ this.clearErrorTimer_ = window.setTimeout(function() {
+ errorBanner.classList.remove('visible');
+ this.clearErrorTimer_ = null;
+ }.bind(this), 5000);
+ },
+
+ /**
+ * Handles keypressed event in the app id input element.
+ * @private
+ */
+ handleAppIdInputKeyPressed_: function(e) {
+ if (e.keyIdentifier == 'Enter' && e.target.value) {
+ chrome.send('addKioskApp', [e.target.value]);
+ e.target.value = '';
+ }
+ }
+ };
+
+ /**
+ * Sets apps to be displayed in kiosk-app-list.
+ * @param {!Array.<!Object>} apps An array of app info objects.
+ */
+ KioskAppsOverlay.setApps = function(apps) {
+ $('kiosk-app-list').setApps(apps);
+ };
+
+ /**
+ * Update an app in kiosk-app-list.
+ * @param {!Object} app App info to be updated.
+ */
+ KioskAppsOverlay.updateApp = function(app) {
+ $('kiosk-app-list').updateApp(app);
+ };
+
+ /**
+ * Shows error for given app name/id.
+ * @param {!string} appName App name/id to show in error banner.
+ */
+ KioskAppsOverlay.showError = function(appName) {
+ KioskAppsOverlay.getInstance().showError(appName);
+ };
+
+ // Export
+ return {
+ KioskAppsOverlay: KioskAppsOverlay
+ };
+});
+
+<include src="kiosk_app_list.js"></include>
diff --git a/chrome/browser/resources/options/options.html b/chrome/browser/resources/options/options.html
index 685289b..2775970 100644
--- a/chrome/browser/resources/options/options.html
+++ b/chrome/browser/resources/options/options.html
@@ -48,6 +48,7 @@
<link rel="stylesheet" href="chromeos/display_options.css">
<link rel="stylesheet" href="chromeos/display_overscan.css">
<link rel="stylesheet" href="chromeos/internet_detail.css">
+ <link rel="stylesheet" href="chromeos/kiosk_apps.css">
<link rel="stylesheet" href="chromeos/pointer_overlay.css">
<link rel="stylesheet" href="factory_reset_overlay.css">
</if>
@@ -116,6 +117,7 @@
<include src="chromeos/change_picture_options.html">
<include src="chromeos/display_options.html">
<include src="chromeos/keyboard_overlay.html">
+ <include src="chromeos/kiosk_apps.html">
<include src="chromeos/pointer_overlay.html">
<include src="factory_reset_overlay.html">
</if>
diff --git a/chrome/browser/resources/options/options.js b/chrome/browser/resources/options/options.js
index 8d4e56f..4f63e32 100644
--- a/chrome/browser/resources/options/options.js
+++ b/chrome/browser/resources/options/options.js
@@ -191,6 +191,9 @@ function load() {
OptionsPage.registerOverlay(KeyboardOverlay.getInstance(),
BrowserOptions.getInstance(),
[$('keyboard-settings-button')]);
+ OptionsPage.registerOverlay(KioskAppsOverlay.getInstance(),
+ BrowserOptions.getInstance(),
+ [$('manage-kiosk-apps-button')]);
OptionsPage.registerOverlay(PointerOverlay.getInstance(),
BrowserOptions.getInstance(),
[$('pointer-settings-button')]);
diff --git a/chrome/browser/resources/options/options_bundle.js b/chrome/browser/resources/options/options_bundle.js
index a41718e..c69dd8a 100644
--- a/chrome/browser/resources/options/options_bundle.js
+++ b/chrome/browser/resources/options/options_bundle.js
@@ -31,6 +31,7 @@
<include src="chromeos/display_options.js"></include>
<include src="chromeos/display_overscan.js"></include>
<include src="chromeos/keyboard_overlay.js"></include>
+ <include src="chromeos/kiosk_apps.js"></include>
<include src="chromeos/pointer_overlay.js"></include>
var AccountsOptions = options.AccountsOptions;
var ChangePictureOptions = options.ChangePictureOptions;
@@ -40,6 +41,7 @@
var BluetoothOptions = options.BluetoothOptions;
var BluetoothPairing = options.BluetoothPairing;
var KeyboardOverlay = options.KeyboardOverlay;
+ var KioskAppsOverlay = options.KioskAppsOverlay;
var PointerOverlay = options.PointerOverlay;
var UIAccountTweaks = uiAccountTweaks.UIAccountTweaks;
</if>
diff --git a/chrome/browser/ui/webui/options/browser_options_handler.cc b/chrome/browser/ui/webui/options/browser_options_handler.cc
index 1a423cf..942de5e 100644
--- a/chrome/browser/ui/webui/options/browser_options_handler.cc
+++ b/chrome/browser/ui/webui/options/browser_options_handler.cc
@@ -88,6 +88,7 @@
#if defined(OS_CHROMEOS)
#include "ash/magnifier/magnifier_constants.h"
+#include "base/chromeos/chromeos_version.h"
#include "chrome/browser/chromeos/accessibility/accessibility_util.h"
#include "chrome/browser/chromeos/extensions/wallpaper_manager_util.h"
#include "chrome/browser/chromeos/login/user_manager.h"
@@ -306,6 +307,7 @@ void BrowserOptionsHandler::GetLocalizedValues(DictionaryValue* values) {
IDS_OPTIONS_SETTINGS_ACCESSIBILITY_VIRTUAL_KEYBOARD_DESCRIPTION },
{ "accessibilityAlwaysShowMenu",
IDS_OPTIONS_SETTINGS_ACCESSIBILITY_SHOULD_ALWAYS_SHOW_MENU },
+ { "advancedSectionTitleKiosk", IDS_OPTIONS_KIOSK },
{ "factoryResetHeading", IDS_OPTIONS_FACTORY_RESET_HEADING },
{ "factoryResetTitle", IDS_OPTIONS_FACTORY_RESET },
{ "factoryResetRestart", IDS_OPTIONS_FACTORY_RESET_BUTTON },
@@ -323,6 +325,7 @@ void BrowserOptionsHandler::GetLocalizedValues(DictionaryValue* values) {
{ "keyboardSettingsButtonTitle",
IDS_OPTIONS_DEVICE_GROUP_KEYBOARD_SETTINGS_BUTTON_TITLE },
{ "manageAccountsButtonTitle", IDS_OPTIONS_ACCOUNTS_BUTTON_TITLE },
+ { "manageKioskAppsButton", IDS_OPTIONS_KIOSK_MANAGE_BUTTON },
{ "noPointingDevices", IDS_OPTIONS_NO_POINTING_DEVICES },
{ "sectionTitleDevice", IDS_OPTIONS_DEVICE_GROUP_NAME },
{ "sectionTitleInternet", IDS_OPTIONS_INTERNET_OPTIONS_GROUP_LABEL },
@@ -450,7 +453,14 @@ void BrowserOptionsHandler::GetLocalizedValues(DictionaryValue* values) {
values->Set("magnifierList", magnifier_list.release());
+ // Sets flag of whether kiosk section should be enabled.
+ values->SetBoolean(
+ "enableKioskSection",
+ CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableAppMode) &&
+ (chromeos::UserManager::Get()->IsCurrentUserOwner() ||
+ !base::chromeos::IsRunningOnChromeOS()));
#endif
+
#if defined(OS_MACOSX)
values->SetString("macPasswordsWarning",
l10n_util::GetStringUTF16(IDS_OPTIONS_PASSWORDS_MAC_WARNING));
@@ -688,7 +698,6 @@ void BrowserOptionsHandler::InitializePage() {
web_ui()->CallJavascriptFunction(
"BrowserOptions.enableFactoryResetSection");
}
-
#endif
}
diff --git a/chrome/browser/ui/webui/options/chromeos/kiosk_apps_browsertest.js b/chrome/browser/ui/webui/options/chromeos/kiosk_apps_browsertest.js
new file mode 100644
index 0000000..99b659a
--- /dev/null
+++ b/chrome/browser/ui/webui/options/chromeos/kiosk_apps_browsertest.js
@@ -0,0 +1,149 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * TestFixture for kiosk app settings WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ **/
+function KioskAppSettingsWebUITest() {}
+
+KioskAppSettingsWebUITest.prototype = {
+ __proto__: testing.Test.prototype,
+
+ /**
+ * Browse to the kiosk app settings page.
+ */
+ browsePreload: 'chrome://settings-frame/kioskAppsOverlay',
+
+ /**
+ * Mock apps data.
+ */
+ apps_: [
+ {
+ id: 'app_1',
+ name: 'App1 Name',
+ iconURL: '',
+ autoLaunch: false,
+ isLoading: false,
+ },
+ {
+ id: 'app_2',
+ name: '', // no name
+ iconURL: '',
+ autoLaunch: false,
+ isLoading: true,
+ },
+ ],
+
+ /**
+ * Register a mock dictionary handler.
+ */
+ preLoad: function() {
+ this.makeAndRegisterMockHandler(
+ ['getKioskApps',
+ 'addKioskApp',
+ 'removeKioskApp',
+ 'enableKioskAutoLaunch',
+ 'disableKioskAutoLaunch'
+ ]);
+ this.mockHandler.stubs().getKioskApps().
+ will(callFunction(function() {
+ KioskAppsOverlay.setApps(this.apps_);
+ }.bind(this)));
+ this.mockHandler.stubs().addKioskApp(ANYTHING);
+ this.mockHandler.stubs().removeKioskApp(ANYTHING);
+ this.mockHandler.stubs().enableKioskAutoLaunch(ANYTHING);
+ this.mockHandler.stubs().disableKioskAutoLaunch(ANYTHING);
+ }
+};
+
+// Test opening kiosk app settings has correct location and app items have
+// correct label.
+TEST_F('KioskAppSettingsWebUITest', 'testOpenKioskAppSettings', function() {
+ assertEquals(this.browsePreload, document.location.href);
+
+ var appItems = $('kiosk-app-list').items;
+ assertEquals(this.apps_.length, appItems.length);
+ assertEquals(this.apps_[0].name, appItems[0].name.textContent);
+ assertFalse(appItems[0].icon.classList.contains('spinner'));
+ assertEquals(this.apps_[1].id, appItems[1].name.textContent);
+ assertTrue(appItems[1].icon.classList.contains('spinner'));
+});
+
+// Verify that enter key on 'kiosk-app-id-edit' adds an app.
+TEST_F('KioskAppSettingsWebUITest', 'testAddKioskApp', function() {
+ var testAppId = 'app_3';
+ var appIdInput = $('kiosk-app-id-edit');
+
+ appIdInput.value = testAppId;
+
+ this.mockHandler.expects(once()).addKioskApp([testAppId]);
+ var keypress = document.createEvent("KeyboardEvents");
+ keypress.initKeyboardEvent('keypress', true, true, null, 'Enter', '');
+ appIdInput.dispatchEvent(keypress);
+});
+
+// Test the row delete button.
+TEST_F('KioskAppSettingsWebUITest', 'testRemoveKioskApp', function() {
+ var appItem = $('kiosk-app-list').items[0];
+ var appId = appItem.data.id;
+
+ this.mockHandler.expects(once()).removeKioskApp([appId]);
+ appItem.querySelector('.row-delete-button').click();
+});
+
+// Test enable/disable auto launch buttons.
+TEST_F('KioskAppSettingsWebUITest', 'testEnableDisableAutoLaunch', function() {
+ var appItem = $('kiosk-app-list').items[0];
+ var appId = appItem.data.id;
+
+ var enableAutoLaunchCalled = false;
+ this.mockHandler.expects(once()).enableKioskAutoLaunch([appId]).
+ will(callFunction(function() {
+ enableAutoLaunchCalled = true;
+ }));
+ appItem.querySelector('.enable-auto-launch-button').click();
+ expectTrue(enableAutoLaunchCalled);
+
+ var disableAutoLaunchCalled = false;
+ this.mockHandler.expects(once()).disableKioskAutoLaunch([appId]).
+ will(callFunction(function() {
+ disableAutoLaunchCalled = true;
+ }));
+ appItem.querySelector('.disable-auto-launch-button').click();
+ expectTrue(disableAutoLaunchCalled);
+});
+
+// Verify that updateApp updates app info.
+TEST_F('KioskAppSettingsWebUITest', 'testUpdateApp', function() {
+ var appItems = $('kiosk-app-list').items;
+ assertEquals(appItems[1].data.id, 'app_2');
+ expectEquals(appItems[1].data.name, '');
+ expectTrue(appItems[1].icon.classList.contains('spinner'));
+ expectFalse(appItems[1].autoLaunch);
+
+ // New data changes name, autoLaunch and isLoading.
+ var newName = 'Name for App2';
+ var newApp2 = {
+ id: 'app_2',
+ name: newName,
+ iconURL: '',
+ autoLaunch: true,
+ isLoading: false,
+ };
+ KioskAppsOverlay.updateApp(newApp2);
+
+ assertEquals('app_2', appItems[1].data.id);
+ expectEquals(newName, appItems[1].data.name, newName);
+ expectEquals(newName, appItems[1].name.textContent);
+ expectFalse(appItems[1].icon.classList.contains('spinner'));
+ expectTrue(appItems[1].autoLaunch);
+});
+
+// Verify that showError makes error banner visible.
+TEST_F('KioskAppSettingsWebUITest', 'testShowError', function() {
+ KioskAppsOverlay.showError('A bad app');
+ expectTrue($('kiosk-apps-error-banner').classList.contains('visible'));
+});
diff --git a/chrome/browser/ui/webui/options/chromeos/kiosk_apps_handler.cc b/chrome/browser/ui/webui/options/chromeos/kiosk_apps_handler.cc
new file mode 100644
index 0000000..11a0a3c
--- /dev/null
+++ b/chrome/browser/ui/webui/options/chromeos/kiosk_apps_handler.cc
@@ -0,0 +1,236 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/options/chromeos/kiosk_apps_handler.h"
+
+#include <algorithm>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "base/string_util.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
+#include "chrome/common/extensions/extension.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_ui.h"
+#include "googleurl/src/gurl.h"
+#include "grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/webui/web_ui_util.h"
+
+namespace chromeos {
+namespace options {
+
+namespace {
+
+// Populates app info dictionary with |app_data|.
+void PopulateAppDict(const KioskAppManager::App& app_data,
+ base::DictionaryValue* app_dict) {
+ std::string icon_url("chrome://theme/IDR_APP_DEFAULT_ICON");
+
+ // TODO(xiyuan): Replace data url with a URLDataSource.
+ if (!app_data.icon.isNull())
+ icon_url = webui::GetBitmapDataUrl(*app_data.icon.bitmap());
+
+ app_dict->SetString("id", app_data.id);
+ app_dict->SetString("name", app_data.name);
+ app_dict->SetString("iconURL", icon_url);
+ app_dict->SetBoolean(
+ "autoLaunch",
+ KioskAppManager::Get()->GetAutoLaunchApp() == app_data.id);
+ app_dict->SetBoolean("isLoading", app_data.is_loading);
+}
+
+// Sanitize app id input value and extracts app id out of it.
+// Returns false if an app id could not be derived out of the input.
+bool ExtractsAppIdFromInput(const std::string& input,
+ std::string* app_id) {
+ if (extensions::Extension::IdIsValid(input)) {
+ *app_id = input;
+ return true;
+ }
+
+ GURL webstore_url = GURL(input);
+ if (!webstore_url.is_valid())
+ return false;
+
+ GURL webstore_base_url =
+ GURL(extension_urls::GetWebstoreItemDetailURLPrefix());
+
+ if (webstore_url.scheme() != webstore_base_url.scheme() ||
+ webstore_url.host() != webstore_base_url.host() ||
+ !StartsWithASCII(
+ webstore_url.path(), webstore_base_url.path(), true)) {
+ return false;
+ }
+
+ const std::string path = webstore_url.path();
+ const size_t last_slash = path.rfind('/');
+ if (last_slash == std::string::npos)
+ return false;
+
+ const std::string candidate_id = path.substr(last_slash + 1);
+ if (!extensions::Extension::IdIsValid(candidate_id))
+ return false;
+
+ *app_id = candidate_id;
+ return true;
+}
+
+} // namespace
+
+KioskAppsHandler::KioskAppsHandler()
+ : kiosk_app_manager_(KioskAppManager::Get()),
+ initialized_(false) {
+ kiosk_app_manager_->AddObserver(this);
+}
+
+KioskAppsHandler::~KioskAppsHandler() {
+ kiosk_app_manager_->RemoveObserver(this);
+}
+
+void KioskAppsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("getKioskApps",
+ base::Bind(&KioskAppsHandler::HandleGetKioskApps,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("addKioskApp",
+ base::Bind(&KioskAppsHandler::HandleAddKioskApp,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("removeKioskApp",
+ base::Bind(&KioskAppsHandler::HandleRemoveKioskApp,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("enableKioskAutoLaunch",
+ base::Bind(&KioskAppsHandler::HandleEnableKioskAutoLaunch,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("disableKioskAutoLaunch",
+ base::Bind(&KioskAppsHandler::HandleDisableKioskAutoLaunch,
+ base::Unretained(this)));
+}
+
+void KioskAppsHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ RegisterTitle(localized_strings,
+ "kioskOverlayTitle",
+ IDS_OPTIONS_KIOSK_OVERLAY_TITLE);
+
+ localized_strings->SetString(
+ "addKioskApp",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_ADD_APP));
+ localized_strings->SetString(
+ "kioskAppIdEditHint",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_ADD_APP_HINT));
+ localized_strings->SetString(
+ "enableAutoLaunchButton",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_ENABLE_AUTO_LAUNCH));
+ localized_strings->SetString(
+ "disableAutoLaunchButton",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_DISABLE_AUTO_LAUNCH));
+ localized_strings->SetString(
+ "autoLaunch",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_AUTO_LAUNCH));
+ localized_strings->SetString(
+ "invalidApp",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_INVALID_APP));
+}
+
+void KioskAppsHandler::OnKioskAutoLaunchAppChanged() {
+ SendKioskApps();
+}
+
+void KioskAppsHandler::OnKioskAppsChanged() {
+ SendKioskApps();
+}
+
+void KioskAppsHandler::OnKioskAppDataChanged(const std::string& app_id) {
+ KioskAppManager::App app_data;
+ if (!kiosk_app_manager_->GetApp(app_id, &app_data))
+ return;
+
+ base::DictionaryValue app_dict;
+ PopulateAppDict(app_data, &app_dict);
+
+ web_ui()->CallJavascriptFunction("options.KioskAppsOverlay.updateApp",
+ app_dict);
+}
+
+void KioskAppsHandler::OnKioskAppDataLoadFailure(const std::string& app_id) {
+ base::StringValue app_id_value(app_id);
+ web_ui()->CallJavascriptFunction("options.KioskAppsOverlay.showError",
+ app_id_value);
+}
+
+void KioskAppsHandler::SendKioskApps() {
+ if (!initialized_)
+ return;
+
+ KioskAppManager::Apps apps;
+ kiosk_app_manager_->GetApps(&apps);
+
+ base::ListValue apps_list;
+ for (size_t i = 0; i < apps.size(); ++i) {
+ const KioskAppManager::App& app_data = apps[i];
+
+ scoped_ptr<base::DictionaryValue> app_info(new base::DictionaryValue);
+ PopulateAppDict(app_data, app_info.get());
+ apps_list.Append(app_info.release());
+ }
+
+ web_ui()->CallJavascriptFunction("options.KioskAppsOverlay.setApps",
+ apps_list);
+}
+
+void KioskAppsHandler::HandleGetKioskApps(const base::ListValue* args) {
+ initialized_ = true;
+ SendKioskApps();
+}
+
+void KioskAppsHandler::HandleAddKioskApp(const base::ListValue* args) {
+ std::string input;
+ CHECK(args->GetString(0, &input));
+
+ std::string app_id;
+ if (!ExtractsAppIdFromInput(input, &app_id)) {
+ OnKioskAppDataLoadFailure(input);
+ return;
+ }
+
+ kiosk_app_manager_->AddApp(app_id);
+}
+
+void KioskAppsHandler::HandleRemoveKioskApp(const base::ListValue* args) {
+ std::string app_id;
+ CHECK(args->GetString(0, &app_id));
+
+ kiosk_app_manager_->RemoveApp(app_id);
+}
+
+void KioskAppsHandler::HandleEnableKioskAutoLaunch(
+ const base::ListValue* args) {
+ std::string app_id;
+ CHECK(args->GetString(0, &app_id));
+
+ kiosk_app_manager_->SetAutoLaunchApp(app_id);
+}
+
+void KioskAppsHandler::HandleDisableKioskAutoLaunch(
+ const base::ListValue* args) {
+ std::string app_id;
+ CHECK(args->GetString(0, &app_id));
+
+ std::string startup_app_id = kiosk_app_manager_->GetAutoLaunchApp();
+ if (startup_app_id != app_id)
+ return;
+
+ kiosk_app_manager_->SetAutoLaunchApp("");
+}
+
+} // namespace options
+} // namespace chromeos
diff --git a/chrome/browser/ui/webui/options/chromeos/kiosk_apps_handler.h b/chrome/browser/ui/webui/options/chromeos/kiosk_apps_handler.h
new file mode 100644
index 0000000..8c2ae65
--- /dev/null
+++ b/chrome/browser/ui/webui/options/chromeos/kiosk_apps_handler.h
@@ -0,0 +1,54 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_KIOSK_APPS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_KIOSK_APPS_HANDLER_H_
+
+#include "chrome/browser/chromeos/app_mode/kiosk_app_manager_observer.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+
+namespace chromeos {
+
+class KioskAppManager;
+
+namespace options {
+
+class KioskAppsHandler : public ::options::OptionsPageUIHandler,
+ public KioskAppManagerObserver {
+ public:
+ KioskAppsHandler();
+ virtual ~KioskAppsHandler();
+
+ // options::OptionsPageUIHandler overrides:
+ virtual void RegisterMessages() OVERRIDE;
+ virtual void GetLocalizedValues(
+ base::DictionaryValue* localized_strings) OVERRIDE;
+
+ // KioskAppPrefsObserver overrides:
+ virtual void OnKioskAutoLaunchAppChanged() OVERRIDE;
+ virtual void OnKioskAppsChanged() OVERRIDE;
+ virtual void OnKioskAppDataChanged(const std::string& app_id) OVERRIDE;
+ virtual void OnKioskAppDataLoadFailure(const std::string& app_id) OVERRIDE;
+
+ private:
+ // Sends all kiosk apps to webui.
+ void SendKioskApps();
+
+ // JS callbacks.
+ void HandleGetKioskApps(const base::ListValue* args);
+ void HandleAddKioskApp(const base::ListValue* args);
+ void HandleRemoveKioskApp(const base::ListValue* args);
+ void HandleEnableKioskAutoLaunch(const base::ListValue* args);
+ void HandleDisableKioskAutoLaunch(const base::ListValue* args);
+
+ KioskAppManager* kiosk_app_manager_; // not owned.
+ bool initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(KioskAppsHandler);
+};
+
+} // namespace options
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_KIOSK_APPS_HANDLER_H_
diff --git a/chrome/browser/ui/webui/options/options_ui.cc b/chrome/browser/ui/webui/options/options_ui.cc
index c9a6203..fcc1a00 100644
--- a/chrome/browser/ui/webui/options/options_ui.cc
+++ b/chrome/browser/ui/webui/options/options_ui.cc
@@ -73,6 +73,7 @@
#include "chrome/browser/ui/webui/options/chromeos/display_overscan_handler.h"
#include "chrome/browser/ui/webui/options/chromeos/internet_options_handler.h"
#include "chrome/browser/ui/webui/options/chromeos/keyboard_handler.h"
+#include "chrome/browser/ui/webui/options/chromeos/kiosk_apps_handler.h"
#include "chrome/browser/ui/webui/options/chromeos/language_chewing_handler.h"
#include "chrome/browser/ui/webui/options/chromeos/language_hangul_handler.h"
#include "chrome/browser/ui/webui/options/chromeos/language_mozc_handler.h"
@@ -290,6 +291,8 @@ OptionsUI::OptionsUI(content::WebUI* web_ui)
AddOptionsPageUIHandler(localized_strings,
new chromeos::options::KeyboardHandler());
AddOptionsPageUIHandler(localized_strings,
+ new chromeos::options::KioskAppsHandler());
+ AddOptionsPageUIHandler(localized_strings,
new chromeos::options::LanguageHangulHandler());
AddOptionsPageUIHandler(localized_strings,
new chromeos::options::LanguageMozcHandler());
diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi
index 37ff1cf..9623381 100644
--- a/chrome/chrome_browser_ui.gypi
+++ b/chrome/chrome_browser_ui.gypi
@@ -2028,6 +2028,8 @@
'browser/ui/webui/options/chromeos/internet_options_handler.h',
'browser/ui/webui/options/chromeos/keyboard_handler.cc',
'browser/ui/webui/options/chromeos/keyboard_handler.h',
+ 'browser/ui/webui/options/chromeos/kiosk_apps_handler.cc',
+ 'browser/ui/webui/options/chromeos/kiosk_apps_handler.h',
'browser/ui/webui/options/chromeos/language_chewing_handler.cc',
'browser/ui/webui/options/chromeos/language_chewing_handler.h',
'browser/ui/webui/options/chromeos/language_hangul_handler.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 80e02a9..1aae04d 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1390,6 +1390,7 @@
'browser/ui/webui/options/browser_options_browsertest.js',
'browser/ui/webui/options/certificate_manager_browsertest.js',
'browser/ui/webui/options/chromeos/guest_mode_options_ui_browsertest.cc',
+ 'browser/ui/webui/options/chromeos/kiosk_apps_browsertest.js',
'browser/ui/webui/options/content_options_browsertest.js',
'browser/ui/webui/options/content_settings_exception_area_browsertest.js',
'browser/ui/webui/options/cookies_view_browsertest.js',