summaryrefslogtreecommitdiff
path: root/chromium/ui/webui/resources
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/ui/webui/resources')
-rw-r--r--chromium/ui/webui/resources/PRESUBMIT.py5
-rw-r--r--chromium/ui/webui/resources/cr_components/OWNERS2
-rw-r--r--chromium/ui/webui/resources/cr_components/README.md12
-rw-r--r--chromium/ui/webui/resources/cr_components/certificate_manager/ca_trust_edit_dialog.html57
-rw-r--r--chromium/ui/webui/resources/cr_components/certificate_manager/ca_trust_edit_dialog.js80
-rw-r--r--chromium/ui/webui/resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.html30
-rw-r--r--chromium/ui/webui/resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.js97
-rw-r--r--chromium/ui/webui/resources/cr_components/certificate_manager/certificate_entry.html39
-rw-r--r--chromium/ui/webui/resources/cr_components/certificate_manager/certificate_entry.js29
-rw-r--r--chromium/ui/webui/resources/cr_components/certificate_manager/certificate_list.html41
-rw-r--r--chromium/ui/webui/resources/cr_components/certificate_manager/certificate_list.js158
-rw-r--r--chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager.html106
-rw-r--r--chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager.js189
-rw-r--r--chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager_types.html1
-rw-r--r--chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager_types.js44
-rw-r--r--chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_decryption_dialog.html34
-rw-r--r--chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_decryption_dialog.js53
-rw-r--r--chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_encryption_dialog.html44
-rw-r--r--chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_encryption_dialog.js69
-rw-r--r--chromium/ui/webui/resources/cr_components/certificate_manager/certificate_shared_css.html35
-rw-r--r--chromium/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.html63
-rw-r--r--chromium/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.js150
-rw-r--r--chromium/ui/webui/resources/cr_components/certificate_manager/certificates_browser_proxy.html1
-rw-r--r--chromium/ui/webui/resources/cr_components/certificate_manager/certificates_browser_proxy.js271
-rw-r--r--chromium/ui/webui/resources/cr_components/certificate_manager/certificates_error_dialog.html29
-rw-r--r--chromium/ui/webui/resources/cr_components/certificate_manager/certificates_error_dialog.js38
-rw-r--r--chromium/ui/webui/resources/cr_components/certificate_manager/compiled_resources2.gyp125
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.html137
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.js459
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/compiled_resources2.gyp30
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/compiled_resources2.gyp63
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.html43
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.js271
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.html30
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js214
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_nameservers.html43
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_nameservers.js214
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list.html71
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list.js214
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy.html154
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy.js428
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.html37
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.js46
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.html50
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.js57
-rw-r--r--chromium/ui/webui/resources/cr_components/chromeos/network/network_shared_css.html51
-rw-r--r--chromium/ui/webui/resources/cr_components/compiled_resources2.gyp15
-rw-r--r--chromium/ui/webui/resources/cr_components_resources.grdp131
-rw-r--r--chromium/ui/webui/resources/cr_elements/OWNERS1
-rw-r--r--chromium/ui/webui/resources/cr_elements/READE.md16
-rw-r--r--chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.html2
-rw-r--r--chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js1
-rw-r--r--chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.js4
-rw-r--r--chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.js39
-rw-r--r--chromium/ui/webui/resources/cr_elements/chromeos/network/cr_onc_types.js1
-rw-r--r--chromium/ui/webui/resources/cr_elements/compiled_resources2.gyp1
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html8
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.js25
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.html9
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html4
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_icons_css.html19
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_link_row/compiled_resources2.gyp (renamed from chromium/ui/webui/resources/js/chromeos/compiled_resources2.gyp)8
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.html74
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.js27
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.html2
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.js4
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_scrollable_behavior.js9
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_behavior.js3
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html17
-rw-r--r--chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html1
-rw-r--r--chromium/ui/webui/resources/cr_elements/icons.html3
-rw-r--r--chromium/ui/webui/resources/cr_elements/paper_checkbox_style_css.html19
-rw-r--r--chromium/ui/webui/resources/cr_elements/paper_toggle_style_css.html42
-rw-r--r--chromium/ui/webui/resources/cr_elements/policy/compiled_resources2.gyp5
-rw-r--r--chromium/ui/webui/resources/cr_elements/policy/cr_policy_indicator.html29
-rw-r--r--chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_indicator.html28
-rw-r--r--chromium/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.html29
-rw-r--r--chromium/ui/webui/resources/cr_elements/policy/cr_policy_vars_css.html17
-rw-r--r--chromium/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.html34
-rw-r--r--chromium/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.js12
-rw-r--r--chromium/ui/webui/resources/cr_elements/shared_vars_css.html33
-rw-r--r--chromium/ui/webui/resources/cr_elements_images.grdp6
-rw-r--r--chromium/ui/webui/resources/cr_elements_resources.grdp19
-rw-r--r--chromium/ui/webui/resources/css/chrome_shared.css3
-rw-r--r--chromium/ui/webui/resources/css/chromeos/ui_account_tweaks.css14
-rw-r--r--chromium/ui/webui/resources/css/widgets.css2
-rw-r--r--chromium/ui/webui/resources/html/chromeos/ui_account_tweaks.html1
-rw-r--r--chromium/ui/webui/resources/images/icon_arrow_back.svg1
-rw-r--r--chromium/ui/webui/resources/images/icon_menu_white.svg1
-rw-r--r--chromium/ui/webui/resources/images/icon_refresh.svg1
-rw-r--r--chromium/ui/webui/resources/images/info.svg4
-rw-r--r--chromium/ui/webui/resources/js/chromeos/ui_account_tweaks.js178
-rw-r--r--chromium/ui/webui/resources/js/cr/ui/menu.js1
-rw-r--r--chromium/ui/webui/resources/webui_resources.grd12
94 files changed, 4996 insertions, 333 deletions
diff --git a/chromium/ui/webui/resources/PRESUBMIT.py b/chromium/ui/webui/resources/PRESUBMIT.py
index 2b24710521a..26bc5ec895e 100644
--- a/chromium/ui/webui/resources/PRESUBMIT.py
+++ b/chromium/ui/webui/resources/PRESUBMIT.py
@@ -28,9 +28,12 @@ def _CheckForTranslations(input_api, output_api):
for f in input_api.AffectedFiles():
local_path = f.LocalPath()
+ # Allow translation in i18n_behavior.js.
if local_path.endswith('i18n_behavior.js'):
continue
-
+ # Allow translation in the cr_components directory.
+ if 'cr_components' in local_path:
+ continue
keywords = None
if local_path.endswith('.js'):
keywords = js_keywords
diff --git a/chromium/ui/webui/resources/cr_components/OWNERS b/chromium/ui/webui/resources/cr_components/OWNERS
new file mode 100644
index 00000000000..ae9d7fbf6f8
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/OWNERS
@@ -0,0 +1,2 @@
+stevenjb@chromium.org
+dpapad@chromium.org
diff --git a/chromium/ui/webui/resources/cr_components/README.md b/chromium/ui/webui/resources/cr_components/README.md
new file mode 100644
index 00000000000..a4ea0138a7d
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/README.md
@@ -0,0 +1,12 @@
+This directory contains complex Polymer web components for Web UI. They may be
+shared between Settings, login, stand alone dialogs, etc.
+
+These components are allowed to use I18nBehavior. The Web UI hosting these
+components is expected to provide loadTimeData with any necessary strings.
+TODO(stevenjb/dschuyler): Add support for i18n{} substitution.
+
+These components may also use chrome and extension APIs, e.g. chrome.send
+(through a browser proxy) or chrome.settingsPrivate. The C++ code hosting the
+component is expected to handle these calls.
+
+For simpler components with no I18n or chrome dependencies, see cr_elements.
diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/ca_trust_edit_dialog.html b/chromium/ui/webui/resources/cr_components/certificate_manager/ca_trust_edit_dialog.html
new file mode 100644
index 00000000000..82be73ab885
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/certificate_manager/ca_trust_edit_dialog.html
@@ -0,0 +1,57 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
+<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
+<link rel="import" href="chrome://resources/cr_elements/paper_checkbox_style_css.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner.html">
+<link rel="import" href="certificate_shared_css.html">
+<link rel="import" href="certificates_browser_proxy.html">
+
+<dom-module id="ca-trust-edit-dialog">
+ <template>
+ <style include="certificate-shared paper-button-style paper-checkbox-style">
+ paper-checkbox {
+ display: block;
+ }
+
+ paper-checkbox,
+ #description {
+ margin: 15px 0;
+ }
+ </style>
+
+ <dialog is="cr-dialog" id="dialog" close-text="[[i18n('close')]]">
+ <div slot="title">
+ [[i18n('certificateManagerCaTrustEditDialogTitle')]]
+ </div>
+ <div slot="body">
+ <div>[[explanationText_]]</div>
+ <div id="description">
+ [[i18n('certificateManagerCaTrustEditDialogDescription')]]
+ </div>
+ <paper-checkbox id="ssl" checked="[[trustInfo_.ssl]]">
+ [[i18n('certificateManagerCaTrustEditDialogSsl')]]
+ </paper-checkbox>
+ <paper-checkbox id="email" checked="[[trustInfo_.email]]">
+ [[i18n('certificateManagerCaTrustEditDialogEmail')]]
+ </paper-checkbox>
+ <paper-checkbox id="objSign" checked="[[trustInfo_.objSign]]">
+ [[i18n('certificateManagerCaTrustEditDialogObjSign')]]
+ </paper-checkbox>
+ </div>
+ <div slot="button-container">
+ <paper-spinner id="spinner"></paper-spinner>
+ <paper-button class="cancel-button" on-tap="onCancelTap_">
+ [[i18n('cancel')]]
+ </paper-button>
+ <paper-button id="ok" class="action-button" on-tap="onOkTap_">
+ [[i18n('ok')]]
+ </paper-button>
+ </div>
+ </dialog>
+ </template>
+ <script src="ca_trust_edit_dialog.js"></script>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/ca_trust_edit_dialog.js b/chromium/ui/webui/resources/cr_components/certificate_manager/ca_trust_edit_dialog.js
new file mode 100644
index 00000000000..c33d5558efa
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/certificate_manager/ca_trust_edit_dialog.js
@@ -0,0 +1,80 @@
+// Copyright 2016 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.
+
+/**
+ * @fileoverview 'ca-trust-edit-dialog' allows the user to:
+ * - specify the trust level of a certificate authority that is being
+ * imported.
+ * - edit the trust level of an already existing certificate authority.
+ */
+Polymer({
+ is: 'ca-trust-edit-dialog',
+
+ behaviors: [I18nBehavior],
+
+ properties: {
+ /** @type {!CertificateSubnode|!NewCertificateSubNode} */
+ model: Object,
+
+ /** @private {?CaTrustInfo} */
+ trustInfo_: Object,
+
+ /** @private {string} */
+ explanationText_: String,
+ },
+
+ /** @private {?certificate_manager.CertificatesBrowserProxy} */
+ browserProxy_: null,
+
+ /** @override */
+ ready: function() {
+ this.browserProxy_ =
+ certificate_manager.CertificatesBrowserProxyImpl.getInstance();
+ },
+
+ /** @override */
+ attached: function() {
+ this.explanationText_ = loadTimeData.getStringF(
+ 'certificateManagerCaTrustEditDialogExplanation', this.model.name);
+
+ // A non existing |model.id| indicates that a new certificate is being
+ // imported, otherwise an existing certificate is being edited.
+ if (this.model.id) {
+ this.browserProxy_.getCaCertificateTrust(this.model.id)
+ .then(trustInfo => {
+ this.trustInfo_ = trustInfo;
+ this.$.dialog.showModal();
+ });
+ } else {
+ /** @type {!CrDialogElement} */ (this.$.dialog).showModal();
+ }
+ },
+
+ /** @private */
+ onCancelTap_: function() {
+ /** @type {!CrDialogElement} */ (this.$.dialog).close();
+ },
+
+ /** @private */
+ onOkTap_: function() {
+ this.$.spinner.active = true;
+
+ var whenDone = this.model.id ?
+ this.browserProxy_.editCaCertificateTrust(
+ this.model.id, this.$.ssl.checked, this.$.email.checked,
+ this.$.objSign.checked) :
+ this.browserProxy_.importCaCertificateTrustSelected(
+ this.$.ssl.checked, this.$.email.checked, this.$.objSign.checked);
+
+ whenDone.then(
+ () => {
+ this.$.spinner.active = false;
+ /** @type {!CrDialogElement} */ (this.$.dialog).close();
+ },
+ error => {
+ /** @type {!CrDialogElement} */ (this.$.dialog).close();
+ this.fire('certificates-error', {error: error, anchor: null});
+ });
+ },
+});
diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.html b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.html
new file mode 100644
index 00000000000..fbf253c3a5d
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.html
@@ -0,0 +1,30 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
+<link rel="import" href="certificate_shared_css.html">
+<link rel="import" href="certificates_browser_proxy.html">
+
+<dom-module id="certificate-delete-confirmation-dialog">
+ <template>
+ <style include="certificate-shared"></style>
+ <dialog is="cr-dialog" id="dialog" close-text="[[i18n('close')]]">
+ <div slot="title">
+ [[getTitleText_(model, certificateType)]]
+ </div>
+ <div slot="body">
+ <div>[[getDescriptionText_(model, certificateType)]]</div>
+ </div>
+ <div slot="button-container">
+ <paper-button class="cancel-button" on-tap="onCancelTap_">
+ [[i18n('cancel')]]
+ </paper-button>
+ <paper-button id="ok" class="action-button" on-tap="onOkTap_">
+ [[i18n('ok')]]
+ </paper-button>
+ </div>
+ </dialog>
+ </template>
+ <script src="certificate_delete_confirmation_dialog.js"></script>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.js b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.js
new file mode 100644
index 00000000000..f914b0865ad
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.js
@@ -0,0 +1,97 @@
+// Copyright 2016 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.
+
+/**
+ * @fileoverview A confirmation dialog allowing the user to delete various types
+ * of certificates.
+ */
+Polymer({
+ is: 'certificate-delete-confirmation-dialog',
+
+ behaviors: [I18nBehavior],
+
+ properties: {
+ /** @type {!CertificateSubnode} */
+ model: Object,
+
+ /** @type {!CertificateType} */
+ certificateType: String,
+ },
+
+ /** @private {?certificate_manager.CertificatesBrowserProxy} */
+ browserProxy_: null,
+
+ /** @override */
+ ready: function() {
+ this.browserProxy_ =
+ certificate_manager.CertificatesBrowserProxyImpl.getInstance();
+ },
+
+ /** @override */
+ attached: function() {
+ /** @type {!CrDialogElement} */ (this.$.dialog).showModal();
+ },
+
+ /**
+ * @private
+ * @return {string}
+ */
+ getTitleText_: function() {
+ /**
+ * @param {string} localizedMessageId
+ * @return {string}
+ */
+ var getString = localizedMessageId =>
+ loadTimeData.getStringF(localizedMessageId, this.model.name);
+
+ switch (this.certificateType) {
+ case CertificateType.PERSONAL:
+ return getString('certificateManagerDeleteUserTitle');
+ case CertificateType.SERVER:
+ return getString('certificateManagerDeleteServerTitle');
+ case CertificateType.CA:
+ return getString('certificateManagerDeleteCaTitle');
+ case CertificateType.OTHER:
+ return getString('certificateManagerDeleteOtherTitle');
+ }
+ assertNotReached();
+ },
+
+ /**
+ * @private
+ * @return {string}
+ */
+ getDescriptionText_: function() {
+ var getString = loadTimeData.getString.bind(loadTimeData);
+ switch (this.certificateType) {
+ case CertificateType.PERSONAL:
+ return getString('certificateManagerDeleteUserDescription');
+ case CertificateType.SERVER:
+ return getString('certificateManagerDeleteServerDescription');
+ case CertificateType.CA:
+ return getString('certificateManagerDeleteCaDescription');
+ case CertificateType.OTHER:
+ return '';
+ }
+ assertNotReached();
+ },
+
+ /** @private */
+ onCancelTap_: function() {
+ /** @type {!CrDialogElement} */ (this.$.dialog).close();
+ },
+
+ /** @private */
+ onOkTap_: function() {
+ this.browserProxy_.deleteCertificate(this.model.id)
+ .then(
+ () => {
+ /** @type {!CrDialogElement} */ (this.$.dialog).close();
+ },
+ error => {
+ /** @type {!CrDialogElement} */ (this.$.dialog).close();
+ this.fire('certificates-error', {error: error, anchor: null});
+ });
+ },
+});
diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_entry.html b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_entry.html
new file mode 100644
index 00000000000..8dcb44091d0
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_entry.html
@@ -0,0 +1,39 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_expand_button/cr_expand_button.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
+<link rel="import" href="certificate_shared_css.html">
+<link rel="import" href="certificate_subentry.html">
+<link rel="import" href="certificates_browser_proxy.html">
+
+<dom-module id="certificate-entry">
+ <template>
+ <style include="certificate-shared iron-flex">
+ .expand-box {
+ align-items: center;
+ border-top: var(--cr-separator-line);
+ display: flex;
+ min-height: 48px;
+ padding: 0 20px;
+ }
+ </style>
+ <div class="expand-box">
+ <div class="flex">[[model.id]]</div>
+ <cr-expand-button expanded="{{expanded_}}"
+ alt="[[i18n('certificateManagerExpandA11yLabel')]]">
+ </cr-expand-button>
+ </div>
+ <template is="dom-if" if="[[expanded_]]">
+ <div class="list-frame">
+ <template is="dom-repeat" items="[[model.subnodes]]">
+ <certificate-subentry model="[[item]]"
+ certificate-type="[[certificateType]]"
+ is-last$="[[isLast_(index, model)]]">
+ </certificate-subentry>
+ </template>
+ </div>
+ </template>
+ </template>
+ <script src="certificate_entry.js"></script>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_entry.js b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_entry.js
new file mode 100644
index 00000000000..bcd900e48c1
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_entry.js
@@ -0,0 +1,29 @@
+// Copyright 2016 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.
+
+/**
+ * @fileoverview An element that represents an SSL certificate entry.
+ */
+Polymer({
+ is: 'certificate-entry',
+
+ behaviors: [I18nBehavior],
+
+ properties: {
+ /** @type {!Certificate} */
+ model: Object,
+
+ /** @type {!CertificateType} */
+ certificateType: String,
+ },
+
+ /**
+ * @param {number} index
+ * @return {boolean} Whether the given index corresponds to the last sub-node.
+ * @private
+ */
+ isLast_: function(index) {
+ return index == this.model.subnodes.length - 1;
+ },
+});
diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_list.html b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_list.html
new file mode 100644
index 00000000000..55396b5f371
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_list.html
@@ -0,0 +1,41 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/html/assert.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
+<link rel="import" href="certificate_entry.html">
+<link rel="import" href="certificate_manager_types.html">
+<link rel="import" href="certificate_shared_css.html">
+<link rel="import" href="certificates_browser_proxy.html">
+
+<dom-module id="certificate-list">
+ <template>
+ <style include="certificate-shared iron-flex">
+ .button-box {
+ align-items: center;
+ display: flex;
+ margin-bottom: 24px;
+ min-height: 48px;
+ padding: 0 20px;
+ }
+ </style>
+ <div class="button-box">
+ <span class="flex">
+ [[getDescription_(certificateType, certificates)]]</span>
+ <paper-button id="import" on-tap="onImportTap_"
+ hidden="[[!canImport_(certificateType)]]">
+ [[i18n('certificateManagerImport')]]</paper-button>
+<if expr="chromeos">
+ <paper-button id="importAndBind" on-tap="onImportAndBindTap_"
+ hidden="[[!canImportAndBind_(certificateType, isGuest_)]]">
+ [[i18n('certificateManagerImportAndBind')]]</paper-button>
+</if>
+ </div>
+ <template is="dom-repeat" items="[[certificates]]">
+ <certificate-entry model="[[item]]"
+ certificate-type="[[certificateType]]">
+ </certificate-entry>
+ </template>
+ </template>
+ <script src="certificate_list.js"></script>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_list.js b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_list.js
new file mode 100644
index 00000000000..d24361fdeee
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_list.js
@@ -0,0 +1,158 @@
+// Copyright 2016 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.
+
+/**
+ * @fileoverview 'certificate-list' is an element that displays a list of
+ * certificates.
+ */
+Polymer({
+ is: 'certificate-list',
+
+ properties: {
+ /** @type {!Array<!Certificate>} */
+ certificates: {
+ type: Array,
+ value: function() {
+ return [];
+ },
+ },
+
+ /** @type {!CertificateType} */
+ certificateType: String,
+
+ // 'if expr="chromeos"' here is breaking vulcanize. TODO(stevenjb/dpapad):
+ // Restore after migrating to polymer-bundler, crbug.com/731881.
+ /** @private */
+ isGuest_: {
+ type: Boolean,
+ value: function() {
+ return loadTimeData.valueExists('isGuest') &&
+ loadTimeData.getBoolean('isGuest');
+ },
+ },
+ },
+
+ behaviors: [I18nBehavior],
+
+ /**
+ * @return {string}
+ * @private
+ */
+ getDescription_: function() {
+ if (this.certificates.length == 0)
+ return this.i18n('certificateManagerNoCertificates');
+
+ switch (this.certificateType) {
+ case CertificateType.PERSONAL:
+ return this.i18n('certificateManagerYourCertificatesDescription');
+ case CertificateType.SERVER:
+ return this.i18n('certificateManagerServersDescription');
+ case CertificateType.CA:
+ return this.i18n('certificateManagerAuthoritiesDescription');
+ case CertificateType.OTHER:
+ return this.i18n('certificateManagerOthersDescription');
+ }
+
+ assertNotReached();
+ },
+
+ /**
+ * @return {boolean}
+ * @private
+ */
+ canImport_: function() {
+ return this.certificateType != CertificateType.OTHER;
+ },
+
+ // <if expr="chromeos">
+ /**
+ * @return {boolean}
+ * @private
+ */
+ canImportAndBind_: function() {
+ return !this.isGuest_ && this.certificateType == CertificateType.PERSONAL;
+ },
+ // </if>
+
+ /**
+ * Handles a rejected Promise returned from |browserProxy_|.
+ * @param {!HTMLElement} anchor
+ * @param {*} error Expects {!CertificatesError|!CertificatesImportError}.
+ * @private
+ */
+ onRejected_: function(anchor, error) {
+ if (error === null) {
+ // Nothing to do here. Null indicates that the user clicked "cancel" on
+ // a native file chooser dialog.
+ return;
+ }
+
+ // Otherwise propagate the error to the parents, such that a dialog
+ // displaying the error will be shown.
+ this.fire('certificates-error', {error: error, anchor: anchor});
+ },
+
+
+ /**
+ * @param {?NewCertificateSubNode} subnode
+ * @param {!HTMLElement} anchor
+ * @private
+ */
+ dispatchImportActionEvent_: function(subnode, anchor) {
+ this.fire(
+ CertificateActionEvent,
+ /** @type {!CertificateActionEventDetail} */ ({
+ action: CertificateAction.IMPORT,
+ subnode: subnode,
+ certificateType: this.certificateType,
+ anchor: anchor,
+ }));
+ },
+
+ /**
+ * @param {!Event} e
+ * @private
+ */
+ onImportTap_: function(e) {
+ this.handleImport_(
+ false, /** @type {!HTMLElement} */ (Polymer.dom(e).localTarget));
+ },
+
+ // <if expr="chromeos">
+ /**
+ * @private
+ * @param {!Event} e
+ */
+ onImportAndBindTap_: function(e) {
+ this.handleImport_(
+ true, /** @type {!HTMLElement} */ (Polymer.dom(e).localTarget));
+ },
+ // </if>
+
+ /**
+ * @param {boolean} useHardwareBacked
+ * @param {!HTMLElement} anchor
+ * @private
+ */
+ handleImport_: function(useHardwareBacked, anchor) {
+ var browserProxy =
+ certificate_manager.CertificatesBrowserProxyImpl.getInstance();
+ if (this.certificateType == CertificateType.PERSONAL) {
+ browserProxy.importPersonalCertificate(useHardwareBacked)
+ .then(showPasswordPrompt => {
+ if (showPasswordPrompt)
+ this.dispatchImportActionEvent_(null, anchor);
+ }, this.onRejected_.bind(this, anchor));
+ } else if (this.certificateType == CertificateType.CA) {
+ browserProxy.importCaCertificate().then(certificateName => {
+ this.dispatchImportActionEvent_({name: certificateName}, anchor);
+ }, this.onRejected_.bind(this, anchor));
+ } else if (this.certificateType == CertificateType.SERVER) {
+ browserProxy.importServerCertificate().catch(
+ this.onRejected_.bind(this, anchor));
+ } else {
+ assertNotReached();
+ }
+ },
+});
diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager.html b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager.html
new file mode 100644
index 00000000000..9c9ebcb86c3
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager.html
@@ -0,0 +1,106 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/html/assert.html">
+<link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-pages/iron-pages.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-tabs/paper-tabs.html">
+<link rel="import" href="ca_trust_edit_dialog.html">
+<link rel="import" href="certificate_delete_confirmation_dialog.html">
+<link rel="import" href="certificate_list.html">
+<link rel="import" href="certificate_manager_types.html">
+<link rel="import" href="certificate_password_decryption_dialog.html">
+<link rel="import" href="certificate_password_encryption_dialog.html">
+<link rel="import" href="certificates_browser_proxy.html">
+<link rel="import" href="certificates_error_dialog.html">
+
+<dom-module id="certificate-manager">
+ <template>
+ <style>
+ :host {
+ --paper-tabs-selection-bar-color: var(--paper-blue-500);
+ }
+
+ paper-tabs {
+ font-size: inherit;
+ height: 40px;
+ margin-bottom: 24px;
+ }
+
+ paper-tab {
+ --paper-tab-content: {
+ color: var(--paper-grey-800);
+ };
+ --paper-tab-content-unselected: {
+ color: var(--paper-grey-600);
+ };
+ text-transform: uppercase;
+ }
+ </style>
+
+ <template is="dom-if" if="[[showCaTrustEditDialog_]]" restamp>
+ <ca-trust-edit-dialog model="[[dialogModel_]]">
+ </ca-trust-edit-dialog>
+ </template>
+ <template is="dom-if" if="[[showDeleteConfirmationDialog_]]" restamp>
+ <certificate-delete-confirmation-dialog
+ model="[[dialogModel_]]"
+ certificate-type="[[dialogModelCertificateType_]]">
+ </certificate-delete-confirmation-dialog>
+ </template>
+ <template is="dom-if" if="[[showPasswordEncryptionDialog_]]" restamp>
+ <certificate-password-encryption-dialog
+ model="[[dialogModel_]]">
+ </certificate-password-encryption-dialog>
+ </template>
+ <template is="dom-if" if="[[showPasswordDecryptionDialog_]]" restamp>
+ <certificate-password-decryption-dialog>
+ </certificate-password-decryption-dialog>
+ </template>
+ <template is="dom-if" if="[[showErrorDialog_]]" restamp>
+ <certificates-error-dialog model="[[errorDialogModel_]]">
+ </certificates-error-dialog>
+ </template>
+
+ <paper-tabs noink selected="{{selected}}">
+ <paper-tab>[[i18n('certificateManagerYourCertificates')]]</paper-tab>
+ <paper-tab>[[i18n('certificateManagerServers')]]</paper-tab>
+ <paper-tab>[[i18n('certificateManagerAuthorities')]]</paper-tab>
+ <paper-tab>[[i18n('certificateManagerOthers')]]</paper-tab>
+ </paper-tabs>
+ <iron-pages selected="[[selected]]">
+ <div>
+ <certificate-list id="personalCerts"
+ certificates="[[personalCerts]]"
+ certificate-type="[[certificateTypeEnum_.PERSONAL]]">
+ </certificate-list>
+ </div>
+ <div>
+ <template is="dom-if" if="[[isTabSelected_(selected, 1)]]">
+ <certificate-list id="serverCerts"
+ certificates="[[serverCerts]]"
+ certificate-type="[[certificateTypeEnum_.SERVER]]">
+ </certificate-list>
+ </template>
+ </div>
+ <div>
+ <template is="dom-if" if="[[isTabSelected_(selected, 2)]]">
+ <certificate-list id="caCerts"
+ certificates="[[caCerts]]"
+ certificate-type="[[certificateTypeEnum_.CA]]">
+ </certificate-list>
+ </template>
+ </div>
+ <div>
+ <template is="dom-if" if="[[isTabSelected_(selected, 3)]]">
+ <certificate-list id="otherCerts"
+ certificates="[[otherCerts]]"
+ certificate-type="[[certificateTypeEnum_.OTHER]]">
+ </certificate-list>
+ </template>
+ </div>
+ </iron-pages>
+ </template>
+ <script src="certificate_manager.js"></script>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager.js b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager.js
new file mode 100644
index 00000000000..0b1049d56df
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager.js
@@ -0,0 +1,189 @@
+// Copyright 2015 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.
+
+/**
+ * @fileoverview The 'certificate-manager' component manages SSL certificates.
+ */
+Polymer({
+ is: 'certificate-manager',
+
+ behaviors: [I18nBehavior, WebUIListenerBehavior],
+
+ properties: {
+ /** @type {number} */
+ selected: {
+ type: Number,
+ value: 0,
+ },
+
+ /** @type {!Array<!Certificate>} */
+ personalCerts: {
+ type: Array,
+ value: function() {
+ return [];
+ },
+ },
+
+ /** @type {!Array<!Certificate>} */
+ serverCerts: {
+ type: Array,
+ value: function() {
+ return [];
+ },
+ },
+
+ /** @type {!Array<!Certificate>} */
+ caCerts: {
+ type: Array,
+ value: function() {
+ return [];
+ },
+ },
+
+ /** @type {!Array<!Certificate>} */
+ otherCerts: {
+ type: Array,
+ value: function() {
+ return [];
+ },
+ },
+
+ /** @private */
+ certificateTypeEnum_: {
+ type: Object,
+ value: CertificateType,
+ readOnly: true,
+ },
+
+ /** @private */
+ showCaTrustEditDialog_: Boolean,
+
+ /** @private */
+ showDeleteConfirmationDialog_: Boolean,
+
+ /** @private */
+ showPasswordEncryptionDialog_: Boolean,
+
+ /** @private */
+ showPasswordDecryptionDialog_: Boolean,
+
+ /** @private */
+ showErrorDialog_: Boolean,
+
+ /**
+ * The model to be passed to dialogs that refer to a given certificate.
+ * @private {?CertificateSubnode}
+ */
+ dialogModel_: Object,
+
+ /**
+ * The certificate type to be passed to dialogs that refer to a given
+ * certificate.
+ * @private {?CertificateType}
+ */
+ dialogModelCertificateType_: String,
+
+ /**
+ * The model to be passed to the error dialog.
+ * @private {null|!CertificatesError|!CertificatesImportError}
+ */
+ errorDialogModel_: Object,
+
+ /**
+ * The element to return focus to, when the currently shown dialog is
+ * closed.
+ * @private {?HTMLElement}
+ */
+ activeDialogAnchor_: Object,
+ },
+
+ /** @override */
+ attached: function() {
+ this.addWebUIListener('certificates-changed', this.set.bind(this));
+ certificate_manager.CertificatesBrowserProxyImpl.getInstance()
+ .refreshCertificates();
+ },
+
+ /**
+ * @param {number} selectedIndex
+ * @param {number} tabIndex
+ * @return {boolean} Whether to show tab at |tabIndex|.
+ * @private
+ */
+ isTabSelected_: function(selectedIndex, tabIndex) {
+ return selectedIndex == tabIndex;
+ },
+
+ /** @override */
+ ready: function() {
+ this.addEventListener(CertificateActionEvent, event => {
+ this.dialogModel_ = event.detail.subnode;
+ this.dialogModelCertificateType_ = event.detail.certificateType;
+
+ if (event.detail.action == CertificateAction.IMPORT) {
+ if (event.detail.certificateType == CertificateType.PERSONAL) {
+ this.openDialog_(
+ 'certificate-password-decryption-dialog',
+ 'showPasswordDecryptionDialog_', event.detail.anchor);
+ } else if (event.detail.certificateType == CertificateType.CA) {
+ this.openDialog_(
+ 'ca-trust-edit-dialog', 'showCaTrustEditDialog_',
+ event.detail.anchor);
+ }
+ } else {
+ if (event.detail.action == CertificateAction.EDIT) {
+ this.openDialog_(
+ 'ca-trust-edit-dialog', 'showCaTrustEditDialog_',
+ event.detail.anchor);
+ } else if (event.detail.action == CertificateAction.DELETE) {
+ this.openDialog_(
+ 'certificate-delete-confirmation-dialog',
+ 'showDeleteConfirmationDialog_', event.detail.anchor);
+ } else if (event.detail.action == CertificateAction.EXPORT_PERSONAL) {
+ this.openDialog_(
+ 'certificate-password-encryption-dialog',
+ 'showPasswordEncryptionDialog_', event.detail.anchor);
+ }
+ }
+
+ event.stopPropagation();
+ });
+
+ this.addEventListener('certificates-error', event => {
+ var detail = /** @type {!CertificatesErrorEventDetail} */ (event.detail);
+ this.errorDialogModel_ = detail.error;
+ this.openDialog_(
+ 'certificates-error-dialog', 'showErrorDialog_', detail.anchor);
+ event.stopPropagation();
+ });
+ },
+
+ /**
+ * Opens a dialog and registers a listener for removing the dialog from the
+ * DOM once is closed. The listener is destroyed when the dialog is removed
+ * (because of 'restamp').
+ *
+ * @param {string} dialogTagName The tag name of the dialog to be shown.
+ * @param {string} domIfBooleanName The name of the boolean variable
+ * corresponding to the dialog.
+ * @param {?HTMLElement} anchor The element to focus when the dialog is
+ * closed. If null, the previous anchor element should be reused. This
+ * happens when a 'certificates-error-dialog' is opened, which when closed
+ * should focus the anchor of the previous dialog (the one that generated
+ * the error).
+ * @private
+ */
+ openDialog_: function(dialogTagName, domIfBooleanName, anchor) {
+ if (anchor)
+ this.activeDialogAnchor_ = anchor;
+ this.set(domIfBooleanName, true);
+ this.async(() => {
+ var dialog = this.$$(dialogTagName);
+ dialog.addEventListener('close', () => {
+ this.set(domIfBooleanName, false);
+ cr.ui.focusWithoutInk(assert(this.activeDialogAnchor_));
+ });
+ });
+ },
+});
diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager_types.html b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager_types.html
new file mode 100644
index 00000000000..271b2666939
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager_types.html
@@ -0,0 +1 @@
+<script src="certificate_manager_types.js"></script>
diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager_types.js b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager_types.js
new file mode 100644
index 00000000000..76d448770cf
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager_types.js
@@ -0,0 +1,44 @@
+// Copyright 2016 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.
+
+/**
+ * @fileoverview Closure compiler typedefs.
+ */
+
+/**
+ * The payload of the 'certificate-action' event.
+ * @typedef {{
+ * action: !CertificateAction,
+ * subnode: (null|CertificateSubnode|NewCertificateSubNode),
+ * certificateType: !CertificateType,
+ * anchor: !HTMLElement
+ * }}
+ */
+var CertificateActionEventDetail;
+
+/**
+ * The payload of the 'certificates-error' event.
+ * @typedef {{
+ * error: (null|CertificatesError|CertificatesImportError),
+ * anchor: ?HTMLElement
+ * }}
+ */
+var CertificatesErrorEventDetail;
+
+/**
+ * Enumeration of actions that require a popup menu to be shown to the user.
+ * @enum {number}
+ */
+var CertificateAction = {
+ DELETE: 0,
+ EDIT: 1,
+ EXPORT_PERSONAL: 2,
+ IMPORT: 3,
+};
+
+/**
+ * The name of the event fired when a certificate action is selected from the
+ * dropdown menu. CertificateActionEventDetail is passed as the event detail.
+ */
+var CertificateActionEvent = 'certificate-action';
diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_decryption_dialog.html b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_decryption_dialog.html
new file mode 100644
index 00000000000..850da130e7f
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_decryption_dialog.html
@@ -0,0 +1,34 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html">
+<link rel="import" href="certificate_shared_css.html">
+<link rel="import" href="certificates_browser_proxy.html">
+
+<dom-module id="certificate-password-decryption-dialog">
+ <template>
+ <style include="certificate-shared"></style>
+ <dialog is="cr-dialog" id="dialog" close-text="[[i18n('close')]]">
+ <div slot="title">
+ [[i18n('certificateManagerDecryptPasswordTitle')]]
+ </div>
+ <div slot="body">
+ <paper-input type="password" id="password"
+ label="[[i18n('certificateManagerPassword')]]"
+ value="{{password_}}">
+ </paper-input>
+ </div>
+ <div slot="button-container">
+ <paper-button class="cancel-button" on-tap="onCancelTap_">
+ [[i18n('cancel')]]
+ </paper-button>
+ <paper-button id="ok" class="action-button" on-tap="onOkTap_">
+ [[i18n('ok')]]
+ </paper-button>
+ </div>
+ </dialog>
+ </template>
+ <script src="certificate_password_decryption_dialog.js"></script>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_decryption_dialog.js b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_decryption_dialog.js
new file mode 100644
index 00000000000..0e502404c1b
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_decryption_dialog.js
@@ -0,0 +1,53 @@
+// Copyright 2016 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.
+
+/**
+ * @fileoverview A dialog prompting the user for a decryption password such that
+ * a previously exported personal certificate can be imported.
+ */
+Polymer({
+ is: 'certificate-password-decryption-dialog',
+
+ behaviors: [I18nBehavior],
+
+ properties: {
+ /** @private */
+ password_: {
+ type: String,
+ value: '',
+ },
+ },
+
+ /** @private {?certificate_manager.CertificatesBrowserProxy} */
+ browserProxy_: null,
+
+ /** @override */
+ ready: function() {
+ this.browserProxy_ =
+ certificate_manager.CertificatesBrowserProxyImpl.getInstance();
+ },
+
+ /** @override */
+ attached: function() {
+ /** @type {!CrDialogElement} */ (this.$.dialog).showModal();
+ },
+
+ /** @private */
+ onCancelTap_: function() {
+ /** @type {!CrDialogElement} */ (this.$.dialog).close();
+ },
+
+ /** @private */
+ onOkTap_: function() {
+ this.browserProxy_.importPersonalCertificatePasswordSelected(this.password_)
+ .then(
+ () => {
+ /** @type {!CrDialogElement} */ (this.$.dialog).close();
+ },
+ error => {
+ /** @type {!CrDialogElement} */ (this.$.dialog).close();
+ this.fire('certificates-error', {error: error, anchor: null});
+ });
+ },
+});
diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_encryption_dialog.html b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_encryption_dialog.html
new file mode 100644
index 00000000000..f17b2825fef
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_encryption_dialog.html
@@ -0,0 +1,44 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html">
+<link rel="import" href="certificate_shared_css.html">
+<link rel="import" href="certificates_browser_proxy.html">
+
+<dom-module id="certificate-password-encryption-dialog">
+ <template>
+ <style include="certificate-shared">
+ .password-buttons {
+ margin-bottom: 20px;
+ }
+ </style>
+ <dialog is="cr-dialog" id="dialog" close-text="[[i18n('close')]]">
+ <div slot="title">
+ [[i18n('certificateManagerEncryptPasswordTitle')]]
+ </div>
+ <div slot="body">
+ <div>[[i18n('certificateManagerEncryptPasswordDescription')]]</div>
+ <div class="password-buttons">
+ <paper-input type="password" value="{{password_}}" id="password"
+ label="[[i18n('certificateManagerPassword')]]"
+ on-input="validate_"></paper-input>
+ <paper-input type="password"
+ value="{{confirmPassword_}}" id="confirmPassword"
+ label="[[i18n('certificateManagerConfirmPassword')]]"
+ on-input="validate_"></paper-input>
+ </div>
+ </div>
+ <div slot="button-container">
+ <paper-button class="cancel-button" on-tap="onCancelTap_">
+ [[i18n('cancel')]]
+ </paper-button>
+ <paper-button id="ok" class="action-button" on-tap="onOkTap_" disabled>
+ [[i18n('ok')]]
+ </paper-button>
+ </div>
+ </dialog>
+ </template>
+ <script src="certificate_password_encryption_dialog.js"></script>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_encryption_dialog.js b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_encryption_dialog.js
new file mode 100644
index 00000000000..b605ee7c22a
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_encryption_dialog.js
@@ -0,0 +1,69 @@
+// Copyright 2016 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.
+
+/**
+ * @fileoverview A dialog prompting the user to encrypt a personal certificate
+ * before it is exported to disk.
+ */
+Polymer({
+ is: 'certificate-password-encryption-dialog',
+
+ behaviors: [I18nBehavior],
+
+ properties: {
+ /** @type {!CertificateSubnode} */
+ model: Object,
+
+ /** @private */
+ password_: {
+ type: String,
+ value: '',
+ },
+
+ /** @private */
+ confirmPassword_: {
+ type: String,
+ value: '',
+ },
+ },
+
+ /** @private {?certificate_manager.CertificatesBrowserProxy} */
+ browserProxy_: null,
+
+ /** @override */
+ ready: function() {
+ this.browserProxy_ =
+ certificate_manager.CertificatesBrowserProxyImpl.getInstance();
+ },
+
+ /** @override */
+ attached: function() {
+ /** @type {!CrDialogElement} */ (this.$.dialog).showModal();
+ },
+
+ /** @private */
+ onCancelTap_: function() {
+ /** @type {!CrDialogElement} */ (this.$.dialog).close();
+ },
+
+ /** @private */
+ onOkTap_: function() {
+ this.browserProxy_.exportPersonalCertificatePasswordSelected(this.password_)
+ .then(
+ () => {
+ this.$.dialog.close();
+ },
+ error => {
+ this.$.dialog.close();
+ this.fire('certificates-error', {error: error, anchor: null});
+ });
+ },
+
+ /** @private */
+ validate_: function() {
+ var isValid =
+ this.password_ != '' && this.password_ == this.confirmPassword_;
+ this.$.ok.disabled = !isValid;
+ },
+});
diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_shared_css.html b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_shared_css.html
new file mode 100644
index 00000000000..88fbdb2e211
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_shared_css.html
@@ -0,0 +1,35 @@
+<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
+
+<!-- Common styles for certificate elements. -->
+
+<dom-module id="certificate-shared">
+ <template>
+ <style include="cr-shared-style">
+ /* .list-frame and .list-item match the styling in settings_shared_css. */
+ .list-frame {
+ -webkit-padding-end: 20px;
+ -webkit-padding-start: 60px;
+ align-items: center;
+ display: block;
+ }
+
+ .list-item {
+ align-items: center;
+ display: flex;
+ min-height: 48px;
+ }
+
+ .list-item.underbar {
+ border-bottom: var(--cr-separator-line);
+ }
+
+ .list-item.selected {
+ font-weight: 500;
+ }
+
+ .list-item > .start {
+ flex: 1;
+ }
+ </style>
+ </template>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.html b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.html
new file mode 100644
index 00000000000..691625fb095
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.html
@@ -0,0 +1,63 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_action_menu/cr_action_menu.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.html">
+<link rel="import" href="chrome://resources/cr_elements/icons.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
+<link rel="import" href="certificate_manager_types.html">
+<link rel="import" href="certificate_shared_css.html">
+<link rel="import" href="certificates_browser_proxy.html">
+
+<dom-module id="certificate-subentry">
+ <template>
+ <style include="certificate-shared cr-icons">
+ .name {
+ flex: auto;
+ }
+
+ .untrusted {
+ -webkit-margin-end: 16px;
+ color: var(--paper-red-700);
+ font-weight: 500;
+ text-transform: uppercase;
+ }
+
+ :host([is-last]) .list-item {
+ border-bottom: none;
+ }
+ </style>
+ <div class="list-item underbar">
+ <div class="untrusted" hidden$="[[!model.untrusted]]">
+ [[i18n('certificateManagerUntrusted')]]
+ </div>
+ <div class="name">[[model.name]]</div>
+ <button is="paper-icon-button-light" class="icon-more-vert" id="dots"
+ title="[[i18n('moreActions')]]" on-tap="onDotsTap_"></button>
+ <template is="cr-lazy-render" id="menu">
+ <dialog is="cr-action-menu">
+ <button class="dropdown-item" id="view"
+ on-tap="onViewTap_">
+ [[i18n('certificateManagerView')]]
+ </button>
+ <button class="dropdown-item" id="edit"
+ hidden$="[[!canEdit_(certificateType, model)]]"
+ on-tap="onEditTap_">
+ [[i18n('edit')]]
+ </button>
+ <button class="dropdown-item" id="export"
+ hidden$="[[!canExport_(certificateType, model)]]"
+ on-tap="onExportTap_">
+ [[i18n('certificateManagerExport')]]
+ </button>
+ <button class="dropdown-item" id="delete"
+ hidden$="[[!canDelete_(model)]]"
+ on-tap="onDeleteTap_">
+ [[i18n('certificateManagerDelete')]]
+ </button>
+ </dialog>
+ </template>
+ <div>
+ </template>
+ <script src="certificate_subentry.js"></script>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.js b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.js
new file mode 100644
index 00000000000..f2bc22e51fc
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.js
@@ -0,0 +1,150 @@
+// Copyright 2016 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.
+
+/**
+ * @fileoverview certificate-subentry represents an SSL certificate sub-entry.
+ */
+
+Polymer({
+ is: 'certificate-subentry',
+
+ behaviors: [I18nBehavior],
+
+ properties: {
+ /** @type {!CertificateSubnode} */
+ model: Object,
+
+ /** @type {!CertificateType} */
+ certificateType: String,
+ },
+
+ /** @private {certificate_manager.CertificatesBrowserProxy} */
+ browserProxy_: null,
+
+ /** @override */
+ created: function() {
+ this.browserProxy_ =
+ certificate_manager.CertificatesBrowserProxyImpl.getInstance();
+ },
+
+ /**
+ * Dispatches an event indicating which certificate action was tapped. It is
+ * used by the parent of this element to display a modal dialog accordingly.
+ * @param {!CertificateAction} action
+ * @private
+ */
+ dispatchCertificateActionEvent_: function(action) {
+ this.fire(
+ CertificateActionEvent,
+ /** @type {!CertificateActionEventDetail} */ ({
+ action: action,
+ subnode: this.model,
+ certificateType: this.certificateType,
+ anchor: this.$.dots,
+ }));
+ },
+
+ /**
+ * Handles the case where a call to the browser resulted in a rejected
+ * promise.
+ * @param {*} error Expects {?CertificatesError}.
+ * @private
+ */
+ onRejected_: function(error) {
+ if (error === null) {
+ // Nothing to do here. Null indicates that the user clicked "cancel" on
+ // the native file chooser dialog.
+ return;
+ }
+
+ // Otherwise propagate the error to the parents, such that a dialog
+ // displaying the error will be shown.
+ this.fire('certificates-error', {error: error, anchor: this.$.dots});
+ },
+
+ /**
+ * @param {!Event} event
+ * @private
+ */
+ onViewTap_: function(event) {
+ this.closePopupMenu_();
+ this.browserProxy_.viewCertificate(this.model.id);
+ },
+
+ /**
+ * @param {!Event} event
+ * @private
+ */
+ onEditTap_: function(event) {
+ this.closePopupMenu_();
+ this.dispatchCertificateActionEvent_(CertificateAction.EDIT);
+ },
+
+ /**
+ * @param {!Event} event
+ * @private
+ */
+ onDeleteTap_: function(event) {
+ this.closePopupMenu_();
+ this.dispatchCertificateActionEvent_(CertificateAction.DELETE);
+ },
+
+ /**
+ * @param {!Event} event
+ * @private
+ */
+ onExportTap_: function(event) {
+ this.closePopupMenu_();
+ if (this.certificateType == CertificateType.PERSONAL) {
+ this.browserProxy_.exportPersonalCertificate(this.model.id).then(() => {
+ this.dispatchCertificateActionEvent_(CertificateAction.EXPORT_PERSONAL);
+ }, this.onRejected_.bind(this));
+ } else {
+ this.browserProxy_.exportCertificate(this.model.id);
+ }
+ },
+
+ /**
+ * @param {!CertificateType} certificateType
+ * @param {!CertificateSubnode} model
+ * @return {boolean} Whether the certificate can be edited.
+ * @private
+ */
+ canEdit_: function(certificateType, model) {
+ return certificateType == CertificateType.CA && !model.policy;
+ },
+
+ /**
+ * @param {!CertificateType} certificateType
+ * @param {!CertificateSubnode} model
+ * @return {boolean} Whether the certificate can be exported.
+ * @private
+ */
+ canExport_: function(certificateType, model) {
+ if (certificateType == CertificateType.PERSONAL) {
+ return model.extractable;
+ }
+ return true;
+ },
+
+ /**
+ * @param {!CertificateSubnode} model
+ * @return {boolean} Whether the certificate can be deleted.
+ * @private
+ */
+ canDelete_: function(model) {
+ return !model.readonly && !model.policy;
+ },
+
+ /** @private */
+ closePopupMenu_: function() {
+ this.$$('dialog[is=cr-action-menu]').close();
+ },
+
+ /** @private */
+ onDotsTap_: function() {
+ var actionMenu = /** @type {!CrActionMenuElement} */ (this.$.menu.get());
+ actionMenu.showAt(this.$.dots);
+ },
+});
diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_browser_proxy.html b/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_browser_proxy.html
new file mode 100644
index 00000000000..0dc7c572ff1
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_browser_proxy.html
@@ -0,0 +1 @@
+<script src="certificates_browser_proxy.js"></script>
diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_browser_proxy.js b/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_browser_proxy.js
new file mode 100644
index 00000000000..bbf1d73f17a
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_browser_proxy.js
@@ -0,0 +1,271 @@
+// Copyright 2016 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.
+
+/**
+ * @fileoverview A helper object used from the "Manage certificates" section
+ * to interact with the browser.
+ */
+
+/**
+ * @typedef {{
+ * extractable: boolean,
+ * id: string,
+ * name: string,
+ * policy: boolean,
+ * readonly: boolean,
+ * untrusted: boolean,
+ * }}
+ * @see chrome/browser/ui/webui/settings/certificates_handler.cc
+ */
+var CertificateSubnode;
+
+/**
+ * A data structure describing a certificate that is currently being imported,
+ * therefore it has no ID yet, but it has a name. Used within JS only.
+ * @typedef {{
+ * name: string,
+ * }}
+ */
+var NewCertificateSubNode;
+
+/**
+ * @typedef {{
+ * id: string,
+ * name: string,
+ * subnodes: !Array<!CertificateSubnode>
+ * }}
+ * @see chrome/browser/ui/webui/settings/certificates_handler.cc
+ */
+var Certificate;
+
+/**
+ * @typedef {{
+ * ssl: boolean,
+ * email: boolean,
+ * objSign: boolean
+ * }}
+ */
+var CaTrustInfo;
+
+/**
+ * Generic error returned from C++ via a Promise reject callback.
+ * @typedef {{
+ * title: string,
+ * description: string
+ * }}
+ * @see chrome/browser/ui/webui/settings/certificates_handler.cc
+ */
+var CertificatesError;
+
+/**
+ * Enumeration of all possible certificate types.
+ * @enum {string}
+ */
+var CertificateType = {
+ CA: 'ca',
+ OTHER: 'other',
+ PERSONAL: 'personal',
+ SERVER: 'server',
+};
+
+
+/**
+ * Error returned from C++ via a Promise reject callback, when some certificates
+ * fail to be imported.
+ * @typedef {{
+ * title: string,
+ * description: string,
+ * certificateErrors: !Array<{name: string, error: string}>
+ * }}
+ * @see chrome/browser/ui/webui/settings/certificates_handler.cc
+ */
+var CertificatesImportError;
+
+cr.define('certificate_manager', function() {
+ /** @interface */
+ class CertificatesBrowserProxy {
+ /**
+ * Triggers 5 events in the following order
+ * 1x 'certificates-model-ready' event.
+ * 4x 'certificates-changed' event, one for each certificate category.
+ */
+ refreshCertificates() {}
+
+ /** @param {string} id */
+ viewCertificate(id) {}
+
+ /** @param {string} id */
+ exportCertificate(id) {}
+
+ /**
+ * @param {string} id
+ * @return {!Promise} A promise resolved when the certificate has been
+ * deleted successfully or rejected with a CertificatesError.
+ */
+ deleteCertificate(id) {}
+
+ /**
+ * @param {string} id
+ * @return {!Promise<!CaTrustInfo>}
+ */
+ getCaCertificateTrust(id) {}
+
+ /**
+ * @param {string} id
+ * @param {boolean} ssl
+ * @param {boolean} email
+ * @param {boolean} objSign
+ * @return {!Promise}
+ */
+ editCaCertificateTrust(id, ssl, email, objSign) {}
+
+ cancelImportExportCertificate() {}
+
+ /**
+ * @param {string} id
+ * @return {!Promise} A promise firing once the user has selected
+ * the export location. A prompt should be shown to asking for a
+ * password to use for encrypting the file. The password should be
+ * passed back via a call to
+ * exportPersonalCertificatePasswordSelected().
+ */
+ exportPersonalCertificate(id) {}
+
+ /**
+ * @param {string} password
+ * @return {!Promise}
+ */
+ exportPersonalCertificatePasswordSelected(password) {}
+
+ /**
+ * @param {boolean} useHardwareBacked
+ * @return {!Promise<boolean>} A promise firing once the user has selected
+ * the file to be imported. If true a password prompt should be shown to
+ * the user, and the password should be passed back via a call to
+ * importPersonalCertificatePasswordSelected().
+ */
+ importPersonalCertificate(useHardwareBacked) {}
+
+ /**
+ * @param {string} password
+ * @return {!Promise}
+ */
+ importPersonalCertificatePasswordSelected(password) {}
+
+ /**
+ * @return {!Promise} A promise firing once the user has selected
+ * the file to be imported, or failing with CertificatesError.
+ * Upon success, a prompt should be shown to the user to specify the
+ * trust levels, and that information should be passed back via a call
+ * to importCaCertificateTrustSelected().
+ */
+ importCaCertificate() {}
+
+ /**
+ * @param {boolean} ssl
+ * @param {boolean} email
+ * @param {boolean} objSign
+ * @return {!Promise} A promise firing once the trust level for the imported
+ * certificate has been successfully set. The promise is rejected if an
+ * error occurred with either a CertificatesError or
+ * CertificatesImportError.
+ */
+ importCaCertificateTrustSelected(ssl, email, objSign) {}
+
+ /**
+ * @return {!Promise} A promise firing once the certificate has been
+ * imported. The promise is rejected if an error occurred, with either
+ * a CertificatesError or CertificatesImportError.
+ */
+ importServerCertificate() {}
+ }
+
+ /**
+ * @implements {certificate_manager.CertificatesBrowserProxy}
+ */
+ class CertificatesBrowserProxyImpl {
+ /** @override */
+ refreshCertificates() {
+ chrome.send('refreshCertificates');
+ }
+
+ /** @override */
+ viewCertificate(id) {
+ chrome.send('viewCertificate', [id]);
+ }
+
+ /** @override */
+ exportCertificate(id) {
+ chrome.send('exportCertificate', [id]);
+ }
+
+ /** @override */
+ deleteCertificate(id) {
+ return cr.sendWithPromise('deleteCertificate', id);
+ }
+
+ /** @override */
+ exportPersonalCertificate(id) {
+ return cr.sendWithPromise('exportPersonalCertificate', id);
+ }
+
+ /** @override */
+ exportPersonalCertificatePasswordSelected(password) {
+ return cr.sendWithPromise(
+ 'exportPersonalCertificatePasswordSelected', password);
+ }
+
+ /** @override */
+ importPersonalCertificate(useHardwareBacked) {
+ return cr.sendWithPromise('importPersonalCertificate', useHardwareBacked);
+ }
+
+ /** @override */
+ importPersonalCertificatePasswordSelected(password) {
+ return cr.sendWithPromise(
+ 'importPersonalCertificatePasswordSelected', password);
+ }
+
+ /** @override */
+ getCaCertificateTrust(id) {
+ return cr.sendWithPromise('getCaCertificateTrust', id);
+ }
+
+ /** @override */
+ editCaCertificateTrust(id, ssl, email, objSign) {
+ return cr.sendWithPromise(
+ 'editCaCertificateTrust', id, ssl, email, objSign);
+ }
+
+ /** @override */
+ importCaCertificateTrustSelected(ssl, email, objSign) {
+ return cr.sendWithPromise(
+ 'importCaCertificateTrustSelected', ssl, email, objSign);
+ }
+
+ /** @override */
+ cancelImportExportCertificate() {
+ chrome.send('cancelImportExportCertificate');
+ }
+
+ /** @override */
+ importCaCertificate() {
+ return cr.sendWithPromise('importCaCertificate');
+ }
+
+ /** @override */
+ importServerCertificate() {
+ return cr.sendWithPromise('importServerCertificate');
+ }
+ }
+
+ // The singleton instance_ is replaced with a test version of this wrapper
+ // during testing.
+ cr.addSingletonGetter(CertificatesBrowserProxyImpl);
+
+ return {
+ CertificatesBrowserProxy: CertificatesBrowserProxy,
+ CertificatesBrowserProxyImpl: CertificatesBrowserProxyImpl,
+ };
+});
diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_error_dialog.html b/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_error_dialog.html
new file mode 100644
index 00000000000..2c0bb746765
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_error_dialog.html
@@ -0,0 +1,29 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
+<link rel="import" href="certificate_shared_css.html">
+
+<dom-module id="certificates-error-dialog">
+ <template>
+ <style include="certificate-shared"></style>
+ <dialog is="cr-dialog" id="dialog" close-text="[[i18n('close')]]">
+ <div slot="title">[[model.title]]</div>
+ <div slot="body">
+ <div>[[model.description]]</div>
+ <template is="dom-if" if="[[model.certificateErrors]]">
+ <template is="dom-repeat" items="[[model.certificateErrors]]">
+ <div>[[getCertificateErrorText_(item)]]</div>
+ </template>
+ </template>
+ </div>
+ <div slot="button-container">
+ <paper-button id="ok" class="action-button" on-tap="onOkTap_">
+ [[i18n('ok')]]
+ </paper-button>
+ </div>
+ </dialog>
+ </template>
+ <script src="certificates_error_dialog.js"></script>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_error_dialog.js b/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_error_dialog.js
new file mode 100644
index 00000000000..2b3cd779653
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_error_dialog.js
@@ -0,0 +1,38 @@
+// Copyright 2016 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.
+
+/**
+ * @fileoverview A dialog for showing SSL certificate related error messages.
+ * The user can only close the dialog, there is no other possible interaction.
+ */
+Polymer({
+ is: 'certificates-error-dialog',
+
+ behaviors: [I18nBehavior],
+
+ properties: {
+ /** @type {!CertificatesError|!CertificatesImportError} */
+ model: Object,
+ },
+
+ /** @override */
+ attached: function() {
+ /** @type {!CrDialogElement} */ (this.$.dialog).showModal();
+ },
+
+ /** @private */
+ onOkTap_: function() {
+ /** @type {!CrDialogElement} */ (this.$.dialog).close();
+ },
+
+ /**
+ * @param {{name: string, error: string}} importError
+ * @return {string}
+ * @private
+ */
+ getCertificateErrorText_: function(importError) {
+ return loadTimeData.getStringF(
+ 'certificateImportErrorFormat', importError.name, importError.error);
+ },
+});
diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/compiled_resources2.gyp b/chromium/ui/webui/resources/cr_components/certificate_manager/compiled_resources2.gyp
new file mode 100644
index 00000000000..fef217bee75
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/certificate_manager/compiled_resources2.gyp
@@ -0,0 +1,125 @@
+# Copyright 2017 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.
+{
+ 'targets': [
+ {
+ 'target_name': 'ca_trust_edit_dialog',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/cr_elements/cr_dialog/compiled_resources2.gyp:cr_dialog',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data',
+ 'certificate_manager_types',
+ 'certificates_browser_proxy',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'certificate_delete_confirmation_dialog',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/cr_elements/cr_dialog/compiled_resources2.gyp:cr_dialog',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data',
+ 'certificate_manager_types',
+ 'certificates_browser_proxy',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'certificate_entry',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
+ 'certificate_manager_types',
+ 'certificates_browser_proxy',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'certificate_list',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
+ 'certificate_manager_types',
+ 'certificate_subentry',
+ 'certificates_browser_proxy',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'certificate_manager',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:web_ui_listener_behavior',
+ '<(DEPTH)/ui/webui/resources/js/cr/ui/compiled_resources2.gyp:focus_without_ink',
+ 'certificate_list',
+ 'certificate_manager_types',
+ 'certificates_browser_proxy',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'certificate_manager_types',
+ 'dependencies': [
+ 'certificates_browser_proxy',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'certificate_password_decryption_dialog',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/cr_elements/cr_dialog/compiled_resources2.gyp:cr_dialog',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
+ 'certificate_manager_types',
+ 'certificates_browser_proxy',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'certificate_password_encryption_dialog',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/cr_elements/cr_dialog/compiled_resources2.gyp:cr_dialog',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
+ 'certificate_manager_types',
+ 'certificates_browser_proxy',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'certificate_subentry',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/cr_elements/cr_action_menu/compiled_resources2.gyp:cr_action_menu',
+ '<(DEPTH)/ui/webui/resources/cr_elements/cr_lazy_render/compiled_resources2.gyp:cr_lazy_render',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
+ 'certificate_manager_types',
+ 'certificates_browser_proxy',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'certificates_browser_proxy',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'certificates_error_dialog',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/cr_elements/cr_dialog/compiled_resources2.gyp:cr_dialog',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data',
+ 'certificate_manager_types',
+ 'certificates_browser_proxy',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ ],
+}
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.html b/chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.html
new file mode 100644
index 00000000000..a89ba4d5fde
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.html
@@ -0,0 +1,137 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
+<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
+<link rel="import" href="chrome://resources/html/cr.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html">
+
+<dom-module id="bluetooth-dialog">
+ <template>
+ <style include="cr-hidden-style iron-flex">
+ #pairing {
+ margin-bottom: 10px;
+ }
+
+ #pairing paper-input {
+ text-align: center;
+ }
+
+ #pinDiv {
+ margin-top: 10px;
+ }
+
+ .dialog-message {
+ margin-bottom: 10px;
+ }
+
+ div.contents {
+ height: 250px;
+ }
+
+ /* .display indicates a displayed pin code or passkey. */
+ span.display {
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ box-shadow: 0 0 0 1px #222;
+ color: #222;
+ font-size: 123.08%; /* 16px / 13px */
+ height: 38px;
+ line-height: 38px;
+ margin: 0 5px;
+ padding: 0 15px;
+ text-align: center;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+ }
+
+ span.display.next {
+ background: rgb(77, 144, 254);
+ border: 2px solid rgb(77, 144, 254);
+ box-shadow: none;
+ color: #fff;
+ }
+
+ span.display.untyped {
+ border: 1px solid #d4d4d4;
+ box-shadow: 0 0 0 1px #888;
+ color: #666;
+ }
+
+ /* .confirm indicates a confirmation passkey. */
+ span.confirm {
+ color: #999;
+ font-size: 153.85%; /* 20px / 13px */
+ font-weight: 600; /* semibold */
+ margin: 0 20px;
+ }
+ </style>
+ <!-- TODO(stevenjb/dschuyler): Find a solution to support i18n{} here -->
+ <dialog is="cr-dialog" id="dialog" on-cancel="onDialogCanceled_"
+ close-text="[[i18n('close')]]" on-closed="onDialogCanceled_">
+ <div slot="title">[[title]]</div>
+ <div slot="body">
+ <div class="contents layout vertical center center-justified">
+ <template is="dom-if" if="[[!errorMessage_]]">
+ <div id="pairing" class="layout vertical center center-justified">
+ <div class="dialog-message">
+ [[getMessage_(pairingDevice, pairingEvent_)]]
+ </div>
+ <div hidden$="[[!showEnterPincode_(pairingEvent_)]]">
+ <paper-input id="pincode" minlength="1" maxlength="16"
+ type="text" auto-validate value="{{pinOrPass_}}">
+ </paper-input>
+ </div>
+ <div hidden$="[[!showEnterPasskey_(pairingEvent_)]]">
+ <paper-input id="passkey" minlength="6" maxlength="6"
+ type="text" auto-validate value="{{pinOrPass_}}">
+ </paper-input>
+ </div>
+ <div id="pinDiv" class="layout horizontal center center-justified"
+ hidden="[[!showDisplayPassOrPin_(pairingEvent_)]]">
+ <template is="dom-repeat" items="[[digits_]]">
+ <span class$="[[getPinClass_(index, pairingEvent_)]]">
+ [[getPinDigit_(index, pairingEvent_)]]
+ </span>
+ </template>
+ <span class$="[[getPinClass_(-1, pairingEvent_)]]"
+ hidden="[[showAcceptReject_(pairingEvent_)]]">
+ [[i18n('bluetoothEnterKey')]]
+ </span>
+ </div>
+ </div>
+ </template>
+ <template is="dom-if" if="[[errorMessage_]]">
+ <div class="layout vertical center center-justified">
+ <div class="dialog-message">[[errorMessage_]]</div>
+ </div>
+ </template>
+ </div>
+ </div>
+ <div slot="button-container">
+ <template is="dom-if" if="[[!errorMessage_]]">
+ <paper-button hidden$="[[!showAcceptReject_(pairingEvent_)]]"
+ on-tap="onAcceptTap_">[[i18n('bluetoothAccept')]]</paper-button>
+ <paper-button hidden$="[[!showAcceptReject_(pairingEvent_)]]"
+ on-tap="onRejectTap_">[[i18n('bluetoothReject')]]</paper-button>
+ <paper-button hidden$="[[!showConnect_(pairingEvent_)]]"
+ disabled="[[!enableConnect_(pairingEvent_, pinOrPass_)]]"
+ on-tap="onConnectTap_">[[i18n('bluetoothPair')]]</paper-button>
+ <paper-button
+ hidden$="[[!showDismiss_(pairingDevice, pairingEvent_)]]"
+ on-tap="close">[[i18n('ok')]]</paper-button>
+ <paper-button hidden$="[[showDismiss_(pairingDevice, pairingEvent_)]]"
+ on-tap="onCancelTap_">
+ [[i18n('cancel')]]
+ </paper-button>
+ </template>
+ <template is="dom-if" if="[[errorMessage_]]">
+ <paper-button on-tap="close">[[i18n('ok')]]</paper-button>
+ </template>
+ </div>
+ </dialog>
+ </template>
+ <script src="bluetooth_dialog.js"></script>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.js b/chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.js
new file mode 100644
index 00000000000..b061349e266
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.js
@@ -0,0 +1,459 @@
+// Copyright 2016 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.
+
+/**
+ * @fileoverview
+ * Dialog used for pairing a provided |pairing-device|. Set |show-error| to
+ * show the error results from a pairing event instead of the pairing UI.
+ * NOTE: This module depends on I18nBehavior which depends on loadTimeData.
+ */
+
+var PairingEventType = chrome.bluetoothPrivate.PairingEventType;
+
+Polymer({
+ is: 'bluetooth-dialog',
+
+ behaviors: [I18nBehavior],
+
+ properties: {
+ /**
+ * Interface for bluetooth calls. Set in bluetooth-page.
+ * @type {Bluetooth}
+ * @private
+ */
+ bluetooth: {
+ type: Object,
+ value: chrome.bluetooth,
+ },
+
+ /**
+ * Interface for bluetoothPrivate calls.
+ * @type {BluetoothPrivate}
+ */
+ bluetoothPrivate: {
+ type: Object,
+ value: chrome.bluetoothPrivate,
+ },
+
+ /** Dialog title */
+ title: String,
+
+ /**
+ * Current Pairing device.
+ * @type {!chrome.bluetooth.Device|undefined}
+ */
+ pairingDevice: Object,
+
+ /**
+ * Current Pairing event.
+ * @private {?chrome.bluetoothPrivate.PairingEvent}
+ */
+ pairingEvent_: {
+ type: Object,
+ value: null,
+ },
+
+ /**
+ * May be set by the host to show a pairing error result, or may be
+ * set by the dialog if a pairing or connect error occured.
+ * @private
+ */
+ errorMessage_: String,
+
+ /**
+ * Pincode or passkey value, used to trigger connect enabled changes.
+ * @private
+ */
+ pinOrPass_: String,
+
+ /**
+ * @const {!Array<number>}
+ * @private
+ */
+ digits_: {
+ type: Array,
+ readOnly: true,
+ value: [0, 1, 2, 3, 4, 5],
+ },
+ },
+
+ observers: [
+ 'dialogUpdated_(errorMessage_, pairingEvent_)',
+ 'pairingChanged_(pairingDevice, pairingEvent_)',
+ ],
+
+ /**
+ * Listener for chrome.bluetoothPrivate.onPairing events.
+ * @private {?function(!chrome.bluetoothPrivate.PairingEvent)}
+ */
+ bluetoothPrivateOnPairingListener_: null,
+
+ /**
+ * Listener for chrome.bluetooth.onBluetoothDeviceChanged events.
+ * @private {?function(!chrome.bluetooth.Device)}
+ */
+ bluetoothDeviceChangedListener_: null,
+
+ open: function() {
+ this.startPairing();
+ this.pinOrPass_ = '';
+ this.getDialog_().showModal();
+ this.itemWasFocused_ = false;
+ },
+
+ close: function() {
+ this.endPairing();
+ var dialog = this.getDialog_();
+ if (dialog.open)
+ dialog.close();
+ },
+
+ /**
+ * Updates the dialog after a connect attempt.
+ * @param {!chrome.bluetooth.Device} device The device connected to
+ * @param {!{message: string}} lastError chrome.runtime.lastError
+ * @param {chrome.bluetoothPrivate.ConnectResultType} result The connect
+ * result
+ * @return {boolean}
+ */
+ handleError: function(device, lastError, result) {
+ var error;
+ if (lastError) {
+ error = lastError.message;
+ } else {
+ switch (result) {
+ case chrome.bluetoothPrivate.ConnectResultType.IN_PROGRESS:
+ case chrome.bluetoothPrivate.ConnectResultType.ALREADY_CONNECTED:
+ case chrome.bluetoothPrivate.ConnectResultType.AUTH_CANCELED:
+ case chrome.bluetoothPrivate.ConnectResultType.SUCCESS:
+ this.errorMessage_ = '';
+ return false;
+ default:
+ error = result;
+ }
+ }
+
+ var name = device.name || device.address;
+ var id = 'bluetooth_connect_' + error;
+ if (this.i18nExists(id)) {
+ this.errorMessage_ = this.i18n(id, name);
+ } else {
+ this.errorMessage_ = error;
+ console.error('Unexpected error connecting to: ' + name + ': ' + error);
+ }
+ return true;
+ },
+
+ /** @private */
+ dialogUpdated_: function() {
+ if (this.showEnterPincode_())
+ this.$$('#pincode').focus();
+ else if (this.showEnterPasskey_())
+ this.$$('#passkey').focus();
+ },
+
+ /**
+ * @return {!CrDialogElement}
+ * @private
+ */
+ getDialog_: function() {
+ return /** @type {!CrDialogElement} */ (this.$.dialog);
+ },
+
+ /** @private */
+ onCancelTap_: function() {
+ this.getDialog_().cancel();
+ },
+
+ /** @private */
+ onDialogCanceled_: function() {
+ if (!this.errorMessage_)
+ this.sendResponse_(chrome.bluetoothPrivate.PairingResponse.CANCEL);
+ this.endPairing();
+ },
+
+ /** Called when the dialog is opened. Starts listening for pairing events. */
+ startPairing: function() {
+ if (!this.bluetoothPrivateOnPairingListener_) {
+ this.bluetoothPrivateOnPairingListener_ =
+ this.onBluetoothPrivateOnPairing_.bind(this);
+ this.bluetoothPrivate.onPairing.addListener(
+ this.bluetoothPrivateOnPairingListener_);
+ }
+ if (!this.bluetoothDeviceChangedListener_) {
+ this.bluetoothDeviceChangedListener_ =
+ this.onBluetoothDeviceChanged_.bind(this);
+ this.bluetooth.onDeviceChanged.addListener(
+ this.bluetoothDeviceChangedListener_);
+ }
+ },
+
+ /** Called when the dialog is closed. */
+ endPairing: function() {
+ if (this.bluetoothPrivateOnPairingListener_) {
+ this.bluetoothPrivate.onPairing.removeListener(
+ this.bluetoothPrivateOnPairingListener_);
+ this.bluetoothPrivateOnPairingListener_ = null;
+ }
+ if (this.bluetoothDeviceChangedListener_) {
+ this.bluetooth.onDeviceChanged.removeListener(
+ this.bluetoothDeviceChangedListener_);
+ this.bluetoothDeviceChangedListener_ = null;
+ }
+ this.pairingEvent_ = null;
+ },
+
+ /**
+ * Process bluetoothPrivate.onPairing events.
+ * @param {!chrome.bluetoothPrivate.PairingEvent} event
+ * @private
+ */
+ onBluetoothPrivateOnPairing_: function(event) {
+ if (!this.pairingDevice ||
+ event.device.address != this.pairingDevice.address) {
+ return;
+ }
+ if (event.pairing == PairingEventType.KEYS_ENTERED &&
+ event.passkey === undefined && this.pairingEvent_) {
+ // 'keysEntered' event might not include the updated passkey so preserve
+ // the current one.
+ event.passkey = this.pairingEvent_.passkey;
+ }
+ this.pairingEvent_ = event;
+ },
+
+ /**
+ * Process bluetooth.onDeviceChanged events. This ensures that the dialog
+ * updates when the connection state changes.
+ * @param {!chrome.bluetooth.Device} device
+ * @private
+ */
+ onBluetoothDeviceChanged_: function(device) {
+ if (!this.pairingDevice || device.address != this.pairingDevice.address)
+ return;
+ this.pairingDevice = device;
+ },
+
+ /** @private */
+ pairingChanged_: function() {
+ // Auto-close the dialog when pairing completes.
+ if (this.pairingDevice.paired && !this.pairingDevice.connecting &&
+ this.pairingDevice.connected) {
+ this.close();
+ return;
+ }
+ this.errorMessage_ = '';
+ this.pinOrPass_ = '';
+ },
+
+ /**
+ * @return {string}
+ * @private
+ */
+ getMessage_: function() {
+ var message;
+ if (!this.pairingEvent_)
+ message = 'bluetoothStartConnecting';
+ else
+ message = this.getEventDesc_(this.pairingEvent_.pairing);
+ return this.i18n(message, this.pairingDevice.name);
+ },
+
+ /**
+ * @return {boolean}
+ * @private
+ */
+ showEnterPincode_: function() {
+ return !!this.pairingEvent_ &&
+ this.pairingEvent_.pairing == PairingEventType.REQUEST_PINCODE;
+ },
+
+ /**
+ * @return {boolean}
+ * @private
+ */
+ showEnterPasskey_: function() {
+ return !!this.pairingEvent_ &&
+ this.pairingEvent_.pairing == PairingEventType.REQUEST_PASSKEY;
+ },
+
+ /**
+ * @return {boolean}
+ * @private
+ */
+ showDisplayPassOrPin_: function() {
+ if (!this.pairingEvent_)
+ return false;
+ var pairing = this.pairingEvent_.pairing;
+ return (
+ pairing == PairingEventType.DISPLAY_PINCODE ||
+ pairing == PairingEventType.DISPLAY_PASSKEY ||
+ pairing == PairingEventType.CONFIRM_PASSKEY ||
+ pairing == PairingEventType.KEYS_ENTERED);
+ },
+
+ /**
+ * @return {boolean}
+ * @private
+ */
+ showAcceptReject_: function() {
+ return !!this.pairingEvent_ &&
+ this.pairingEvent_.pairing == PairingEventType.CONFIRM_PASSKEY;
+ },
+
+ /**
+ * @return {boolean}
+ * @private
+ */
+ showConnect_: function() {
+ if (!this.pairingEvent_)
+ return false;
+ var pairing = this.pairingEvent_.pairing;
+ return pairing == PairingEventType.REQUEST_PINCODE ||
+ pairing == PairingEventType.REQUEST_PASSKEY;
+ },
+
+ /**
+ * @return {boolean}
+ * @private
+ */
+ enableConnect_: function() {
+ if (!this.showConnect_())
+ return false;
+ var inputId =
+ (this.pairingEvent_.pairing == PairingEventType.REQUEST_PINCODE) ?
+ '#pincode' :
+ '#passkey';
+ var paperInput = /** @type {!PaperInputElement} */ (this.$$(inputId));
+ assert(paperInput);
+ /** @type {string} */ var value = paperInput.value;
+ return !!value && paperInput.validate();
+ },
+
+ /**
+ * @return {boolean}
+ * @private
+ */
+ showDismiss_: function() {
+ return this.pairingDevice.paired ||
+ (!!this.pairingEvent_ &&
+ this.pairingEvent_.pairing == PairingEventType.COMPLETE);
+ },
+
+ /** @private */
+ onAcceptTap_: function() {
+ this.sendResponse_(chrome.bluetoothPrivate.PairingResponse.CONFIRM);
+ },
+
+ /** @private */
+ onConnectTap_: function() {
+ this.sendResponse_(chrome.bluetoothPrivate.PairingResponse.CONFIRM);
+ },
+
+ /** @private */
+ onRejectTap_: function() {
+ this.sendResponse_(chrome.bluetoothPrivate.PairingResponse.REJECT);
+ },
+
+ /**
+ * @param {!chrome.bluetoothPrivate.PairingResponse} response
+ * @private
+ */
+ sendResponse_: function(response) {
+ if (!this.pairingDevice)
+ return;
+ var options =
+ /** @type {!chrome.bluetoothPrivate.SetPairingResponseOptions} */ (
+ {device: this.pairingDevice, response: response});
+ if (response == chrome.bluetoothPrivate.PairingResponse.CONFIRM) {
+ var pairing = this.pairingEvent_.pairing;
+ if (pairing == PairingEventType.REQUEST_PINCODE)
+ options.pincode = this.$$('#pincode').value;
+ else if (pairing == PairingEventType.REQUEST_PASSKEY)
+ options.passkey = parseInt(this.$$('#passkey').value, 10);
+ }
+ this.bluetoothPrivate.setPairingResponse(options, () => {
+ if (chrome.runtime.lastError) {
+ // TODO(stevenjb): Show error.
+ console.error(
+ 'Error setting pairing response: ' + options.device.name +
+ ': Response: ' + options.response +
+ ': Error: ' + chrome.runtime.lastError.message);
+ }
+ this.close();
+ });
+
+ this.fire('response', options);
+ },
+
+ /**
+ * @param {!PairingEventType} eventType
+ * @return {string}
+ * @private
+ */
+ getEventDesc_: function(eventType) {
+ assert(eventType);
+ if (eventType == PairingEventType.COMPLETE ||
+ eventType == PairingEventType.KEYS_ENTERED ||
+ eventType == PairingEventType.REQUEST_AUTHORIZATION) {
+ return 'bluetoothStartConnecting';
+ }
+ return 'bluetooth_' + /** @type {string} */ (eventType);
+ },
+
+ /**
+ * @param {number} index
+ * @return {string}
+ * @private
+ */
+ getPinDigit_: function(index) {
+ if (!this.pairingEvent_)
+ return '';
+ var digit = '0';
+ var pairing = this.pairingEvent_.pairing;
+ if (pairing == PairingEventType.DISPLAY_PINCODE &&
+ this.pairingEvent_.pincode &&
+ index < this.pairingEvent_.pincode.length) {
+ digit = this.pairingEvent_.pincode[index];
+ } else if (
+ this.pairingEvent_.passkey &&
+ (pairing == PairingEventType.DISPLAY_PASSKEY ||
+ pairing == PairingEventType.KEYS_ENTERED ||
+ pairing == PairingEventType.CONFIRM_PASSKEY)) {
+ var passkeyString = String(this.pairingEvent_.passkey);
+ if (index < passkeyString.length)
+ digit = passkeyString[index];
+ }
+ return digit;
+ },
+
+ /**
+ * @param {number} index
+ * @return {string}
+ * @private
+ */
+ getPinClass_: function(index) {
+ if (!this.pairingEvent_)
+ return '';
+ if (this.pairingEvent_.pairing == PairingEventType.CONFIRM_PASSKEY)
+ return 'confirm';
+ var cssClass = 'display';
+ if (this.pairingEvent_.pairing == PairingEventType.DISPLAY_PASSKEY) {
+ if (index == 0)
+ cssClass += ' next';
+ else
+ cssClass += ' untyped';
+ } else if (
+ this.pairingEvent_.pairing == PairingEventType.KEYS_ENTERED &&
+ this.pairingEvent_.enteredKey) {
+ var enteredKey = this.pairingEvent_.enteredKey; // 1-7
+ var lastKey = this.digits_.length; // 6
+ if ((index == -1 && enteredKey > lastKey) || (index + 1 == enteredKey))
+ cssClass += ' next';
+ else if (index > enteredKey)
+ cssClass += ' untyped';
+ }
+ return cssClass;
+ },
+});
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/compiled_resources2.gyp b/chromium/ui/webui/resources/cr_components/chromeos/compiled_resources2.gyp
new file mode 100644
index 00000000000..1e6afe0491a
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/compiled_resources2.gyp
@@ -0,0 +1,30 @@
+# Copyright 2017 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.
+{
+ 'targets': [
+ {
+ 'target_name': 'network_resources',
+ 'type': 'none',
+ 'dependencies': [
+ 'network/compiled_resources2.gyp:*',
+ ],
+ },
+ {
+ 'target_name': 'bluetooth_dialog',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/cr_elements/cr_dialog/compiled_resources2.gyp:cr_dialog',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
+ '<(DEPTH)/third_party/polymer/v1_0/components-chromium/iron-resizable-behavior/compiled_resources2.gyp:iron-resizable-behavior-extracted',
+ '<(DEPTH)/third_party/polymer/v1_0/components-chromium/paper-input/compiled_resources2.gyp:paper-input-extracted',
+ '<(EXTERNS_GYP):bluetooth',
+ '<(EXTERNS_GYP):bluetooth_private',
+ '<(INTERFACES_GYP):bluetooth_interface',
+ '<(INTERFACES_GYP):bluetooth_private_interface',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ ],
+}
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/compiled_resources2.gyp b/chromium/ui/webui/resources/cr_components/chromeos/network/compiled_resources2.gyp
new file mode 100644
index 00000000000..a18b88baaa0
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/compiled_resources2.gyp
@@ -0,0 +1,63 @@
+# Copyright 2016 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.
+{
+ 'targets': [
+ {
+ 'target_name': 'network_apnlist',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/cr_elements/chromeos/network/compiled_resources2.gyp:cr_onc_types',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
+ ],
+ 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'network_ip_config',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/cr_elements/chromeos/network/compiled_resources2.gyp:cr_onc_types',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
+ ],
+ 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'network_nameservers',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/cr_elements/chromeos/network/compiled_resources2.gyp:cr_onc_types',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
+ ],
+ 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'network_property_list',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/cr_elements/chromeos/network/compiled_resources2.gyp:cr_onc_types',
+ '<(DEPTH)/ui/webui/resources/cr_elements/policy/compiled_resources2.gyp:cr_policy_network_behavior',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
+ ],
+ 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'network_proxy',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/cr_elements/chromeos/network/compiled_resources2.gyp:cr_onc_types',
+ '<(DEPTH)/ui/webui/resources/cr_elements/policy/compiled_resources2.gyp:cr_policy_network_behavior',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
+ ],
+ 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'network_proxy_input',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/cr_elements/chromeos/network/compiled_resources2.gyp:cr_onc_types',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
+ ],
+ 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'network_proxy_exclusions',
+ 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ ],
+}
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.html
new file mode 100644
index 00000000000..1a2c716e7ee
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.html
@@ -0,0 +1,43 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
+<link rel="import" href="network_property_list.html">
+<link rel="import" href="network_shared_css.html">
+
+<dom-module id="network-apnlist">
+ <template>
+ <style include="network-shared md-select">
+ paper-button {
+ margin: 4px 0;
+ }
+ </style>
+ <div class="property-box">
+ <div class="start">[[i18n('networkAccessPoint')]]</div>
+ <div class="md-select-wrapper">
+ <select id="selectApn" class="md-select" on-change="onSelectApnChange_"
+ value="[[selectedApn_]]"
+ aria-label="[[i18n('networkAccessPoint')]]">
+ <template is="dom-repeat" items="[[apnSelectList_]]">
+ <option value="[[item.AccessPointName]]">[[apnDesc_(item)]]</option>
+ </template>
+ </select>
+ <span class="md-select-underline"></span>
+ </div>
+ </div>
+
+ <div class="property-box single-column indented"
+ hidden$="[[!isOtherSelected_(selectedApn_, networkProperties)]]">
+ <network-property-list on-property-change="onOtherApnChange_"
+ fields="[[otherApnFields_]]" property-dict="[[otherApn_]]"
+ edit-field-types="[[otherApnEditTypes_]]" prefix="Cellular.APN.">
+ </network-property-list>
+ <paper-button class="action-button" on-tap="onSaveOtherTap_">
+ [[i18n('save')]]
+ </paper-button>
+ </div>
+ </template>
+ <script src="network_apnlist.js"></script>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.js
new file mode 100644
index 00000000000..c4e5034e13d
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.js
@@ -0,0 +1,271 @@
+// Copyright 2015 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.
+
+/**
+ * @fileoverview Polymer element for displaying and modifying a list of cellular
+ * access points.
+ */
+Polymer({
+ is: 'network-apnlist',
+
+ behaviors: [I18nBehavior],
+
+ properties: {
+ /**
+ * The current set of properties for the network matching |guid|.
+ * @type {!CrOnc.NetworkProperties|undefined}
+ */
+ networkProperties: {
+ type: Object,
+ observer: 'networkPropertiesChanged_',
+ },
+
+ /**
+ * The CrOnc.APNProperties.AccessPointName value of the selected APN.
+ * @private
+ */
+ selectedApn_: {
+ type: String,
+ value: '',
+ },
+
+ /**
+ * Selectable list of APN dictionaries for the UI. Includes an entry
+ * corresponding to |otherApn| (see below).
+ * @private {!Array<!CrOnc.APNProperties>}
+ */
+ apnSelectList_: {
+ type: Array,
+ value: function() {
+ return [];
+ }
+ },
+
+ /**
+ * The user settable properties for a new ('other') APN. The values for
+ * AccessPointName, Username, and Password will be set to the currently
+ * active APN if it does not match an existing list entry.
+ * @private {CrOnc.APNProperties|undefined}
+ */
+ otherApn_: {
+ type: Object,
+ },
+
+ /**
+ * Array of property names to pass to the Other APN property list.
+ * @private {!Array<string>}
+ */
+ otherApnFields_: {
+ type: Array,
+ value: function() {
+ return ['AccessPointName', 'Username', 'Password'];
+ },
+ readOnly: true
+ },
+
+ /**
+ * Array of edit types to pass to the Other APN property list.
+ * @private
+ */
+ otherApnEditTypes_: {
+ type: Object,
+ value: function() {
+ return {
+ 'AccessPointName': 'String',
+ 'Username': 'String',
+ 'Password': 'Password'
+ };
+ },
+ readOnly: true
+ },
+ },
+
+ /** @const */
+ DefaultAccessPointName: 'none',
+
+ /**
+ * Polymer networkProperties changed method.
+ */
+ networkPropertiesChanged_: function() {
+ if (!this.networkProperties || !this.networkProperties.Cellular)
+ return;
+
+ /** @type {!CrOnc.APNProperties|undefined} */ var activeApn;
+ var cellular = this.networkProperties.Cellular;
+ /** @type {!chrome.networkingPrivate.ManagedAPNProperties|undefined} */ var
+ apn = cellular.APN;
+ if (apn && apn.AccessPointName) {
+ activeApn = /** @type {!CrOnc.APNProperties|undefined} */ (
+ CrOnc.getSimpleActiveProperties(apn));
+ } else if (cellular.LastGoodAPN && cellular.LastGoodAPN.AccessPointName) {
+ activeApn = cellular.LastGoodAPN;
+ }
+ this.setApnSelectList_(activeApn);
+ },
+
+ /**
+ * Sets the list of selectable APNs for the UI. Appends an 'Other' entry
+ * (see comments for |otherApn_| above).
+ * @param {CrOnc.APNProperties|undefined} activeApn The currently active APN
+ * properties.
+ * @private
+ */
+ setApnSelectList_: function(activeApn) {
+ // Copy the list of APNs from this.networkProperties.
+ var result = this.getApnList_().slice();
+
+ // Test whether |activeApn| is in the current APN list in networkProperties.
+ var activeApnInList = activeApn && result.some(function(a) {
+ return a.AccessPointName == activeApn.AccessPointName;
+ });
+
+ // If |activeApn| is specified and not in the list, use the active
+ // properties for 'other'. Otherwise use any existing 'other' properties.
+ var otherApnProperties =
+ (activeApn && !activeApnInList) ? activeApn : this.otherApn_;
+ var otherApn = this.createApnObject_(otherApnProperties);
+
+ // Always use 'Other' for the name of custom APN entries (the name does
+ // not get saved).
+ otherApn.Name = 'Other';
+
+ // If no 'active' or 'other' AccessPointName was provided, use the default.
+ otherApn.AccessPointName =
+ otherApn.AccessPointName || this.DefaultAccessPointName;
+
+ // Save the 'other' properties.
+ this.otherApn_ = otherApn;
+
+ // Append 'other' to the end of the list of APNs.
+ result.push(otherApn);
+
+ this.apnSelectList_ = result;
+ // Set selectedApn_ after dom-repeat has been stamped.
+ this.async(() => {
+ this.selectedApn_ =
+ (activeApn && activeApn.AccessPointName) || otherApn.AccessPointName;
+ });
+ },
+
+ /**
+ * @param {!CrOnc.APNProperties|undefined=} apnProperties
+ * @return {!CrOnc.APNProperties} A new APN object with properties from
+ * |apnProperties| if provided.
+ * @private
+ */
+ createApnObject_: function(apnProperties) {
+ var newApn = {AccessPointName: ''};
+ if (apnProperties)
+ Object.assign(newApn, apnProperties);
+ return newApn;
+ },
+
+ /**
+ * @return {!Array<!CrOnc.APNProperties>} The list of APN properties in
+ * |networkProperties| or an empty list if the property is not set.
+ * @private
+ */
+ getApnList_: function() {
+ if (!this.networkProperties || !this.networkProperties.Cellular)
+ return [];
+ /** @type {!chrome.networkingPrivate.ManagedAPNList|undefined} */ var
+ apnlist = this.networkProperties.Cellular.APNList;
+ if (!apnlist)
+ return [];
+ return /** @type {!Array<!CrOnc.APNProperties>} */ (
+ CrOnc.getActiveValue(apnlist));
+ },
+
+ /**
+ * Event triggered when the selectApn selection changes.
+ * @param {!Event} event
+ * @private
+ */
+ onSelectApnChange_: function(event) {
+ var target = /** @type {!HTMLSelectElement} */ (event.target);
+ var accessPointName = target.value;
+ // When selecting 'Other', don't set a change event unless a valid
+ // non-default value has been set for Other.
+ if (this.isOtherSelected_(accessPointName) &&
+ (!this.otherApn_ || !this.otherApn_.AccessPointName ||
+ this.otherApn_.AccessPointName == this.DefaultAccessPointName)) {
+ this.selectedApn_ = accessPointName;
+ return;
+ }
+ this.sendApnChange_(accessPointName);
+ },
+
+ /**
+ * Event triggered when any 'Other' APN network property changes.
+ * @param {!{detail: {field: string, value: string}}} event
+ * @private
+ */
+ onOtherApnChange_: function(event) {
+ this.set('otherApn_.' + event.detail.field, event.detail.value);
+ // Don't send a change event for 'Other' until the 'Save' button is tapped.
+ },
+
+ /**
+ * Event triggered when the Other APN 'Save' button is tapped.
+ * @param {!Event} event
+ * @private
+ */
+ onSaveOtherTap_: function(event) {
+ this.sendApnChange_(this.selectedApn_);
+ },
+
+ /**
+ * Send the apn-change event.
+ * @param {string} accessPointName
+ * @private
+ */
+ sendApnChange_: function(accessPointName) {
+ var apnList = this.getApnList_();
+ var apn = this.findApnInList(apnList, accessPointName);
+ if (apn == undefined) {
+ apn = this.createApnObject_();
+ if (this.otherApn_) {
+ apn.AccessPointName = this.otherApn_.AccessPointName;
+ apn.Username = this.otherApn_.Username;
+ apn.Password = this.otherApn_.Password;
+ }
+ }
+ this.fire('apn-change', {field: 'APN', value: apn});
+ },
+
+ /**
+ * @param {string} accessPointName
+ * @return {boolean} True if the 'other' APN is currently selected.
+ * @private
+ */
+ isOtherSelected_: function(accessPointName) {
+ if (!this.networkProperties || !this.networkProperties.Cellular)
+ return false;
+ var apnList = this.getApnList_();
+ var apn = this.findApnInList(apnList, accessPointName);
+ return apn == undefined;
+ },
+
+ /**
+ * @param {!CrOnc.APNProperties} apn
+ * @return {string} The most descriptive name for the access point.
+ * @private
+ */
+ apnDesc_: function(apn) {
+ return apn.LocalizedName || apn.Name || apn.AccessPointName;
+ },
+
+ /**
+ * @param {!Array<!CrOnc.APNProperties>} apnList
+ * @param {string} accessPointName
+ * @return {CrOnc.APNProperties|undefined} The entry in |apnList| matching
+ * |accessPointName| if it exists, or undefined.
+ * @private
+ */
+ findApnInList: function(apnList, accessPointName) {
+ return apnList.find(function(a) {
+ return a.AccessPointName == accessPointName;
+ });
+ }
+});
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.html
new file mode 100644
index 00000000000..8999e86a290
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.html
@@ -0,0 +1,30 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-toggle-button/paper-toggle-button.html">
+<link rel="import" href="network_property_list.html">
+<link rel="import" href="network_shared_css.html">
+
+<dom-module id="network-ip-config">
+ <template>
+ <style include="network-shared iron-flex"></style>
+ <div class="property-box">
+ <div id="autoIPConfigLabel" class="start">
+ [[i18n('networkIPConfigAuto')]]
+ </div>
+ <paper-toggle-button checked="{{automatic_}}" disabled="[[!editable]]"
+ aria-labelledby="autoIPConfigLabel">
+ </paper-toggle-button>
+ </div>
+ <div class="property-box single-column indented stretch"
+ hidden$="[[!ipConfig_]]">
+ <network-property-list
+ fields="[[ipConfigFields_]]" property-dict="[[ipConfig_]]"
+ edit-field-types="[[getIPEditFields_(editable, automatic_)]]"
+ on-property-change="onIPChange_">
+ </network-property-list>
+ </div>
+ </template>
+ <script src="network_ip_config.js"></script>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js
new file mode 100644
index 00000000000..519367b120a
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js
@@ -0,0 +1,214 @@
+// Copyright 2015 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.
+
+/**
+ * @fileoverview Polymer element for displaying the IP Config properties for
+ * a network state. TODO(stevenjb): Allow editing of static IP configurations
+ * when 'editable' is true.
+ */
+Polymer({
+ is: 'network-ip-config',
+
+ behaviors: [I18nBehavior],
+
+ properties: {
+ /**
+ * The network properties dictionary containing the IP Config properties to
+ * display and modify.
+ * @type {!CrOnc.NetworkProperties|undefined}
+ */
+ networkProperties: {
+ type: Object,
+ observer: 'networkPropertiesChanged_',
+ },
+
+ /**
+ * Whether or not the IP Address can be edited.
+ */
+ editable: {
+ type: Boolean,
+ value: false,
+ },
+
+ /**
+ * State of 'Configure IP Addresses Automatically'.
+ * @private
+ */
+ automatic_: {
+ type: Boolean,
+ value: true,
+ observer: 'automaticChanged_',
+ },
+
+ /**
+ * The currently visible IP Config property dictionary. The 'RoutingPrefix'
+ * property is a human-readable mask instead of a prefix length.
+ * @private {?{
+ * ipv4: !CrOnc.IPConfigUIProperties,
+ * ipv6: (!CrOnc.IPConfigUIProperties|undefined)
+ * }}
+ */
+ ipConfig_: {
+ type: Object,
+ value: null,
+ },
+
+ /**
+ * Array of properties to pass to the property list.
+ * @private {!Array<string>}
+ */
+ ipConfigFields_: {
+ type: Array,
+ value: function() {
+ return [
+ 'ipv4.IPAddress',
+ 'ipv4.RoutingPrefix',
+ 'ipv4.Gateway',
+ 'ipv6.IPAddress',
+ ];
+ },
+ readOnly: true
+ },
+ },
+
+ /**
+ * Saved static IP configuration properties when switching to 'automatic'.
+ * @private {!CrOnc.IPConfigUIProperties|undefined}
+ */
+ savedStaticIp_: undefined,
+
+ /**
+ * Polymer networkProperties changed method.
+ */
+ networkPropertiesChanged_: function(newValue, oldValue) {
+ if (!this.networkProperties)
+ return;
+
+ var properties = this.networkProperties;
+ if (newValue.GUID != (oldValue && oldValue.GUID))
+ this.savedStaticIp_ = undefined;
+
+ // Update the 'automatic' property.
+ if (properties.IPAddressConfigType) {
+ var ipConfigType = CrOnc.getActiveValue(properties.IPAddressConfigType);
+ this.automatic_ = (ipConfigType != CrOnc.IPConfigType.STATIC);
+ }
+
+ if (properties.IPConfigs || properties.StaticIPConfig) {
+ // Update the 'ipConfig' property.
+ var ipv4 = CrOnc.getIPConfigForType(properties, CrOnc.IPType.IPV4);
+ var ipv6 = CrOnc.getIPConfigForType(properties, CrOnc.IPType.IPV6);
+ this.ipConfig_ = {
+ ipv4: this.getIPConfigUIProperties_(ipv4),
+ ipv6: this.getIPConfigUIProperties_(ipv6)
+ };
+ } else {
+ this.ipConfig_ = null;
+ }
+ },
+
+ /** @private */
+ automaticChanged_: function() {
+ if (!this.automatic_) {
+ // Ensure that there is a valid IPConfig object.
+ this.ipConfig_ = this.ipConfig_ || {
+ ipv4: {
+ Gateway: '192.168.1.1',
+ IPAddress: '192.168.1.1',
+ RoutingPrefix: '255.255.255.0',
+ Type: CrOnc.IPType.IPV4,
+ },
+ };
+ this.sendStaticIpConfig_();
+ return;
+ }
+
+ // Save the static IP configuration when switching to automatic.
+ if (this.ipConfig_)
+ this.savedStaticIp_ = this.ipConfig_.ipv4;
+ // Send the change.
+ this.fire('ip-change', {
+ field: 'IPAddressConfigType',
+ value: CrOnc.IPConfigType.DHCP,
+ });
+ },
+
+ /**
+ * @param {!CrOnc.IPConfigProperties|undefined} ipconfig
+ * @return {!CrOnc.IPConfigUIProperties} A new IPConfigUIProperties object
+ * with RoutingPrefix expressed as a string mask instead of a prefix
+ * length. Returns an empty object if |ipconfig| is undefined.
+ * @private
+ */
+ getIPConfigUIProperties_: function(ipconfig) {
+ var result = {};
+ if (!ipconfig)
+ return result;
+ for (var key in ipconfig) {
+ var value = ipconfig[key];
+ if (key == 'RoutingPrefix')
+ result.RoutingPrefix = CrOnc.getRoutingPrefixAsNetmask(value);
+ else
+ result[key] = value;
+ }
+ return result;
+ },
+
+ /**
+ * @param {!CrOnc.IPConfigUIProperties} ipconfig The IP Config UI properties.
+ * @return {!CrOnc.IPConfigProperties} A new IPConfigProperties object with
+ * RoutingPrefix expressed as a a prefix length.
+ * @private
+ */
+ getIPConfigProperties_: function(ipconfig) {
+ var result = {};
+ for (var key in ipconfig) {
+ var value = ipconfig[key];
+ if (key == 'RoutingPrefix')
+ result.RoutingPrefix = CrOnc.getRoutingPrefixAsLength(value);
+ else
+ result[key] = value;
+ }
+ return result;
+ },
+
+ /**
+ * @return {Object} An object with the edit type for each editable field.
+ * @private
+ */
+ getIPEditFields_: function() {
+ if (!this.editable || this.automatic_)
+ return {};
+ return {
+ 'ipv4.IPAddress': 'String',
+ 'ipv4.RoutingPrefix': 'String',
+ 'ipv4.Gateway': 'String'
+ };
+ },
+
+ /**
+ * Event triggered when the network property list changes.
+ * @param {!{detail: {field: string, value: string}}} event The
+ * network-property-list change event.
+ * @private
+ */
+ onIPChange_: function(event) {
+ if (!this.ipConfig_)
+ return;
+ var field = event.detail.field;
+ var value = event.detail.value;
+ // Note: |field| includes the 'ipv4.' prefix.
+ this.set('ipConfig_.' + field, value);
+ this.sendStaticIpConfig_();
+ },
+
+ /** @private */
+ sendStaticIpConfig_: function() {
+ // This will also set IPAddressConfigType to STATIC.
+ this.fire('ip-change', {
+ field: 'StaticIPConfig',
+ value: this.getIPConfigProperties_(this.ipConfig_.ipv4)
+ });
+ },
+});
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_nameservers.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_nameservers.html
new file mode 100644
index 00000000000..a64f6ca853d
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_nameservers.html
@@ -0,0 +1,43 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input-container.html">
+<link rel="import" href="network_shared_css.html">
+
+<dom-module id="network-nameservers">
+ <template>
+ <style include=" network-shared md-select">
+ paper-input-container {
+ -webkit-padding-start: 4px;
+ }
+ </style>
+
+ <div class="property-box">
+ <div class="start">[[i18n('networkNameservers')]]</div>
+ <div class="md-select-wrapper">
+ <select id="nameserverType" class="md-select" on-change="onTypeChange_"
+ value="[[nameserversType_]]"
+ aria-label="[[i18n('networkNameservers')]]">
+ <template is="dom-repeat" items="[[nameserverTypeNames_]]">
+ <option value="[[item]]">[[nameserverTypeDesc_(item)]]</option>
+ </template>
+ </select>
+ <span class="md-select-underline"></span>
+ </div>
+ </div>
+
+ <div class="property-box single-column indented"
+ hidden$="[[!nameservers_.length]]">
+ <template is="dom-repeat" items="[[nameservers_]]">
+ <paper-input-container no-label-float>
+ <input id="nameserver[[index]]" is="iron-input" value="[[item]]"
+ disabled="[[!canEdit_(editable, nameserversType_)]]"
+ on-change="onValueChange_">
+ </paper-input-container>
+ </template>
+ </div>
+ </template>
+ <script src="network_nameservers.js"></script>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_nameservers.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_nameservers.js
new file mode 100644
index 00000000000..237614403be
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_nameservers.js
@@ -0,0 +1,214 @@
+// Copyright 2015 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.
+
+/**
+ * @fileoverview Polymer element for displaying network nameserver options.
+ */
+Polymer({
+ is: 'network-nameservers',
+
+ behaviors: [I18nBehavior],
+
+ properties: {
+ /**
+ * The network properties dictionary containing the nameserver properties to
+ * display and modify.
+ * @type {!CrOnc.NetworkProperties|undefined}
+ */
+ networkProperties: {
+ type: Object,
+ observer: 'networkPropertiesChanged_',
+ },
+
+ /** Whether or not the nameservers can be edited. */
+ editable: {
+ type: Boolean,
+ value: false,
+ },
+
+ /**
+ * Array of nameserver addresses stored as strings.
+ * @private {!Array<string>}
+ */
+ nameservers_: {
+ type: Array,
+ value: function() {
+ return [];
+ },
+ },
+
+ /**
+ * The selected nameserver type.
+ * @private
+ */
+ nameserversType_: {
+ type: String,
+ value: 'automatic',
+ },
+
+ /**
+ * Array of nameserver types.
+ * @private
+ */
+ nameserverTypeNames_: {
+ type: Array,
+ value: ['automatic', 'google', 'custom'],
+ readOnly: true,
+ },
+ },
+
+ /** @const */
+ GOOGLE_NAMESERVERS: [
+ '8.8.4.4',
+ '8.8.8.8',
+ ],
+
+ /** @const */
+ MAX_NAMESERVERS: 4,
+
+ /**
+ * Saved nameservers when switching to 'automatic'.
+ * @private {!Array<string>}
+ */
+ savedNameservers_: [],
+
+ /** @private */
+ networkPropertiesChanged_: function(newValue, oldValue) {
+ if (!this.networkProperties)
+ return;
+
+ if (!oldValue || newValue.GUID != oldValue.GUID)
+ this.savedNameservers_ = [];
+
+ // Update the 'nameservers' property.
+ var nameservers = [];
+ var ipv4 =
+ CrOnc.getIPConfigForType(this.networkProperties, CrOnc.IPType.IPV4);
+ if (ipv4 && ipv4.NameServers)
+ nameservers = ipv4.NameServers;
+
+ // Update the 'nameserversType' property.
+ var configType =
+ CrOnc.getActiveValue(this.networkProperties.NameServersConfigType);
+ var type;
+ if (configType == CrOnc.IPConfigType.STATIC) {
+ if (nameservers.join(',') == this.GOOGLE_NAMESERVERS.join(',')) {
+ type = 'google';
+ } else {
+ type = 'custom';
+ }
+ } else {
+ type = 'automatic';
+ }
+ this.setNameservers_(type, nameservers);
+ },
+
+ /**
+ * @param {string} nameserversType
+ * @param {!Array<string>} nameservers
+ * @private
+ */
+ setNameservers_: function(nameserversType, nameservers) {
+ if (nameserversType == 'custom') {
+ // Add empty entries for unset custom nameservers.
+ for (var i = nameservers.length; i < this.MAX_NAMESERVERS; ++i)
+ nameservers[i] = '';
+ }
+ this.nameservers_ = nameservers;
+ // Set nameserversType_ after dom-repeat has been stamped.
+ this.async(() => {
+ this.nameserversType_ = nameserversType;
+ });
+ },
+
+ /**
+ * @param {string} type The nameservers type.
+ * @return {string} The description for |type|.
+ * @private
+ */
+ nameserverTypeDesc_: function(type) {
+ // TODO(stevenjb): Translate.
+ if (type == 'custom')
+ return 'Custom name servers';
+ if (type == 'google')
+ return 'Google name servers';
+ return 'Automatic name servers';
+ },
+
+ /**
+ * @param {boolean} editable
+ * @param {string} nameserversType
+ * @return {boolean} True if the nameservers are editable.
+ * @private
+ */
+ canEdit_: function(editable, nameserversType) {
+ return editable && nameserversType == 'custom';
+ },
+
+ /**
+ * Event triggered when the selected type changes. Updates nameservers and
+ * sends the change value if necessary.
+ * @param {!Event} event
+ * @private
+ */
+ onTypeChange_: function(event) {
+ if (this.nameserversType_ == 'custom')
+ this.savedNameservers_ = this.nameservers_;
+ var target = /** @type {!HTMLSelectElement} */ (event.target);
+ var type = target.value;
+ this.nameserversType_ = type;
+ if (type == 'custom') {
+ // Restore the saved nameservers.
+ this.setNameservers_(type, this.savedNameservers_);
+ // Only send custom nameservers if they are not empty.
+ if (this.savedNameservers_.length == 0)
+ return;
+ }
+ this.sendNameServers_();
+ },
+
+ /**
+ * Event triggered when a nameserver value changes.
+ * @private
+ */
+ onValueChange_: function() {
+ if (this.nameserversType_ != 'custom') {
+ // If a user inputs Google nameservers in the custom nameservers fields,
+ // |nameserversType| will change to 'google' so don't send the values.
+ return;
+ }
+ this.sendNameServers_();
+ },
+
+ /**
+ * Sends the current nameservers type (for automatic) or value.
+ * @private
+ */
+ sendNameServers_: function() {
+ var type = this.nameserversType_;
+
+ if (type == 'custom') {
+ var nameservers = new Array(this.MAX_NAMESERVERS);
+ for (var i = 0; i < this.MAX_NAMESERVERS; ++i) {
+ var nameserverInput = this.$$('#nameserver' + i);
+ nameservers[i] = nameserverInput ? nameserverInput.value : '';
+ }
+ this.fire('nameservers-change', {
+ field: 'NameServers',
+ value: nameservers,
+ });
+ } else if (type == 'google') {
+ this.fire('nameservers-change', {
+ field: 'NameServers',
+ value: this.GOOGLE_NAMESERVERS,
+ });
+ } else {
+ // automatic
+ this.fire('nameservers-change', {
+ field: 'NameServersConfigType',
+ value: CrOnc.IPConfigType.DHCP,
+ });
+ }
+ },
+});
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list.html
new file mode 100644
index 00000000000..0d73594c0af
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list.html
@@ -0,0 +1,71 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html">
+<link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_network_behavior.html">
+<link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_network_indicator.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input-container.html">
+<link rel="import" href="network_shared_css.html">
+
+<dom-module id="network-property-list">
+ <template>
+ <style include="network-shared iron-flex">
+ paper-input-container {
+ margin-bottom: -12px;
+ margin-top: -8px;
+ }
+
+ /* Property lists are embedded; remove the padding. */
+ .property-box {
+ padding: 0;
+ }
+
+ .secondary {
+ color: var(--paper-grey-600);
+ font-weight: 400;
+ }
+
+ cr-policy-network-indicator {
+ -webkit-margin-start: var(--settings-controlled-by-spacing);
+ }
+ </style>
+ <template is="dom-repeat" items="[[fields]]"
+ filter="[[computeFilter_(prefix, propertyDict, editFieldTypes)]]">
+ <div class="property-box single-column stretch">
+ <!-- Property label -->
+ <div>[[getPropertyLabel_(item, prefix)]]</div>
+ <!-- Uneditable property value -->
+ <div class="layout horizontal"
+ hidden="[[isEditable_(item, '', propertyDict, editFieldTypes)]]">
+ <div class="secondary">
+ [[getPropertyValue_(item, prefix, propertyDict)]]
+ </div>
+ <cr-policy-network-indicator
+ property="[[getProperty_(item, propertyDict)]]">
+ </cr-policy-network-indicator>
+ </div>
+ <!-- Editable String property value -->
+ <template is="dom-if" if="[[isEditable_(
+ item, 'String', propertyDict, editFieldTypes)]]">
+ <paper-input-container no-label-float>
+ <input id="[[item]]" is="iron-input"
+ value="[[getPropertyValue_(item, prefix, propertyDict)]]"
+ on-change="onValueChange_">
+ </paper-input-container>
+ </template>
+ <!-- Editable Password property value -->
+ <template is="dom-if" if="[[isEditable_(
+ item, 'Password', propertyDict, editFieldTypes)]]">
+ <paper-input-container no-label-float>
+ <input id="[[item]]" is="iron-input" type="password"
+ value="[[getPropertyValue_(item, prefix, propertyDict)]]"
+ on-change="onValueChange_">
+ </paper-input-container>
+ </template>
+ <!-- TODO(stevenjb): Support other types. -->
+ </div>
+ </template>
+ </template>
+ <script src="network_property_list.js"></script>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list.js
new file mode 100644
index 00000000000..f333386cb78
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list.js
@@ -0,0 +1,214 @@
+// Copyright 2015 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.
+
+/**
+ * @fileoverview Polymer element for displaying a list of network properties
+ * in a list. This also supports editing fields inline for fields listed in
+ * editFieldTypes.
+ */
+Polymer({
+ is: 'network-property-list',
+
+ behaviors: [I18nBehavior, CrPolicyNetworkBehavior],
+
+ properties: {
+ /**
+ * The dictionary containing the properties to display.
+ * @type {!Object|undefined}
+ */
+ propertyDict: {type: Object},
+
+ /**
+ * Fields to display.
+ * @type {!Array<string>}
+ */
+ fields: {
+ type: Array,
+ value: function() {
+ return [];
+ },
+ },
+
+ /**
+ * Edit type of editable fields. May contain a property for any field in
+ * |fields|. Other properties will be ignored. Property values can be:
+ * 'String' - A text input will be displayed.
+ * 'Password' - A string with input type = password.
+ * TODO(stevenjb): Support types with custom validation, e.g. IPAddress.
+ * TODO(stevenjb): Support 'Number'.
+ * When a field changes, the 'property-change' event will be fired with
+ * the field name and the new value provided in the event detail.
+ */
+ editFieldTypes: {
+ type: Object,
+ value: function() {
+ return {};
+ },
+ },
+
+ /** Prefix used to look up property key translations. */
+ prefix: {
+ type: String,
+ value: '',
+ },
+ },
+
+ /**
+ * Event triggered when an input field changes. Fires a 'property-change'
+ * event with the field (property) name set to the target id, and the value
+ * set to the target input value.
+ * @param {!Event} event The input change event.
+ * @private
+ */
+ onValueChange_: function(event) {
+ if (!this.propertyDict)
+ return;
+ var field = event.target.id;
+ var curValue = this.get(field, this.propertyDict);
+ if (typeof curValue == 'object') {
+ // Extract the property from an ONC managed dictionary.
+ curValue = CrOnc.getActiveValue(
+ /** @type {!CrOnc.ManagedProperty} */ (curValue));
+ }
+ var newValue = event.target.value;
+ if (newValue == curValue)
+ return;
+ this.fire('property-change', {field: field, value: newValue});
+ },
+
+ /**
+ * @param {string} key The property key.
+ * @param {string} prefix
+ * @return {string} The text to display for the property label.
+ * @private
+ */
+ getPropertyLabel_: function(key, prefix) {
+ var oncKey = 'Onc' + prefix + key;
+ oncKey = oncKey.replace(/\./g, '-');
+ if (this.i18nExists(oncKey))
+ return this.i18n(oncKey);
+ // We do not provide translations for every possible network property key.
+ // For keys specific to a type, strip the type prefix.
+ var result = prefix + key;
+ for (var entry in chrome.networkingPrivate.NetworkType) {
+ var type = chrome.networkingPrivate.NetworkType[entry];
+ if (result.startsWith(type + '.')) {
+ result = result.substr(type.length + 1);
+ break;
+ }
+ }
+ return result;
+ },
+
+ /**
+ * Generates a filter function dependent on propertyDict and editFieldTypes.
+ * @param {string} prefix
+ * @param {!Object} propertyDict
+ * @param {!Object} editFieldTypes
+ * @private
+ */
+ computeFilter_: function(prefix, propertyDict, editFieldTypes) {
+ return key => {
+ if (editFieldTypes.hasOwnProperty(key))
+ return true;
+ var value = this.getPropertyValue_(key, prefix, propertyDict);
+ return value !== undefined && value !== '';
+ };
+ },
+
+ /**
+ * @param {string} key The property key.
+ * @param {string} type The field type.
+ * @param {!Object} propertyDict
+ * @param {!Object} editFieldTypes
+ * @return {boolean}
+ * @private
+ */
+ isEditable_: function(key, type, propertyDict, editFieldTypes) {
+ var property = /** @type {!CrOnc.ManagedProperty|undefined} */ (
+ this.get(key, propertyDict));
+ if (this.isNetworkPolicyEnforced(property))
+ return false;
+ var editType = editFieldTypes[key];
+ return editType !== undefined && (type == '' || editType == type);
+ },
+
+ /**
+ * @param {string} key The property key.
+ * @param {!Object} propertyDict
+ * @return {*} The managed property dictionary associated with |key|.
+ * @private
+ */
+ getProperty_: function(key, propertyDict) {
+ return this.get(key, propertyDict);
+ },
+
+ /**
+ * @param {string} key The property key.
+ * @param {string} prefix
+ * @param {!Object} propertyDict
+ * @return {string} The text to display for the property value.
+ * @private
+ */
+ getPropertyValue_: function(key, prefix, propertyDict) {
+ var value = this.get(key, propertyDict);
+ if (value === undefined)
+ return '';
+ if (typeof value == 'object') {
+ // Extract the property from an ONC managed dictionary
+ value =
+ CrOnc.getActiveValue(/** @type {!CrOnc.ManagedProperty} */ (value));
+ }
+ var customValue = this.getCustomPropertyValue_(key, value);
+ if (customValue)
+ return customValue;
+ if (typeof value == 'number' || typeof value == 'boolean')
+ return value.toString();
+ assert(typeof value == 'string');
+ var valueStr = /** @type {string} */ (value);
+ var oncKey = 'Onc' + prefix + key;
+ oncKey = oncKey.replace(/\./g, '-');
+ oncKey += '_' + valueStr;
+ if (this.i18nExists(oncKey))
+ return this.i18n(oncKey);
+ return valueStr;
+ },
+
+ /**
+ * @param {string} key The property key.
+ * @param {*} value The property value.
+ * @return {string} The text to display for the property value. If the key
+ * does not correspond to a custom property, an empty string is returned.
+ */
+ getCustomPropertyValue_: function(key, value) {
+ if (key == 'Tether.BatteryPercentage') {
+ assert(typeof value == 'number');
+ return this.i18n('OncTether-BatteryPercentage_Value', value.toString());
+ }
+
+ if (key == 'Tether.SignalStrength') {
+ assert(typeof value == 'number');
+ // Possible |signalStrength| values should be 0, 25, 50, 75, and 100. Add
+ // <= checks for robustness.
+ if (value <= 24)
+ return this.i18n('OncTether-SignalStrength_Weak');
+ if (value <= 49)
+ return this.i18n('OncTether-SignalStrength_Okay');
+ if (value <= 74)
+ return this.i18n('OncTether-SignalStrength_Good');
+ if (value <= 99)
+ return this.i18n('OncTether-SignalStrength_Strong');
+ return this.i18n('OncTether-SignalStrength_VeryStrong');
+ }
+
+ if (key == 'Tether.Carrier') {
+ assert(typeof value == 'string');
+ return (!value || value == 'unknown-carrier') ?
+ this.i18n('tetherUnknownCarrier') :
+ value;
+ }
+
+ return '';
+ },
+});
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy.html
new file mode 100644
index 00000000000..4b328f9145b
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy.html
@@ -0,0 +1,154 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html">
+<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
+<link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_network_behavior.html">
+<link rel="import" href="chrome://resources/html/assert.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/html/md_select_css.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-keys/iron-a11y-keys.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input-container.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-toggle-button/paper-toggle-button.html">
+<link rel="import" href="network_proxy_exclusions.html">
+<link rel="import" href="network_proxy_input.html">
+<link rel="import" href="network_shared_css.html">
+
+<dom-module id="network-proxy">
+ <template>
+ <style include="network-shared md-select cr-hidden-style iron-flex iron-flex-alignment">
+ network-proxy-input {
+ margin-bottom: 10px;
+ }
+
+ network-proxy-exclusions {
+ margin: 10px 0;
+ }
+
+ #manualProxy {
+ -webkit-padding-start: var(--cr-section-padding);
+ }
+ </style>
+
+ <!-- Proxy type dropdown -->
+ <div class="property-box">
+ <div class="start">[[i18n('networkProxyConnectionType')]]</div>
+ <div class="md-select-wrapper">
+ <select id="proxyType" class="md-select" on-change="onTypeChange_"
+ value="[[proxy_.Type]]"
+ disabled="[[!isEditable_('Type', networkProperties, editable,
+ useSharedProxies)]]"
+ aria-label="[[i18n('networkProxyConnectionType')]]">
+ <template is="dom-repeat" items="[[proxyTypes_]]">
+ <option value="[[item]]">[[getProxyTypeDesc_(item)]]</option>
+ </template>
+ </select>
+ <span class="md-select-underline"></span>
+ </div>
+ </div>
+
+ <!-- Autoconfiguration (PAC) -->
+ <div class="property-box indented"
+ hidden$="[[!matches_(proxy_.Type, ProxySettingsType_.PAC)]]">
+ <div>[[i18n('networkProxyAutoConfig')]]</div>
+ <paper-input no-label-float class="middle" value="{{proxy_.PAC}}"
+ disabled="[[!isEditable_('PAC', networkProperties, editable,
+ useSharedProxies)]]"
+ on-change="onPACChange_">
+ </paper-input>
+ </div>
+
+ <!-- Web Proxy Auto Discovery (WPAD) -->
+ <div class="property-box indented"
+ hidden$="[[!matches_(proxy_.Type, ProxySettingsType_.WPAD)]]">
+ <div>[[i18n('networkProxyWpad')]]</div>
+ <div class="middle">[[WPAD_]]</div>
+ </div>
+
+ <!-- Manual -->
+ <div class="property-box indented"
+ hidden$="[[!matches_(proxy_.Type, ProxySettingsType_.MANUAL)]]">
+ <div id="networkProxyToggleLabel" class="flex">
+ [[i18n('networkProxyUseSame')]]
+ </div>
+ <paper-toggle-button checked="{{useSameProxy_}}"
+ disabled="[[!isEditable_('Type', networkProperties, editable,
+ useSharedProxies)]]"
+ aria-labelledby="networkProxyToggleLabel">
+ </paper-toggle-button>
+ </div>
+
+ <div id="manualProxy" class="layout vertical start"
+ hidden$="[[!matches_(proxy_.Type, ProxySettingsType_.MANUAL)]]">
+ <div hidden$="[[!useSameProxy_]]" class="layout vertical">
+ <network-proxy-input
+ on-proxy-change="onProxyInputChange_"
+ editable="[[isEditable_('Manual.HTTPProxy.Host', networkProperties,
+ editable, useSharedProxies)]]"
+ value="{{proxy_.Manual.HTTPProxy}}"
+ label="[[i18n('networkProxy')]]">
+ </network-proxy-input>
+ </div>
+ <div hidden$="[[useSameProxy_]]" class="layout vertical">
+ <network-proxy-input
+ on-proxy-change="onProxyInputChange_"
+ editable="[[isEditable_('Manual.HTTPProxy.Host', networkProperties,
+ editable, useSharedProxies)]]"
+ value="{{proxy_.Manual.HTTPProxy}}"
+ label="[[i18n('networkProxyHttp')]]">
+ </network-proxy-input>
+ <network-proxy-input
+ on-proxy-change="onProxyInputChange_"
+ editable="[[isEditable_('Manual.SecureHTTPProxy.Host',
+ networkProperties, editable, useSharedProxies)]]"
+ value="{{proxy_.Manual.SecureHTTPProxy}}"
+ label="[[i18n('networkProxyShttp')]]">
+ </network-proxy-input>
+ <network-proxy-input
+ on-proxy-change="onProxyInputChange_"
+ editable="[[isEditable_('Manual.FTPProxy.Host', networkProperties,
+ editable, useSharedProxies)]]"
+ value="{{proxy_.Manual.FTPProxy}}"
+ label="[[i18n('networkProxyFtp')]]">
+ </network-proxy-input>
+ <network-proxy-input
+ on-proxy-change="onProxyInputChange_"
+ editable="[[isEditable_('Manual.SOCKS.Host', networkProperties,
+ editable, useSharedProxies)]]"
+ value="{{proxy_.Manual.SOCKS}}"
+ label="[[i18n('networkProxySocks')]]">
+ </network-proxy-input>
+ </div>
+
+ <div hidden="[[!isEditable_('Type', networkProperties, editable,
+ useSharedProxies)]]">
+ <div>[[i18n('networkProxyExceptionList')]]</div>
+ <network-proxy-exclusions on-proxy-change="onProxyExclusionsChange_"
+ exclusions="{{proxy_.ExcludeDomains}}">
+ </network-proxy-exclusions>
+ <div class="layout horizontal">
+ <paper-input-container no-label-float class="flex">
+ <input id="proxyExclusion" is="iron-input">
+ <iron-a11y-keys keys="enter"
+ on-keys-pressed="onAddProxyExclusionTap_">
+ </iron-a11y-keys>
+ </paper-input-container>
+ <paper-button on-tap="onAddProxyExclusionTap_">
+ [[i18n('networkProxyAddException')]]
+ </paper-button>
+ </div>
+ </div>
+
+ <paper-button id="saveManualProxy"
+ on-tap="onSaveProxyTap_" class="action-button"
+ disabled="[[!isSaveManualProxyEnabled_(networkProperties,
+ proxyModified_, proxy_.*)]]">
+ [[i18n('save')]]
+ </paper-button>
+ </div>
+
+ </template>
+ <script src="network_proxy.js"></script>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy.js
new file mode 100644
index 00000000000..a1f2056eba8
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy.js
@@ -0,0 +1,428 @@
+// Copyright 2017 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.
+
+/**
+ * @fileoverview Polymer element for displaying and editing network proxy
+ * values.
+ */
+Polymer({
+ is: 'network-proxy',
+
+ behaviors: [
+ CrPolicyNetworkBehavior,
+ I18nBehavior,
+ ],
+
+ properties: {
+ /**
+ * The network properties dictionary containing the proxy properties to
+ * display and modify.
+ * @type {!CrOnc.NetworkProperties|undefined}
+ */
+ networkProperties: {
+ type: Object,
+ observer: 'networkPropertiesChanged_',
+ },
+
+ /** Whether or not the proxy values can be edited. */
+ editable: {
+ type: Boolean,
+ value: false,
+ },
+
+ /** Whether shared proxies are allowed. */
+ useSharedProxies: {
+ type: Boolean,
+ value: false,
+ observer: 'updateProxy_',
+ },
+
+ /**
+ * UI visible / edited proxy configuration.
+ * @private {!CrOnc.ProxySettings}
+ */
+ proxy_: {
+ type: Object,
+ value: function() {
+ return this.createDefaultProxySettings_();
+ },
+ },
+
+ /**
+ * The Web Proxy Auto Discovery URL extracted from networkProperties.
+ * @private
+ */
+ WPAD_: {
+ type: String,
+ value: '',
+ },
+
+ /**
+ * Whether or not to use the same manual proxy for all protocols.
+ * @private
+ */
+ useSameProxy_: {
+ type: Boolean,
+ value: false,
+ observer: 'useSameProxyChanged_',
+ },
+
+ /**
+ * Array of proxy configuration types.
+ * @private {!Array<string>}
+ * @const
+ */
+ proxyTypes_: {
+ type: Array,
+ value: [
+ CrOnc.ProxySettingsType.DIRECT,
+ CrOnc.ProxySettingsType.PAC,
+ CrOnc.ProxySettingsType.WPAD,
+ CrOnc.ProxySettingsType.MANUAL,
+ ],
+ readOnly: true
+ },
+
+ /**
+ * Object providing proxy type values for data binding.
+ * @private {!Object}
+ * @const
+ */
+ ProxySettingsType_: {
+ type: Object,
+ value: {
+ DIRECT: CrOnc.ProxySettingsType.DIRECT,
+ PAC: CrOnc.ProxySettingsType.PAC,
+ MANUAL: CrOnc.ProxySettingsType.MANUAL,
+ WPAD: CrOnc.ProxySettingsType.WPAD,
+ },
+ readOnly: true,
+ },
+ },
+
+ /**
+ * Saved Manual properties so that switching to another type does not loose
+ * any set properties while the UI is open.
+ * @private {!CrOnc.ManualProxySettings|undefined}
+ */
+ savedManual_: undefined,
+
+ /**
+ * Saved ExcludeDomains properties so that switching to a non-Manual type does
+ * not loose any set exclusions while the UI is open.
+ * @private {!Array<string>|undefined}
+ */
+ savedExcludeDomains_: undefined,
+
+ /**
+ * Set to true while modifying proxy values so that an update does not
+ * override the edited values.
+ * @private {boolean}
+ */
+ proxyModified_: false,
+
+ /** @override */
+ attached: function() {
+ this.reset();
+ },
+
+ /**
+ * Called any time the page is refreshed or navigated to so that the proxy
+ * is updated correctly.
+ */
+ reset: function() {
+ this.proxyModified_ = false;
+ this.proxy_ = this.createDefaultProxySettings_();
+ this.updateProxy_();
+ },
+
+ /** @private */
+ networkPropertiesChanged_: function() {
+ if (this.proxyModified_)
+ return; // Ignore update.
+ this.updateProxy_();
+ },
+
+ /** @private */
+ updateProxy_: function() {
+ if (!this.networkProperties)
+ return;
+
+ /** @type {!CrOnc.ProxySettings} */
+ var proxy = this.createDefaultProxySettings_();
+
+ // For shared networks with unmanaged proxy settings, ignore any saved
+ // proxy settings (use the default values).
+ if (this.isShared_()) {
+ var property = this.getProxySettingsTypeProperty_();
+ if (!this.isControlled(property) && !this.useSharedProxies) {
+ this.setProxyAsync_(proxy);
+ return; // Proxy settings will be ignored.
+ }
+ }
+
+ /** @type {!chrome.networkingPrivate.ManagedProxySettings|undefined} */
+ var proxySettings = this.networkProperties.ProxySettings;
+ if (proxySettings) {
+ proxy.Type = /** @type {!CrOnc.ProxySettingsType} */ (
+ CrOnc.getActiveValue(proxySettings.Type));
+ if (proxySettings.Manual) {
+ proxy.Manual.HTTPProxy = /** @type {!CrOnc.ProxyLocation|undefined} */ (
+ CrOnc.getSimpleActiveProperties(
+ proxySettings.Manual.HTTPProxy)) ||
+ {Host: '', Port: 80};
+ proxy.Manual.SecureHTTPProxy =
+ /** @type {!CrOnc.ProxyLocation|undefined} */ (
+ CrOnc.getSimpleActiveProperties(
+ proxySettings.Manual.SecureHTTPProxy)) ||
+ {Host: '', Port: 80};
+ proxy.Manual.FTPProxy =
+ /** @type {!CrOnc.ProxyLocation|undefined} */ (
+ CrOnc.getSimpleActiveProperties(
+ proxySettings.Manual.FTPProxy)) ||
+ {Host: '', Port: 80};
+ proxy.Manual.SOCKS =
+ /** @type {!CrOnc.ProxyLocation|undefined} */ (
+ CrOnc.getSimpleActiveProperties(proxySettings.Manual.SOCKS)) ||
+ {Host: '', Port: 80};
+ var jsonHttp = proxy.Manual.HTTPProxy;
+ this.useSameProxy_ =
+ (CrOnc.proxyMatches(jsonHttp, proxy.Manual.SecureHTTPProxy) &&
+ CrOnc.proxyMatches(jsonHttp, proxy.Manual.FTPProxy) &&
+ CrOnc.proxyMatches(jsonHttp, proxy.Manual.SOCKS)) ||
+ (!proxy.Manual.SecureHTTPProxy.Host &&
+ !proxy.Manual.FTPProxy.Host && !proxy.Manual.SOCKS.Host);
+ }
+ if (proxySettings.ExcludeDomains) {
+ proxy.ExcludeDomains = /** @type {!Array<string>|undefined} */ (
+ CrOnc.getActiveValue(proxySettings.ExcludeDomains));
+ }
+ proxy.PAC = /** @type {string|undefined} */ (
+ CrOnc.getActiveValue(proxySettings.PAC));
+ }
+ // Use saved ExcludeDomains and Manual if not defined.
+ proxy.ExcludeDomains = proxy.ExcludeDomains || this.savedExcludeDomains_;
+ proxy.Manual = proxy.Manual || this.savedManual_;
+
+ // Set the Web Proxy Auto Discovery URL.
+ var ipv4 =
+ CrOnc.getIPConfigForType(this.networkProperties, CrOnc.IPType.IPV4);
+ this.WPAD_ = (ipv4 && ipv4.WebProxyAutoDiscoveryUrl) || '';
+
+ this.setProxyAsync_(proxy);
+ },
+
+ /**
+ * @param {!CrOnc.ProxySettings} proxy
+ * @private
+ */
+ setProxyAsync_: function(proxy) {
+ // Set this.proxy_ after dom-repeat has been stamped.
+ this.async(() => {
+ this.proxy_ = proxy;
+ this.proxyModified_ = false;
+ });
+ },
+
+ /** @private */
+ useSameProxyChanged_: function() {
+ this.proxyModified_ = true;
+ },
+
+ /**
+ * @return {CrOnc.ProxySettings} An empty/default proxy settings object.
+ * @private
+ */
+ createDefaultProxySettings_: function() {
+ return {
+ Type: CrOnc.ProxySettingsType.DIRECT,
+ ExcludeDomains: [],
+ Manual: {
+ HTTPProxy: {Host: '', Port: 80},
+ SecureHTTPProxy: {Host: '', Port: 80},
+ FTPProxy: {Host: '', Port: 80},
+ SOCKS: {Host: '', Port: 1080}
+ },
+ PAC: ''
+ };
+ },
+
+ /**
+ * Called when the proxy changes in the UI.
+ * @private
+ */
+ sendProxyChange_: function() {
+ var proxy =
+ /** @type {!CrOnc.ProxySettings} */ (Object.assign({}, this.proxy_));
+ if (proxy.Type == CrOnc.ProxySettingsType.MANUAL) {
+ var manual = proxy.Manual;
+ var defaultProxy = manual.HTTPProxy || {Host: '', Port: 80};
+ if (this.useSameProxy_) {
+ proxy.Manual.SecureHTTPProxy = /** @type {!CrOnc.ProxyLocation} */ (
+ Object.assign({}, defaultProxy));
+ proxy.Manual.FTPProxy = /** @type {!CrOnc.ProxyLocation} */ (
+ Object.assign({}, defaultProxy));
+ proxy.Manual.SOCKS = /** @type {!CrOnc.ProxyLocation} */ (
+ Object.assign({}, defaultProxy));
+ } else {
+ // Remove properties with empty hosts to unset them.
+ if (manual.HTTPProxy && !manual.HTTPProxy.Host)
+ delete manual.HTTPProxy;
+ if (manual.SecureHTTPProxy && !manual.SecureHTTPProxy.Host)
+ delete manual.SecureHTTPProxy;
+ if (manual.FTPProxy && !manual.FTPProxy.Host)
+ delete manual.FTPProxy;
+ if (manual.SOCKS && !manual.SOCKS.Host)
+ delete manual.SOCKS;
+ }
+ this.savedManual_ = Object.assign({}, manual);
+ this.savedExcludeDomains_ = proxy.ExcludeDomains;
+ } else if (proxy.Type == CrOnc.ProxySettingsType.PAC) {
+ if (!proxy.PAC)
+ return;
+ }
+ this.fire('proxy-change', {field: 'ProxySettings', value: proxy});
+ this.proxyModified_ = false;
+ },
+
+ /**
+ * Event triggered when the selected proxy type changes.
+ * @param {!Event} event
+ * @private
+ */
+ onTypeChange_: function(event) {
+ var target = /** @type {!HTMLSelectElement} */ (event.target);
+ var type = /** @type {chrome.networkingPrivate.ProxySettingsType} */ (
+ target.value);
+ this.set('proxy_.Type', type);
+ if (type == CrOnc.ProxySettingsType.MANUAL)
+ this.proxyModified_ = true;
+ else
+ this.sendProxyChange_();
+ },
+
+ /** @private */
+ onPACChange_: function() {
+ this.sendProxyChange_();
+ },
+
+ /** @private */
+ onProxyInputChange_: function() {
+ this.proxyModified_ = true;
+ },
+
+ /**
+ * Event triggered when a proxy exclusion is added.
+ * @param {!Event} event The add proxy exclusion event.
+ * @private
+ */
+ onAddProxyExclusionTap_: function(event) {
+ var value = this.$.proxyExclusion.value;
+ if (!value)
+ return;
+ this.push('proxy_.ExcludeDomains', value);
+ // Clear input.
+ this.$.proxyExclusion.value = '';
+ this.proxyModified_ = true;
+ },
+
+ /**
+ * Event triggered when the proxy exclusion list changes.
+ * @param {!Event} event The remove proxy exclusions change event.
+ * @private
+ */
+ onProxyExclusionsChange_: function(event) {
+ this.proxyModified_ = true;
+ },
+
+ /** @private */
+ onSaveProxyTap_: function() {
+ this.sendProxyChange_();
+ },
+
+ /**
+ * @param {string} proxyType The proxy type.
+ * @return {string} The description for |proxyType|.
+ * @private
+ */
+ getProxyTypeDesc_: function(proxyType) {
+ if (proxyType == CrOnc.ProxySettingsType.MANUAL)
+ return this.i18n('networkProxyTypeManual');
+ if (proxyType == CrOnc.ProxySettingsType.PAC)
+ return this.i18n('networkProxyTypePac');
+ if (proxyType == CrOnc.ProxySettingsType.WPAD)
+ return this.i18n('networkProxyTypeWpad');
+ return this.i18n('networkProxyTypeDirect');
+ },
+
+ /**
+ * @return {!CrOnc.ManagedProperty|undefined}
+ * @private
+ */
+ getProxySettingsTypeProperty_: function() {
+ return /** @type {!CrOnc.ManagedProperty|undefined} */ (
+ this.get('ProxySettings.Type', this.networkProperties));
+ },
+
+ /**
+ * @param {string} propertyName
+ * @return {boolean} Whether the named property setting is editable.
+ * @private
+ */
+ isEditable_: function(propertyName) {
+ if (!this.editable || (this.isShared_() && !this.useSharedProxies))
+ return false;
+ if (!this.networkProperties.hasOwnProperty('ProxySettings'))
+ return true; // No proxy settings defined, so not enforced.
+ var property = /** @type {!CrOnc.ManagedProperty|undefined} */ (
+ this.get('ProxySettings.' + propertyName, this.networkProperties));
+ if (!property)
+ return true;
+ return this.isPropertyEditable_(property);
+ },
+
+ /**
+ * @param {!CrOnc.ManagedProperty|undefined} property
+ * @return {boolean} Whether |property| is editable.
+ * @private
+ */
+ isPropertyEditable_: function(property) {
+ return !this.isNetworkPolicyEnforced(property) &&
+ !this.isExtensionControlled(property);
+ },
+
+ /**
+ * @return {boolean}
+ * @private
+ */
+ isShared_: function() {
+ return this.networkProperties.Source == 'Device' ||
+ this.networkProperties.Source == 'DevicePolicy';
+ },
+
+ /**
+ * @return {boolean}
+ * @private
+ */
+ isSaveManualProxyEnabled_: function() {
+ if (!this.proxyModified_)
+ return false;
+ var manual = this.proxy_.Manual;
+ var httpHost = this.get('HTTPProxy.Host', manual);
+ if (this.useSameProxy_)
+ return !!httpHost;
+ return !!httpHost || !!this.get('SecureHTTPProxy.Host', manual) ||
+ !!this.get('FTPProxy.Host', manual) || !!this.get('SOCKS.Host', manual);
+ },
+
+ /**
+ * @param {string} property The property to test
+ * @param {string} value The value to test against
+ * @return {boolean} True if property == value
+ * @private
+ */
+ matches_: function(property, value) {
+ return property == value;
+ },
+});
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.html
new file mode 100644
index 00000000000..f574252c285
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.html
@@ -0,0 +1,37 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
+<link rel="import" href="chrome://resources/cr_elements/icons.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
+<link rel="import" href="network_shared_css.html">
+
+<dom-module id="network-proxy-exclusions">
+ <template>
+ <style include="network-shared cr-hidden-style iron-flex">
+ iron-icon {
+ @apply(--cr-actionable);
+ margin: 5px;
+ }
+
+ #container {
+ align-self: stretch;
+ border: 1px solid lightgrey;
+ height: 100px;
+ margin-top: 10px;
+ overflow-y: auto;
+ padding: 5px;
+ }
+ </style>
+ <div id="container">
+ <template is="dom-repeat" items="[[exclusions]]">
+ <div class="layout horizontal center" tabindex="0" >
+ <div class="flex">[[item]]</div>
+ <iron-icon class="favicon-image" icon="cr:clear"
+ on-tap="onRemoveTap_" tabindex="0" hidden="[[!editable]]">
+ </iron-icon>
+ </div>
+ </template>
+ </div>
+ </template>
+ <script src="network_proxy_exclusions.js"></script>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.js
new file mode 100644
index 00000000000..347b22980d3
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.js
@@ -0,0 +1,46 @@
+// Copyright 2015 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.
+
+/**
+ * @fileoverview Polymer element for displaying a list of proxy exclusions.
+ * Includes UI for adding, changing, and removing entries.
+ */
+
+(function() {
+
+Polymer({
+ is: 'network-proxy-exclusions',
+
+ properties: {
+ /** Whether or not the proxy values can be edited. */
+ editable: {
+ type: Boolean,
+ value: false,
+ },
+
+ /**
+ * The list of exclusions.
+ * @type {!Array<string>}
+ */
+ exclusions: {
+ type: Array,
+ value: function() {
+ return [];
+ },
+ notify: true
+ }
+ },
+
+ /**
+ * Event triggered when an item is removed.
+ * @param {!{model: !{index: number}}} event
+ * @private
+ */
+ onRemoveTap_: function(event) {
+ var index = event.model.index;
+ this.splice('exclusions', index, 1);
+ this.fire('proxy-change');
+ }
+});
+})();
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.html
new file mode 100644
index 00000000000..a1104bde59f
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.html
@@ -0,0 +1,50 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-input/iron-input.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input-container.html">
+<link rel="import" href="network_shared_css.html">
+
+<dom-module id="network-proxy-input">
+ <template>
+ <style include="network-shared">
+ paper-input-container {
+ -webkit-margin-start: 10px;
+ margin-bottom: -12px;
+ margin-top: -8px;
+ }
+
+ #container {
+ align-items: center;
+ display: flex;
+ flex: 0 1 auto;
+ flex-direction: row;
+ }
+
+ #label {
+ flex: 1;
+ }
+
+ #host {
+ width: 200px;
+ }
+
+ #port {
+ width: 50px;
+ }
+ </style>
+ <div id="container">
+ <div id="label">[[label]]</div>
+ <paper-input-container id="host" no-label-float>
+ <input is="iron-input" bind-value="{{value.Host}}"
+ disabled="[[!editable]]" on-change="onValueChange_">
+ </paper-input-container>
+ <div>[[i18n('networkProxyPort')]]</div>
+ <paper-input-container id="port" no-label-float>
+ <input is="iron-input" bind-value="{{value.Port}}"
+ disabled="[[!editable]]" on-change="onValueChange_">
+ </paper-input-container>
+ </div>
+ </template>
+ <script src="network_proxy_input.js"></script>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.js
new file mode 100644
index 00000000000..63609b074ce
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.js
@@ -0,0 +1,57 @@
+// Copyright 2015 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.
+
+/**
+ * @fileoverview Polymer element for displaying and editing a single
+ * network proxy value. When the URL or port changes, a 'proxy-change' event is
+ * fired with the combined url and port values passed as a single string,
+ * url:port.
+ */
+Polymer({
+ is: 'network-proxy-input',
+
+ behaviors: [I18nBehavior],
+
+ properties: {
+ /**
+ * Whether or not the proxy value can be edited.
+ */
+ editable: {
+ type: Boolean,
+ value: false,
+ },
+
+ /**
+ * A label for the proxy value.
+ */
+ label: {
+ type: String,
+ value: 'Proxy',
+ },
+
+ /**
+ * The proxy object.
+ * @type {!CrOnc.ProxyLocation}
+ */
+ value: {
+ type: Object,
+ value: function() {
+ return {Host: '', Port: 80};
+ },
+ notify: true,
+ },
+ },
+
+ /**
+ * Event triggered when an input value changes.
+ * @private
+ */
+ onValueChange_: function() {
+ var port = parseInt(this.value.Port, 10);
+ if (isNaN(port))
+ port = 80;
+ this.value.Port = port;
+ this.fire('proxy-change', {value: this.value});
+ }
+});
diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_shared_css.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_shared_css.html
new file mode 100644
index 00000000000..c235c50288d
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_shared_css.html
@@ -0,0 +1,51 @@
+<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
+
+ <!-- Common styles for network elements. -->
+
+<dom-module id="network-shared">
+ <template>
+ <style include="cr-shared-style">
+ .property-box {
+ @apply(--cr-section);
+ border-top: none;
+ min-height: var(--cr-section-two-line-min-height);
+ padding: 0;
+ }
+
+ .property-box.indented {
+ -webkit-margin-start: var(--cr-section-padding);
+ }
+
+ .property-box.single-column {
+ align-items: flex-start;
+ flex-direction: column;
+ justify-content: center;
+ }
+
+ .property-box.stretch {
+ align-items: stretch;
+ }
+
+ .property-box > .start {
+ align-items: center;
+ flex: auto;
+ }
+
+ .property-box > .middle {
+ -webkit-padding-start: 16px;
+ align-items: center;
+ flex: auto;
+ }
+
+ paper-input-container {
+ --paper-input-container-input: {
+ color: var(--paper-grey-600);
+ font-size: inherit;
+ font-weight: 400;
+ };
+ margin-bottom: 0;
+ margin-top: -9px;
+ }
+ </style>
+ </template>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_components/compiled_resources2.gyp b/chromium/ui/webui/resources/cr_components/compiled_resources2.gyp
new file mode 100644
index 00000000000..eb688de9083
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components/compiled_resources2.gyp
@@ -0,0 +1,15 @@
+# Copyright 2017 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.
+{
+ 'targets': [
+ {
+ 'target_name': 'cr_components_resources',
+ 'type': 'none',
+ 'dependencies': [
+ 'chromeos/compiled_resources2.gyp:*',
+ 'certificate_manager/compiled_resources2.gyp:*',
+ ],
+ },
+ ]
+}
diff --git a/chromium/ui/webui/resources/cr_components_resources.grdp b/chromium/ui/webui/resources/cr_components_resources.grdp
new file mode 100644
index 00000000000..164e20e627f
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_components_resources.grdp
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+ <if expr="use_nss_certs">
+ <structure name="IDR_WEBUI_CA_TRUST_EDIT_DIALOG_JS"
+ file="cr_components/certificate_manager/ca_trust_edit_dialog.js"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CA_TRUST_EDIT_DIALOG_HTML"
+ file="cr_components/certificate_manager/ca_trust_edit_dialog.html"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CERTIFICATE_DELETE_CONFIRMATION_DIALOG_JS"
+ file="cr_components/certificate_manager/certificate_delete_confirmation_dialog.js"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CERTIFICATE_DELETE_CONFIRMATION_DIALOG_HTML"
+ file="cr_components/certificate_manager/certificate_delete_confirmation_dialog.html"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CERTIFICATE_MANAGER_HTML"
+ file="cr_components/certificate_manager/certificate_manager.html"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CERTIFICATE_MANAGER_JS"
+ file="cr_components/certificate_manager/certificate_manager.js"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CERTIFICATE_PASSWORD_ENCRYPTION_DIALOG_JS"
+ file="cr_components/certificate_manager/certificate_password_encryption_dialog.js"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CERTIFICATE_PASSWORD_ENCRYPTION_DIALOG_HTML"
+ file="cr_components/certificate_manager/certificate_password_encryption_dialog.html"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CERTIFICATE_PASSWORD_DECRYPTION_DIALOG_JS"
+ file="cr_components/certificate_manager/certificate_password_decryption_dialog.js"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CERTIFICATE_PASSWORD_DECRYPTION_DIALOG_HTML"
+ file="cr_components/certificate_manager/certificate_password_decryption_dialog.html"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CERTIFICATES_ERROR_DIALOG_JS"
+ file="cr_components/certificate_manager/certificates_error_dialog.js"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CERTIFICATES_ERROR_DIALOG_HTML"
+ file="cr_components/certificate_manager/certificates_error_dialog.html"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CERTIFICATE_ENTRY_HTML"
+ file="cr_components/certificate_manager/certificate_entry.html"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CERTIFICATE_ENTRY_JS"
+ file="cr_components/certificate_manager/certificate_entry.js"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CERTIFICATE_LIST_HTML"
+ file="cr_components/certificate_manager/certificate_list.html"
+ preprocess="true"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CERTIFICATE_LIST_JS"
+ file="cr_components/certificate_manager/certificate_list.js"
+ preprocess="true"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CERTIFICATE_MANAGER_TYPES_HTML"
+ file="cr_components/certificate_manager/certificate_manager_types.html"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CERTIFICATE_MANAGER_TYPES_JS"
+ file="cr_components/certificate_manager/certificate_manager_types.js"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CERTIFICATE_SHARED_CSS_HTML"
+ file="cr_components/certificate_manager/certificate_shared_css.html"
+ type="chrome_html"
+ preprocess="true" />
+ <structure name="IDR_WEBUI_CERTIFICATE_SUBENTRY_HTML"
+ file="cr_components/certificate_manager/certificate_subentry.html"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CERTIFICATE_SUBENTRY_JS"
+ file="cr_components/certificate_manager/certificate_subentry.js"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CERTIFICATES_BROWSER_PROXY_HTML"
+ file="cr_components/certificate_manager/certificates_browser_proxy.html"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CERTIFICATES_BROWSER_PROXY_JS"
+ file="cr_components/certificate_manager/certificates_browser_proxy.js"
+ type="chrome_html" />
+ </if>
+ <if expr="chromeos">
+ <!-- Chrome OS Custom Elements -->
+ <structure name="IDR_WEBUI_CHROMEOS_BLUETOOTH_DIALOG_HTML"
+ file="cr_components/chromeos/bluetooth_dialog.html"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CHROMEOS_BLUETOOTH_DIALOG_JS"
+ file="cr_components/chromeos/bluetooth_dialog.js"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CHROMEOS_NETWORK_APNLIST_HTML"
+ file="cr_components/chromeos/network/network_apnlist.html"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CHROMEOS_NETWORK_APNLIST_JS"
+ file="cr_components/chromeos/network/network_apnlist.js"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CHROMEOS_NETWORK_IP_CONFIG_HTML"
+ file="cr_components/chromeos/network/network_ip_config.html"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CHROMEOS_NETWORK_IP_CONFIG_JS"
+ file="cr_components/chromeos/network/network_ip_config.js"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CHROMEOS_NETWORK_NAMESERVERS_HTML"
+ file="cr_components/chromeos/network/network_nameservers.html"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CHROMEOS_NETWORK_NAMESERVERS_JS"
+ file="cr_components/chromeos/network/network_nameservers.js"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CHROMEOS_NETWORK_PROPERTY_LIST_HTML"
+ file="cr_components/chromeos/network/network_property_list.html"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CHROMEOS_NETWORK_PROPERTY_LIST_JS"
+ file="cr_components/chromeos/network/network_property_list.js"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CHROMEOS_NETWORK_PROXY_HTML"
+ file="cr_components/chromeos/network/network_proxy.html"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CHROMEOS_NETWORK_PROXY_JS"
+ file="cr_components/chromeos/network/network_proxy.js"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CHROMEOS_NETWORK_PROXY_EXCLUSIONS_HTML"
+ file="cr_components/chromeos/network/network_proxy_exclusions.html"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CHROMEOS_NETWORK_PROXY_EXCLUSIONS_JS"
+ file="cr_components/chromeos/network/network_proxy_exclusions.js"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CHROMEOS_NETWORK_PROXY_INPUT_HTML"
+ file="cr_components/chromeos/network/network_proxy_input.html"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CHROMEOS_NETWORK_PROXY_INPUT_JS"
+ file="cr_components/chromeos/network/network_proxy_input.js"
+ type="chrome_html" />
+ <structure name="IDR_WEBUI_CHROMEOS_NETWORK_SHARED_CSS_HTML"
+ file="cr_components/chromeos/network/network_shared_css.html"
+ type="chrome_html" />
+ </if>
+</grit-part>
diff --git a/chromium/ui/webui/resources/cr_elements/OWNERS b/chromium/ui/webui/resources/cr_elements/OWNERS
index 057813143a1..5fd1662abb6 100644
--- a/chromium/ui/webui/resources/cr_elements/OWNERS
+++ b/chromium/ui/webui/resources/cr_elements/OWNERS
@@ -1,2 +1,3 @@
michaelpg@chromium.org
+scottchen@chromium.org
stevenjb@chromium.org
diff --git a/chromium/ui/webui/resources/cr_elements/READE.md b/chromium/ui/webui/resources/cr_elements/READE.md
new file mode 100644
index 00000000000..b8b47ef98b3
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_elements/READE.md
@@ -0,0 +1,16 @@
+This directory contains simple Polymer web components for Web UI. These
+components may be shared across any WebUI and should be compatible across
+all platforms (including ios).
+
+These web components may not contain any i18n dependencies and may not use
+I18nBehavior. Instead, any text (labels, tooltips, etc) should be passed as
+properties.
+
+These web components should avoid the use of chrome.send and should generally
+avoid dependencies on extension APIs as well.
+
+TODO(stevenjb/dpapad): Audit elements currently using chrome.settingsPrivate
+and chrome.networkingPrivate and decide whether to move these or update the
+guidelines.
+
+For more complex components, see cr_components.
diff --git a/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.html b/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.html
index d2e3639bbe8..abc2e47ac82 100644
--- a/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.html
+++ b/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.html
@@ -80,7 +80,7 @@
src="[[getImgSrc_(profileImageUrl_)]]"
hidden="[[!profileImageUrl_]]"
srcset="[[getImgSrc2x_(profileImageUrl_)]]"
- title="[[profileImageLoadingLabel]]">
+ title="[[profileImageLabel]]">
<!-- Shows and selects the previously selected ('old') picture. -->
<img id="oldImage" role="radio"
data-type$="[[selectionTypesEnum_.OLD]]"
diff --git a/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js b/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js
index c9cb62fb793..990aaf67fbb 100644
--- a/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js
+++ b/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js
@@ -28,7 +28,6 @@ Polymer({
chooseFileLabel: String,
oldImageLabel: String,
profileImageLabel: String,
- profileImageLoadingLabel: String,
takePhotoLabel: String,
/**
diff --git a/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.js b/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.js
index 5e3992a5705..eb4d0dff4c5 100644
--- a/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.js
+++ b/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.js
@@ -108,6 +108,10 @@ Polymer({
if (!this.isStateTextVisible_())
return '';
var state = this.networkState.ConnectionState;
+ // For Cellular, an empty ConnectionState indicates that the device is
+ // still initializing.
+ if (!state && this.networkState.Type == CrOnc.Type.CELLULAR)
+ return CrOncStrings.networkListItemInitializing;
if (state == CrOnc.ConnectionState.CONNECTED)
return CrOncStrings.networkListItemConnected;
if (state == CrOnc.ConnectionState.CONNECTING)
diff --git a/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.js b/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.js
index 0349141de90..4b98c6fff97 100644
--- a/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.js
+++ b/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.js
@@ -108,24 +108,55 @@ Polymer({
},
/**
- * Request the list of visible networks. May be called externally to force a
+ * Requests the device and network states. May be called externally to force a
* refresh and list update (e.g. when the element is shown).
*/
refreshNetworks: function() {
+ chrome.networkingPrivate.getDeviceStates(
+ this.getDeviceStatesCallback_.bind(this));
+ },
+
+ /**
+ * @param {!Array<!CrOnc.DeviceStateProperties>} deviceStates
+ * @private
+ */
+ getDeviceStatesCallback_: function(deviceStates) {
+ var uninitializedCellular = deviceStates.find(function(device) {
+ return device.Type == CrOnc.Type.CELLULAR &&
+ device.State == CrOnc.DeviceState.UNINITIALIZED;
+ });
+ this.getNetworkStates_(uninitializedCellular);
+ },
+
+ /**
+ * @param {!CrOnc.DeviceStateProperties|undefined} uninitializedCellular
+ * A cellular device state to pass to |getNetworksCallback_| or undefined.
+ */
+ getNetworkStates_: function(uninitializedCellular) {
var filter = {
networkType: chrome.networkingPrivate.NetworkType.ALL,
visible: true,
configured: false
};
- chrome.networkingPrivate.getNetworks(
- filter, this.getNetworksCallback_.bind(this));
+ chrome.networkingPrivate.getNetworks(filter, function(states) {
+ this.getNetworksCallback_(uninitializedCellular, states);
+ }.bind(this));
},
/**
+ * @param {!CrOnc.DeviceStateProperties|undefined} uninitializedCellular
+ * If defined, prepends a Cellular state with no ConnectionState to
+ * represent an uninitialized Cellular device.
* @param {!Array<!CrOnc.NetworkStateProperties>} states
* @private
*/
- getNetworksCallback_: function(states) {
+ getNetworksCallback_: function(uninitializedCellular, states) {
+ if (uninitializedCellular) {
+ states.unshift({
+ GUID: '',
+ Type: uninitializedCellular.Type,
+ });
+ }
this.networkStateList_ = states;
var defaultState = (this.networkStateList_.length > 0 &&
this.networkStateList_[0].ConnectionState ==
diff --git a/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_onc_types.js b/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_onc_types.js
index 8382119b6d4..4c51796778b 100644
--- a/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_onc_types.js
+++ b/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_onc_types.js
@@ -29,6 +29,7 @@
* networkListItemConnected: string,
* networkListItemConnecting: string,
* networkListItemConnectingTo: string,
+ * networkListItemInitializing: string,
* networkListItemNotConnected: string,
* vpnNameTemplate: string,
* }}
diff --git a/chromium/ui/webui/resources/cr_elements/compiled_resources2.gyp b/chromium/ui/webui/resources/cr_elements/compiled_resources2.gyp
index cd7896afb8d..89de6886a21 100644
--- a/chromium/ui/webui/resources/cr_elements/compiled_resources2.gyp
+++ b/chromium/ui/webui/resources/cr_elements/compiled_resources2.gyp
@@ -13,6 +13,7 @@
'cr_dialog/compiled_resources2.gyp:*',
'cr_drawer/compiled_resources2.gyp:*',
'cr_expand_button/compiled_resources2.gyp:*',
+ 'cr_link_row/compiled_resources2.gyp:*',
'cr_profile_avatar_selector/compiled_resources2.gyp:*',
'policy/compiled_resources2.gyp:*',
],
diff --git a/chromium/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html b/chromium/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html
index 3fb14c427f5..05ca3cc16ce 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html
+++ b/chromium/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html
@@ -1,7 +1,8 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
<link rel="import" href="chrome://resources/html/assert.html">
<link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
<link rel="import" href="chrome://resources/html/util.html">
-<link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
<dom-module id="cr-action-menu">
@@ -23,6 +24,7 @@
:host ::content .dropdown-item {
background: none;
border: none;
+ border-radius: 0;
box-sizing: border-box;
color: var(--paper-grey-900);
font: inherit;
@@ -55,8 +57,8 @@
outline: none;
}
</style>
- <div class="item-wrapper" tabindex="-1">
- <content select=".dropdown-item,hr"></content>
+ <div class="item-wrapper" tabindex="-1" role="menu">
+ <content select=".dropdown-item,hr" id="contentNode"></content>
</div>
</template>
<script src="cr_action_menu.js"></script>
diff --git a/chromium/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.js b/chromium/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.js
index 181936d7132..d524c5433cf 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.js
+++ b/chromium/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.js
@@ -30,6 +30,9 @@ var AnchorAlignment = {
AFTER_END: 2,
};
+/** @const {string} */
+var DROPDOWN_ITEM_CLASS = 'dropdown-item';
+
(function() {
/**
* Returns the point to start along the X or Y axis given a start and end
@@ -118,6 +121,9 @@ Polymer({
/** @private {boolean} */
hasMousemoveListener_: false,
+ /** @private {?PolymerDomApi.ObserveHandle} */
+ contentObserver_: null,
+
hostAttributes: {
tabindex: 0,
},
@@ -137,6 +143,10 @@ Polymer({
removeListeners_: function() {
window.removeEventListener('resize', this.boundClose_);
window.removeEventListener('popstate', this.boundClose_);
+ if (this.contentObserver_) {
+ Polymer.dom(this.$.contentNode).unobserveNodes(this.contentObserver_);
+ this.contentObserver_ = null;
+ }
},
/**
@@ -321,7 +331,7 @@ Polymer({
// Restore the scroll position.
doc.scrollTop = scrollTop;
doc.scrollLeft = scrollLeft;
- this.addCloseListeners_();
+ this.addListeners_();
},
/** @private */
@@ -369,13 +379,24 @@ Polymer({
/**
* @private
*/
- addCloseListeners_: function() {
+ addListeners_: function() {
this.boundClose_ = this.boundClose_ || function() {
if (this.open)
this.close();
}.bind(this);
window.addEventListener('resize', this.boundClose_);
window.addEventListener('popstate', this.boundClose_);
+
+ this.contentObserver_ =
+ Polymer.dom(this.$.contentNode).observeNodes((info) => {
+ info.addedNodes.forEach((node) => {
+ if (node.classList &&
+ node.classList.contains(DROPDOWN_ITEM_CLASS) &&
+ !node.getAttribute('role')) {
+ node.setAttribute('role', 'menuitem');
+ }
+ });
+ });
},
});
})();
diff --git a/chromium/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.html b/chromium/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.html
index bb556e458e8..2d5528b32dc 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.html
+++ b/chromium/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.html
@@ -13,12 +13,13 @@
--scroll-border: 1px solid var(--paper-grey-300);
border: 0;
border-radius: 2px;
- bottom: 0;
+ bottom: 50%;
box-shadow: 0 0 16px rgba(0, 0, 0, 0.12),
0 16px 16px rgba(0, 0, 0, 0.24);
color: inherit;
+ overflow-y: hidden;
padding: 0;
- top: 0;
+ top: 50%;
width: 512px;
}
@@ -55,6 +56,7 @@
font-size: calc(15 / 13 * 100%);
line-height: 1;
padding: 16px 16px;
+ @apply(--cr-dialog-title);
}
:host ::slotted([slot=button-container]) {
@@ -95,7 +97,7 @@
.top-container {
align-items: flex-start;
display: flex;
- min-height: 47px;
+ min-height: var(--cr-dialog-top-container-min-height, 47px);
}
.title-container {
@@ -127,6 +129,7 @@
on-keypress="onCloseKeypress_">
</button>
</div>
+ <slot name="header"></slot>
<div class="body-container">
<span id="bodyTopMarker"></span>
<slot name="body"></slot>
diff --git a/chromium/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html b/chromium/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html
index 20d15055f7b..ad9d19fdcf8 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html
+++ b/chromium/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html
@@ -64,14 +64,14 @@
outline: none;
}
- :host ::content .drawer-content {
+ :host ::slotted(.drawer-content) {
height: calc(100% - 56px);
overflow: auto;
}
</style>
<div id="container" on-tap="onContainerTap_">
<div class="drawer-header" tabindex="-1">[[heading]]</div>
- <content></content>
+ <slot></slot>
</div>
</template>
</dom-module>
diff --git a/chromium/ui/webui/resources/cr_elements/cr_icons_css.html b/chromium/ui/webui/resources/cr_elements/cr_icons_css.html
index 6428c757957..77f613fcf69 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_icons_css.html
+++ b/chromium/ui/webui/resources/cr_elements/cr_icons_css.html
@@ -5,7 +5,7 @@
<template>
<style>
:host-context([dir=rtl]) button[is='paper-icon-button-light'] {
- transform: scaleX(-1); /* Flip on the X axis (aka mirror). */
+ transform: scaleX(-1); /* Invert X: flip on the Y axis (aka mirror). */
}
button[is='paper-icon-button-light'] {
@@ -18,6 +18,15 @@
width: var(--cr-icon-ripple-size);
}
+ button[is='paper-icon-button-light'].no-overlap {
+ margin-left: 0;
+ margin-right: 0;
+ }
+
+ button[is='paper-icon-button-light'].icon-arrow-back {
+ background-image: url(../images/icon_arrow_back.svg);
+ }
+
button[is='paper-icon-button-light'].icon-cancel {
background-image: url(../images/icon_cancel.svg);
}
@@ -50,10 +59,18 @@
background-image: url(../images/open_in_new.svg);
}
+ button[is='paper-icon-button-light'].icon-menu-white {
+ background-image: url(../images/icon_menu_white.svg);
+ }
+
button[is='paper-icon-button-light'].icon-more-vert {
background-image: url(../images/icon_more_vert.svg);
}
+ button[is='paper-icon-button-light'].icon-refresh {
+ background-image: url(../images/icon_refresh.svg);
+ }
+
button[is='paper-icon-button-light'].icon-settings {
background-image: url(../images/icon_settings.svg);
}
diff --git a/chromium/ui/webui/resources/js/chromeos/compiled_resources2.gyp b/chromium/ui/webui/resources/cr_elements/cr_link_row/compiled_resources2.gyp
index a363475a954..11ed485f750 100644
--- a/chromium/ui/webui/resources/js/chromeos/compiled_resources2.gyp
+++ b/chromium/ui/webui/resources/cr_elements/cr_link_row/compiled_resources2.gyp
@@ -1,15 +1,13 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2016 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.
{
'targets': [
{
- 'target_name': 'ui_account_tweaks',
+ 'target_name': 'cr_link_row',
'dependencies': [
- '../compiled_resources2.gyp:cr',
- '../compiled_resources2.gyp:load_time_data',
],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
},
- ]
+ ],
}
diff --git a/chromium/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.html b/chromium/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.html
new file mode 100644
index 00000000000..0ea10f6da08
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.html
@@ -0,0 +1,74 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html">
+<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
+<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/default-theme.html">
+
+<dom-module id="cr-link-row">
+ <template strip-whitespace="">
+ <style include="cr-hidden-style cr-icons">
+ :host {
+ background: none;
+ border: none;
+ color: inherit;
+ cursor: pointer;
+ font-size: 100%; /* Specifically for Mac OSX, harmless elsewhere. */
+ line-height: 154%; /* 20px. */
+ margin: 0;
+ outline: none;
+ padding: 0;
+ position: relative;
+ width: 100%;
+ @apply(--cr-section);
+ }
+
+ :host(.continuation),
+ :host(.first) {
+ border-top: none;
+ }
+
+ :host([disabled]) {
+ color: var(--paper-grey-500);
+ cursor: auto;
+ pointer-events: none;
+ }
+
+ #label,
+ #subLabel {
+ display: flex;
+ }
+
+ #labelWrapper {
+ flex: 1;
+ flex-basis: 0.000000001px;
+ }
+
+ #outer {
+ align-items: center;
+ display: flex;
+ min-height: var(--cr-section-two-line-min-height);
+ width: 100%;
+ }
+
+ #outer[noSubLabel] {
+ min-height: var(--cr-section-min-height);
+ }
+
+ #subLabel {
+ /* TODO(dschuyler): replace with: @apply(--cr-secondary-text); */
+ color: var(--paper-grey-600);
+ font-weight: 400;
+ }
+ </style>
+ <div id="outer" noSubLabel$="[[!subLabel]]">
+ <div id="labelWrapper" hidden="[[!label]]">
+ <div id="label" class="label">[[label]]</div>
+ <div id="subLabel" class="secondary label">[[subLabel]]</div>
+ </div>
+ <button class$="[[iconClass]]" is="paper-icon-button-light"></button>
+ </div>
+ </template>
+ <script src="cr_link_row.js"></script>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.js b/chromium/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.js
new file mode 100644
index 00000000000..e700b6d9c2d
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.js
@@ -0,0 +1,27 @@
+// Copyright 2017 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.
+
+/**
+ * @fileoverview
+ * A link row is a UI element similar to a button, though usually wider than a
+ * button (taking up the whole 'row'). The name link comes from the intended use
+ * of this element to take the user to another page in the app or to an external
+ * page (somewhat like an HTML link).
+ */
+Polymer({
+ is: 'cr-link-row',
+ extends: 'button',
+
+ properties: {
+ iconClass: String,
+
+ label: String,
+
+ subLabel: {
+ type: String,
+ /* Value used for noSubLabel attribute. */
+ value: '',
+ },
+ },
+});
diff --git a/chromium/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.html b/chromium/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.html
index 9c37afe5501..29b9923b515 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.html
+++ b/chromium/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.html
@@ -44,7 +44,7 @@
ignore-modified-key-events="[[ignoreModifiedKeyEvents]]">
<template is="dom-repeat" items="[[avatars]]">
<paper-button class="avatar" title="[[item.label]]"
- style$="background-image: [[getIconImageset_(item.url)]]"
+ style$="background-image: [[getIconImageSet_(item.url)]]"
on-tap="onAvatarTap_">
</paper-button>
</template>
diff --git a/chromium/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.js b/chromium/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.js
index c09ce793656..28c58de1449 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.js
+++ b/chromium/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.js
@@ -49,10 +49,10 @@ Polymer({
/**
* @param {string} iconUrl
- * @return {string} A CSS imageset for multiple scale factors.
+ * @return {string} A CSS image-set for multiple scale factors.
* @private
*/
- getIconImageset_: function(iconUrl) {
+ getIconImageSet_: function(iconUrl) {
return cr.icon.getImage(iconUrl);
},
diff --git a/chromium/ui/webui/resources/cr_elements/cr_scrollable_behavior.js b/chromium/ui/webui/resources/cr_elements/cr_scrollable_behavior.js
index 2a497807d95..9535c1a9b40 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_scrollable_behavior.js
+++ b/chromium/ui/webui/resources/cr_elements/cr_scrollable_behavior.js
@@ -63,7 +63,7 @@ var CrScrollableBehavior = {
*/
updateScrollableContents: function() {
if (this.intervalId_ !== null)
- return; // notifyResize is arelady in progress.
+ return; // notifyResize is already in progress.
this.requestUpdateScroll();
@@ -93,7 +93,7 @@ var CrScrollableBehavior = {
},
/**
- * Setup the intial scrolling related classes for each scrollable container.
+ * Setup the initial scrolling related classes for each scrollable container.
* Called from ready() and updateScrollableContents(). May also be called
* directly when the contents change (e.g. when not using iron-list).
*/
@@ -119,7 +119,7 @@ var CrScrollableBehavior = {
this.async(function() {
var scrollTop = list.savedScrollTops.shift();
// Ignore scrollTop of 0 in case it was intermittent (we do not need to
- // explicity scroll to 0).
+ // explicitly scroll to 0).
if (scrollTop != 0)
list.scroll(0, scrollTop);
});
@@ -136,7 +136,8 @@ var CrScrollableBehavior = {
},
/**
- * This gets called once intially and any time a scrollable container scrolls.
+ * This gets called once initially and any time a scrollable container
+ * scrolls.
* @param {!HTMLElement} scrollable
* @private
*/
diff --git a/chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_behavior.js b/chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_behavior.js
index 491bbb53d60..9646f99e0be 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_behavior.js
+++ b/chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_behavior.js
@@ -33,9 +33,8 @@ var CrSearchFieldBehavior = {
},
/**
- * @abstract
* @return {!HTMLInputElement} The input field element the behavior should
- * use.
+ * use.
*/
getSearchInput: function() {},
diff --git a/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html b/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
index bfbe0c97fc8..b53e5f65276 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
+++ b/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
@@ -1,11 +1,13 @@
<link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-media-query/iron-media-query.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html">
<link rel="import" href="chrome://resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-media-query/iron-media-query.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
<dom-module id="cr-toolbar">
<template>
- <style>
+ <style include="cr-icons">
:host {
--cr-toolbar-field-width: 580px;
--cr-toolbar-height: 56px;
@@ -22,9 +24,7 @@
flex: 1;
font-size: 123%;
font-weight: 400;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
+ line-height: normal;
}
#leftContent {
@@ -130,11 +130,12 @@
<div id="leftSpacer">
<!-- Note: showing #menuPromo relies on this dom-if being [restamp]. -->
<template is="dom-if" if="[[showMenu]]" restamp>
- <paper-icon-button id="menuButton" icon="cr20:menu"
+ <button id="menuButton" is="paper-icon-button-light"
+ class="icon-menu-white no-overlap"
on-tap="onMenuTap_"
title="[[titleIfNotShowMenuPromo_(menuLabel, showMenuPromo)]]"
aria-label$="[[menuLabel]]">
- </paper-icon-button>
+ </button>
<template is="dom-if" if="[[showMenuPromo]]">
<div id="menuPromo" role="tooltip">
[[menuPromo]]
diff --git a/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html b/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html
index 5b389334c39..dd002fb9c28 100644
--- a/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html
+++ b/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html
@@ -151,7 +151,6 @@
on-blur="onInputBlur_"
incremental
autofocus>
- </input>
</div>
<template is="dom-if" if="[[hasSearchText]]">
<button is="paper-icon-button-light" class="icon-cancel-toolbar"
diff --git a/chromium/ui/webui/resources/cr_elements/icons.html b/chromium/ui/webui/resources/cr_elements/icons.html
index 40e08ef742e..bc0eae71ea2 100644
--- a/chromium/ui/webui/resources/cr_elements/icons.html
+++ b/chromium/ui/webui/resources/cr_elements/icons.html
@@ -17,7 +17,6 @@ blurry at 20 px). Please use 20 px icons when available.
Keep these in sorted order by id="". See also http://goo.gl/Y1OdAq
-->
<g id="domain"><path d="M2,3 L2,17 L11.8267655,17 L13.7904799,17 L18,17 L18,7 L12,7 L12,3 L2,3 Z M8,13 L10,13 L10,15 L8,15 L8,13 Z M4,13 L6,13 L6,15 L4,15 L4,13 Z M8,9 L10,9 L10,11 L8,11 L8,9 Z M4,9 L6,9 L6,11 L4,11 L4,9 Z M12,9 L16,9 L16,15 L12,15 L12,9 Z M12,11 L14,11 L14,13 L12,13 L12,11 Z M8,5 L10,5 L10,7 L8,7 L8,5 Z M4,5 L6,5 L6,7 L4,7 L4,5 Z"></path></g>
- <g id="menu"><rect x="2" y="4" width="16" height="2"></rect><rect x="2" y="9" width="16" height="2"></rect><rect x="2" y="14" width="16" height="2"></rect></g>
</svg>
</iron-iconset-svg>
<iron-iconset-svg name="cr" size="24">
@@ -34,6 +33,7 @@ blurry at 20 px). Please use 20 px icons when available.
<g id="camera-alt"><circle cx="12" cy="12" r="3.2"></circle><path d="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"></path></g>
<g id="check"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"></path></g>
</if>
+ <g id="account-child-invert" viewBox="0 0 48 48"><path d="M24 4c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6 2.69-6 6-6z"></path><path fill="none" d="M0 0h48v48H0V0z"></path><circle fill="none" cx="24" cy="26" r="4"></circle><path d="M24 18c-6.16 0-13 3.12-13 7.23v11.54c0 2.32 2.19 4.33 5.2 5.63 2.32 1 5.12 1.59 7.8 1.59.66 0 1.33-.06 2-.14v-5.2c-.67.08-1.34.14-2 .14-2.63 0-5.39-.57-7.68-1.55.67-2.12 4.34-3.65 7.68-3.65.86 0 1.75.11 2.6.29 2.79.62 5.2 2.15 5.2 4.04v4.47c3.01-1.31 5.2-3.31 5.2-5.63V25.23C37 21.12 30.16 18 24 18zm0 12c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4z"></path></g>
<g id="chevron-right"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"></path></g>
<g id="clear"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path></g>
<g id="close"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path></g>
@@ -55,6 +55,7 @@ blurry at 20 px). Please use 20 px icons when available.
suffix prevents the naming conflict. -->
<g id="settings_icon"><path d="M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"></path></g>
<g id="star"><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"></path></g>
+ <g id="supervisor-account" viewBox="0 0 48 48"><path d="M0 0h48v48H0z" fill="none"></path><path d="M33 24c2.76 0 4.98-2.24 4.98-5s-2.22-5-4.98-5c-2.76 0-5 2.24-5 5s2.24 5 5 5zm-15-2c3.31 0 5.98-2.69 5.98-6s-2.67-6-5.98-6c-3.31 0-6 2.69-6 6s2.69 6 6 6zm15 6c-3.67 0-11 1.84-11 5.5V38h22v-4.5c0-3.66-7.33-5.5-11-5.5zm-15-2c-4.67 0-14 2.34-14 7v5h14v-4.5c0-1.7.67-4.67 4.74-6.94C21 26.19 19.31 26 18 26z"></path></g>
<g id="warning"><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"></path></g>
</defs>
</svg>
diff --git a/chromium/ui/webui/resources/cr_elements/paper_checkbox_style_css.html b/chromium/ui/webui/resources/cr_elements/paper_checkbox_style_css.html
new file mode 100644
index 00000000000..6aab514b762
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_elements/paper_checkbox_style_css.html
@@ -0,0 +1,19 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<!-- Common paper-checkbox styling for Material Design WebUI. -->
+<dom-module id="paper-checkbox-style">
+ <template>
+ <style>
+ paper-checkbox {
+ --paper-checkbox-checked-color: var(--google-blue-500);
+ --paper-checkbox-ink-size: 40px;
+ --paper-checkbox-label-color: inherit;
+ --paper-checkbox-label-spacing: 20px;
+ --paper-checkbox-size: 16px;
+ --paper-checkbox-unchecked-color: var(--paper-grey-600);
+ -webkit-margin-start: 2px;
+ }
+ </style>
+ </template>
+</dom-module>
+
diff --git a/chromium/ui/webui/resources/cr_elements/paper_toggle_style_css.html b/chromium/ui/webui/resources/cr_elements/paper_toggle_style_css.html
new file mode 100644
index 00000000000..95581fd1707
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_elements/paper_toggle_style_css.html
@@ -0,0 +1,42 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+
+<!-- Common paper-button styling for Material Design WebUI. -->
+<dom-module id="paper-toggle-style">
+ <template>
+ <style>
+ :root {
+ --cr-toggle-bar-size: {
+ height: 12px;
+ left: 4px;
+ width: 28px;
+ };
+ --cr-toggle-button-size: {
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.4);
+ height: 16px;
+ top: -2px;
+ width: 16px;
+ };
+ --cr-toggle-ink-size: {
+ height: 40px;
+ top: -12px;
+ left: -12px;
+ width: 40px;
+ };
+
+ --paper-toggle-button-checked-bar: var(--cr-toggle-bar-size);
+ --paper-toggle-button-checked-bar-color: var(--cr-toggle-color);
+ --paper-toggle-button-checked-button: {
+ @apply(--cr-toggle-button-size);
+ transform: translate(18px, 0);
+ };
+ --paper-toggle-button-checked-button-color: var(--cr-toggle-color);
+ --paper-toggle-button-label-spacing: 0;
+ --paper-toggle-button-unchecked-bar: var(--cr-toggle-bar-size);
+ --paper-toggle-button-unchecked-button: var(--cr-toggle-button-size);
+ --paper-toggle-button-unchecked-ink: var(--cr-toggle-ink-size);
+ };
+ </style>
+ </template>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_elements/policy/compiled_resources2.gyp b/chromium/ui/webui/resources/cr_elements/policy/compiled_resources2.gyp
index 357d75a6a0d..8b6dede4445 100644
--- a/chromium/ui/webui/resources/cr_elements/policy/compiled_resources2.gyp
+++ b/chromium/ui/webui/resources/cr_elements/policy/compiled_resources2.gyp
@@ -51,5 +51,10 @@
],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
},
+ {
+ 'target_name': 'cr_tooltip_icon',
+ 'dependencies': [],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
],
}
diff --git a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_indicator.html b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_indicator.html
index 0bb096776f1..41b5170eff8 100644
--- a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_indicator.html
+++ b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_indicator.html
@@ -1,29 +1,16 @@
-<link rel="import" href="chrome://resources/cr_elements/icons.html">
-<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
<link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-tooltip/paper-tooltip.html">
+
+<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
<link rel="import" href="cr_policy_indicator_behavior.html">
-<link rel="import" href="cr_policy_vars_css.html">
+<link rel="import" href="cr_tooltip_icon.html">
<dom-module id="cr-policy-indicator">
<template>
- <style include="cr-hidden-style">
- :host {
- @apply(--cr-policy-indicator);
- }
-
- paper-tooltip {
- --paper-tooltip: var(--cr-policy-tooltip);
- }
- </style>
- <iron-icon id="indicator" tabindex="0" aria-label$="[[iconAriaLabel]]"
- aria-describedby="tooltip" hidden$="[[!indicatorVisible]]"
- icon="[[indicatorIcon]]"></iron-icon>
- <paper-tooltip id="tooltip" for="indicator" position="top"
- fit-to-visible-bounds>
- [[indicatorTooltip]]
- </paper-tooltip>
+ <style include="cr-hidden-style"></style>
+ <cr-tooltip-icon hidden$="[[!indicatorVisible]]"
+ tooltip-text="[[indicatorTooltip]]" icon-class="[[indicatorIcon]]"
+ icon-aria-label="[[iconAriaLabel]]">
+ </cr-tooltip-icon>
</template>
<script src="cr_policy_indicator.js"></script>
</dom-module>
diff --git a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_indicator.html b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_indicator.html
index 70082b7a034..0994787a73a 100644
--- a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_indicator.html
+++ b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_indicator.html
@@ -1,30 +1,16 @@
-<link rel="import" href="chrome://resources/cr_elements/icons.html">
-<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
<link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-tooltip/paper-tooltip.html">
+
+<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
<link rel="import" href="cr_policy_indicator_behavior.html">
<link rel="import" href="cr_policy_network_behavior.html">
-<link rel="import" href="cr_policy_vars_css.html">
+<link rel="import" href="cr_tooltip_icon.html">
<dom-module id="cr-policy-network-indicator">
<template>
- <style include="cr-hidden-style">
- :host {
- @apply(--cr-policy-indicator);
- }
-
- paper-tooltip {
- --paper-tooltip: var(--cr-policy-tooltip);
- }
- </style>
- <iron-icon id="indicator" tabindex="0" aria-describedby="tooltip"
- hidden$="[[!indicatorVisible]]" icon="[[indicatorIcon]]">
- </iron-icon>
- <paper-tooltip id="tooltip" for="indicator" position="top"
- fit-to-visible-bounds>
- [[indicatorTooltip]]
- </paper-tooltip>
+ <style include="cr-hidden-style"></style>
+ <cr-tooltip-icon hidden$="[[!indicatorVisible]]"
+ tooltip-text="[[indicatorTooltip]]" icon-class="[[indicatorIcon]]">
+ </cr-tooltip-icon>
</template>
<script src="cr_policy_network_indicator.js"></script>
</dom-module>
diff --git a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.html b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.html
index dd33e679cfb..8758980f279 100644
--- a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.html
+++ b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.html
@@ -1,29 +1,16 @@
-<link rel="import" href="chrome://resources/cr_elements/icons.html">
-<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
<link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-tooltip/paper-tooltip.html">
+
+<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
<link rel="import" href="cr_policy_indicator_behavior.html">
-<link rel="import" href="cr_policy_vars_css.html">
+<link rel="import" href="cr_tooltip_icon.html">
<dom-module id="cr-policy-pref-indicator">
<template>
- <style include="cr-hidden-style">
- :host {
- @apply(--cr-policy-indicator);
- }
-
- paper-tooltip {
- --paper-tooltip: var(--cr-policy-tooltip);
- }
- </style>
- <iron-icon id="indicator" tabindex="0" aria-label$="[[iconAriaLabel]]"
- aria-describedby="tooltip" hidden$="[[!indicatorVisible]]"
- icon="[[indicatorIcon]]"></iron-icon>
- <paper-tooltip id="tooltip" for="indicator" position="top"
- fit-to-visible-bounds>
- [[indicatorTooltip]]
- </paper-tooltip>
+ <style include="cr-hidden-style"></style>
+ <cr-tooltip-icon hidden$="[[!indicatorVisible]]"
+ tooltip-text="[[indicatorTooltip]]" icon-class="[[indicatorIcon]]"
+ icon-aria-label="[[iconAriaLabel]]">
+ </cr-tooltip-icon>
</template>
<script src="cr_policy_pref_indicator.js"></script>
</dom-module>
diff --git a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_vars_css.html b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_vars_css.html
deleted file mode 100644
index c16dd6503b8..00000000000
--- a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_vars_css.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<style is="custom-style">
- :root {
- --cr-policy-indicator: {
- @apply(--cr-icon-height-width);
- display: flex; /* Position independently from the line-height. */
- };
-
- --cr-policy-tooltip: {
- font-size: 92.31%; /* Effectively 12px if the host default is 13px. */
- font-weight: 500;
- max-width: 330px;
- min-width: 200px;
- padding: 10px 8px;
- };
- }
-</style>
diff --git a/chromium/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.html b/chromium/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.html
new file mode 100644
index 00000000000..c9a1587bf87
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.html
@@ -0,0 +1,34 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/icons.html">
+<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-tooltip/paper-tooltip.html">
+
+<dom-module id="cr-tooltip-icon">
+ <template>
+ <style>
+ :host {
+ @apply(--cr-icon-height-width);
+ display: flex; /* Position independently from the line-height. */
+ }
+
+ paper-tooltip {
+ --paper-tooltip: {
+ font-size: 92.31%; /* Effectively 12px if the host default is 13px. */
+ font-weight: 500;
+ max-width: 330px;
+ min-width: 200px;
+ padding: 10px 8px;
+ };
+ }
+ </style>
+ <iron-icon id="indicator" tabindex="0" aria-label$="[[iconAriaLabel]]"
+ aria-describedby="tooltip" icon="[[iconClass]]"></iron-icon>
+ <paper-tooltip id="tooltip" for="indicator" position="top"
+ fit-to-visible-bounds>
+ [[tooltipText]]
+ </paper-tooltip>
+ </template>
+ <script src="cr_tooltip_icon.js"></script>
+</dom-module>
diff --git a/chromium/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.js b/chromium/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.js
new file mode 100644
index 00000000000..ba918fb0ca7
--- /dev/null
+++ b/chromium/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.js
@@ -0,0 +1,12 @@
+// Copyright 2017 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.
+
+Polymer({
+ is: 'cr-tooltip-icon',
+ properties: {
+ iconAriaLabel: String,
+ iconClass: String,
+ tooltipText: String,
+ },
+}); \ No newline at end of file
diff --git a/chromium/ui/webui/resources/cr_elements/shared_vars_css.html b/chromium/ui/webui/resources/cr_elements/shared_vars_css.html
index 224a94768a6..2352c441aed 100644
--- a/chromium/ui/webui/resources/cr_elements/shared_vars_css.html
+++ b/chromium/ui/webui/resources/cr_elements/shared_vars_css.html
@@ -37,12 +37,43 @@
-webkit-margin-start: var(--cr-icon-button-margin-start);
}
+ --cr-primary-text: {
+ color: var(--paper-grey-900);
+ line-height: 154%; /* 20px. */
+ }
+
+ --cr-secondary-text: {
+ color: var(--paper-grey-600);
+ font-weight: 400;
+ }
+
+ /* TODO (scottchen): re-implement with paddings instead; */
+ /* These are used for row items such as radio buttons, check boxes, list
+ * items etc. */
+ --cr-section-min-height: 48px;
+ --cr-section-two-line-min-height: 64px;
+ --cr-section-three-line-min-height: 84px;
+
+ --cr-section-padding: 20px;
+ --cr-section-indent-width: 40px;
+ --cr-section-indent-padding: calc(
+ var(--cr-section-padding) + var(--cr-section-indent-width));
+
+ --cr-section: {
+ align-items: center;
+ border-top: var(--cr-separator-line);
+ display: flex;
+ min-height: var(--cr-section-min-height);
+ padding: 0 var(--cr-section-padding);
+ };
+
+ --cr-toggle-color: var(--google-blue-500);
+
--cr-selectable-focus: {
background-color: var(--cr-focused-item-color);
outline: none;
}
--cr-separator-height: 1px;
--cr-separator-line: var(--cr-separator-height) solid rgba(0, 0, 0, 0.06);
- --paper-checkbox-ink-size: 40px;
}
</style>
diff --git a/chromium/ui/webui/resources/cr_elements_images.grdp b/chromium/ui/webui/resources/cr_elements_images.grdp
index a5ec224b864..23265241ab1 100644
--- a/chromium/ui/webui/resources/cr_elements_images.grdp
+++ b/chromium/ui/webui/resources/cr_elements_images.grdp
@@ -9,6 +9,8 @@
file="images/arrow_right.svg" type="BINDATA" />
<include name="IDR_WEBUI_IMAGES_OPEN_IN_NEW"
file="images/open_in_new.svg" type="BINDATA" />
+ <include name="IDR_WEBUI_IMAGES_ICON_ARROW_BACK"
+ file="images/icon_arrow_back.svg" type="BINDATA" />
<include name="IDR_WEBUI_IMAGES_ICON_CANCEL"
file="images/icon_cancel.svg" type="BINDATA" />
<include name="IDR_WEBUI_IMAGES_ICON_CANCEL_TOOLBAR"
@@ -25,8 +27,12 @@
file="images/icon_expand_more.svg" type="BINDATA" />
<include name="IDR_WEBUI_IMAGES_ICON_EXTERNAL"
file="images/open_in_new.svg" type="BINDATA" />
+ <include name="IDR_WEBUI_IMAGES_ICON_MENU_WHITE"
+ file="images/icon_menu_white.svg" type="BINDATA" />
<include name="IDR_WEBUI_IMAGES_ICON_MORE_VERT"
file="images/icon_more_vert.svg" type="BINDATA" />
+ <include name="IDR_WEBUI_IMAGES_ICON_REFRESH"
+ file="images/icon_refresh.svg" type="BINDATA" />
<include name="IDR_WEBUI_IMAGES_ICON_SETTINGS"
file="images/icon_settings.svg" type="BINDATA" />
<include name="IDR_WEBUI_IMAGES_ICON_SEARCH"
diff --git a/chromium/ui/webui/resources/cr_elements_resources.grdp b/chromium/ui/webui/resources/cr_elements_resources.grdp
index d9db27b42bd..d7f4a170287 100644
--- a/chromium/ui/webui/resources/cr_elements_resources.grdp
+++ b/chromium/ui/webui/resources/cr_elements_resources.grdp
@@ -41,6 +41,12 @@
<structure name="IDR_CR_ELEMENTS_CR_LAZY_RENDER_JS"
file="../../webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.js"
type="chrome_html" />
+ <structure name="IDR_CR_ELEMENTS_CR_LINK_ROW_HTML"
+ file="../../webui/resources/cr_elements/cr_link_row/cr_link_row.html"
+ type="chrome_html" />
+ <structure name="IDR_CR_ELEMENTS_CR_LINK_ROW_JS"
+ file="../../webui/resources/cr_elements/cr_link_row/cr_link_row.js"
+ type="chrome_html" />
<if expr="chromeos">
<structure name="IDR_CR_ELEMENTS_CHROMEOS_CR_PICTURE_CR_CAMERA_HTML"
file="../../webui/resources/cr_elements/chromeos/cr_picture/cr_camera.html"
@@ -145,8 +151,11 @@
<structure name="IDR_CR_ELEMENTS_CR_POLICY_PREF_INDICATOR_HTML"
file="../../webui/resources/cr_elements/policy/cr_policy_pref_indicator.html"
type="chrome_html" />
- <structure name="IDR_CR_ELEMENTS_CR_POLICY_VARS_CSS_HTML"
- file="../../webui/resources/cr_elements/policy/cr_policy_vars_css.html"
+ <structure name="IDR_CR_ELEMENTS_CR_TOOLTIP_ICON_HTML"
+ file="../../webui/resources/cr_elements/policy/cr_tooltip_icon.html"
+ type="chrome_html" />
+ <structure name="IDR_CR_ELEMENTS_CR_TOOLTIP_ICON_JS"
+ file="../../webui/resources/cr_elements/policy/cr_tooltip_icon.js"
type="chrome_html" />
<structure name="IDR_CR_ELEMENTS_CR_PROFILE_AVATAR_SELECTOR_HTML"
file="../../webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.html"
@@ -179,6 +188,12 @@
<structure name="IDR_CR_ELEMENTS_PAPER_BUTTON_STYLE_CSS_HTML"
file="../../webui/resources/cr_elements/paper_button_style_css.html"
type="chrome_html" />
+ <structure name="IDR_CR_ELEMENTS_PAPER_CHECKBOX_STYLE_CSS_HTML"
+ file="../../webui/resources/cr_elements/paper_checkbox_style_css.html"
+ type="chrome_html" />
+ <structure name="IDR_CR_ELEMENTS_PAPER_TOGGLE_STYLE_CSS_HTML"
+ file="../../webui/resources/cr_elements/paper_toggle_style_css.html"
+ type="chrome_html" />
<structure name="IDR_CR_ELEMENTS_CR_SHARED_VARS_CSS_HTML"
file="../../webui/resources/cr_elements/shared_vars_css.html"
type="chrome_html" />
diff --git a/chromium/ui/webui/resources/css/chrome_shared.css b/chromium/ui/webui/resources/css/chrome_shared.css
index acca2c1cd22..95ef34d5564 100644
--- a/chromium/ui/webui/resources/css/chrome_shared.css
+++ b/chromium/ui/webui/resources/css/chrome_shared.css
@@ -8,9 +8,6 @@
@import url(chrome://resources/css/text_defaults.css);
@import url(i18n_process.css);
@import url(widgets.css);
-<if expr="chromeos">
-@import url(chromeos/ui_account_tweaks.css);
-</if>
/* Prevent CSS from overriding the hidden property. */
[hidden] {
diff --git a/chromium/ui/webui/resources/css/chromeos/ui_account_tweaks.css b/chromium/ui/webui/resources/css/chromeos/ui_account_tweaks.css
deleted file mode 100644
index 91ae0699729..00000000000
--- a/chromium/ui/webui/resources/css/chromeos/ui_account_tweaks.css
+++ /dev/null
@@ -1,14 +0,0 @@
-/* Copyright (c) 2012 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. */
-
-/* This file defines styles for internal page elements that have special
- * look and feel based on account status (owner/non-owner/guest). */
-
-.guest-disabled {
- color: #999;
-}
-
-a.guest-disabled {
- opacity: 0.75;
-}
diff --git a/chromium/ui/webui/resources/css/widgets.css b/chromium/ui/webui/resources/css/widgets.css
index 997bfc04794..419304710cf 100644
--- a/chromium/ui/webui/resources/css/widgets.css
+++ b/chromium/ui/webui/resources/css/widgets.css
@@ -249,7 +249,7 @@ input:disabled:-webkit-any([type='password'],
*
* <div class="checkbox">
* <label>
- * <input type="checkbox"></input>
+ * <input type="checkbox">
* <span>
* </label>
* </div>
diff --git a/chromium/ui/webui/resources/html/chromeos/ui_account_tweaks.html b/chromium/ui/webui/resources/html/chromeos/ui_account_tweaks.html
deleted file mode 100644
index 10b6db6d7e4..00000000000
--- a/chromium/ui/webui/resources/html/chromeos/ui_account_tweaks.html
+++ /dev/null
@@ -1 +0,0 @@
-<script src="chrome://resources/js/chromeos/ui_account_tweaks.js"></script>
diff --git a/chromium/ui/webui/resources/images/icon_arrow_back.svg b/chromium/ui/webui/resources/images/icon_arrow_back.svg
new file mode 100644
index 00000000000..838eb43618d
--- /dev/null
+++ b/chromium/ui/webui/resources/images/icon_arrow_back.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#757575" preserveAspectRatio="xMidYMid meet"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"></path></svg> \ No newline at end of file
diff --git a/chromium/ui/webui/resources/images/icon_menu_white.svg b/chromium/ui/webui/resources/images/icon_menu_white.svg
new file mode 100644
index 00000000000..aab80273728
--- /dev/null
+++ b/chromium/ui/webui/resources/images/icon_menu_white.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="#fff" preserveAspectRatio="xMidYMid meet"><rect x="2" y="4" width="16" height="2"></rect><rect x="2" y="9" width="16" height="2"></rect><rect x="2" y="14" width="16" height="2"></rect></svg> \ No newline at end of file
diff --git a/chromium/ui/webui/resources/images/icon_refresh.svg b/chromium/ui/webui/resources/images/icon_refresh.svg
new file mode 100644
index 00000000000..d7f66ccf991
--- /dev/null
+++ b/chromium/ui/webui/resources/images/icon_refresh.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#757575" preserveAspectRatio="xMidYMid meet"><path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"></path></svg> \ No newline at end of file
diff --git a/chromium/ui/webui/resources/images/info.svg b/chromium/ui/webui/resources/images/info.svg
new file mode 100644
index 00000000000..e76e2476640
--- /dev/null
+++ b/chromium/ui/webui/resources/images/info.svg
@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="18px" height="18px" viewBox="0 0 48 48" fill="#757575">
+ <path d="M0 0h48v48H0z" fill="none"/>
+ <path d="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm2 30h-4V22h4v12zm0-16h-4v-4h4v4z"/>
+</svg>
diff --git a/chromium/ui/webui/resources/js/chromeos/ui_account_tweaks.js b/chromium/ui/webui/resources/js/chromeos/ui_account_tweaks.js
deleted file mode 100644
index 1aa912312cb..00000000000
--- a/chromium/ui/webui/resources/js/chromeos/ui_account_tweaks.js
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright (c) 2012 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.
-
-/**
- * @fileoverview This file contains methods that allow to tweak
- * internal page UI based on the status of current user (owner/user/guest).
- * It is assumed that required data is passed via i18n strings
- * (using loadTimeData dictionary) that are filled with call to
- * AddAccountUITweaksLocalizedValues in ui_account_tweaks.cc.
- * It is also assumed that tweaked page has chrome://resources/css/widgets.css
- * included.
- */
-
-cr.define('uiAccountTweaks', function() {
-
- /////////////////////////////////////////////////////////////////////////////
- // UIAccountTweaks class:
-
- // String specificators for different types of sessions.
- /** @const */ var SESSION_TYPE_GUEST = 'guest';
- /** @const */ var SESSION_TYPE_PUBLIC = 'public-account';
-
- /**
- * Encapsulated handling of ChromeOS accounts options page.
- * @constructor
- */
- function UIAccountTweaks() {}
-
- /**
- * @return {boolean} Whether the current user is owner or not.
- */
- UIAccountTweaks.currentUserIsOwner = function() {
- return loadTimeData.getBoolean('currentUserIsOwner');
- };
-
- /**
- * @return {boolean} Whether we're currently in guest session.
- */
- UIAccountTweaks.loggedInAsGuest = function() {
- return loadTimeData.getBoolean('loggedInAsGuest');
- };
-
- /**
- * @return {boolean} Whether we're currently in public session.
- */
- UIAccountTweaks.loggedInAsPublicAccount = function() {
- return loadTimeData.getBoolean('loggedInAsPublicAccount');
- };
-
- /**
- * @return {boolean} Whether we're currently in supervised user mode.
- */
- UIAccountTweaks.loggedInAsSupervisedUser = function() {
- return loadTimeData.getBoolean('loggedInAsSupervisedUser');
- };
-
- /**
- * Enables an element unless it should be disabled for the session type.
- *
- * @param {!Element} element Element that should be enabled.
- */
- UIAccountTweaks.enableElementIfPossible = function(element) {
- var sessionType;
- if (UIAccountTweaks.loggedInAsGuest())
- sessionType = SESSION_TYPE_GUEST;
- else if (UIAccountTweaks.loggedInAsPublicAccount())
- sessionType = SESSION_TYPE_PUBLIC;
-
- if (sessionType &&
- element.getAttribute(sessionType + '-visibility') == 'disabled') {
- return;
- }
-
- element.disabled = false;
- };
-
- /**
- * Disables or hides some elements in specified type of session in ChromeOS.
- * All elements within given document with *sessionType*-visibility
- * attribute are either hidden (for *sessionType*-visibility="hidden")
- * or disabled (for *sessionType*-visibility="disabled").
- *
- * @param {Document} document Document that should processed.
- * @param {string} sessionType name of the session type processed.
- * @private
- */
- UIAccountTweaks.applySessionTypeVisibility_ = function(
- document, sessionType) {
- var elements =
- document.querySelectorAll('[' + sessionType + '-visibility]');
- for (var i = 0; i < elements.length; i++) {
- var element = elements[i];
- var visibility = element.getAttribute(sessionType + '-visibility');
- if (visibility == 'hidden')
- element.hidden = true;
- else if (visibility == 'disabled')
- UIAccountTweaks.disableElementsForSessionType(element, sessionType);
- }
- };
-
- /**
- * Updates specific visibility of elements for Guest session in ChromeOS.
- * Calls applySessionTypeVisibility_ method.
- *
- * @param {Document} document Document that should processed.
- */
- UIAccountTweaks.applyGuestSessionVisibility = function(document) {
- if (!UIAccountTweaks.loggedInAsGuest())
- return;
- UIAccountTweaks.applySessionTypeVisibility_(document, SESSION_TYPE_GUEST);
- };
-
- /**
- * Updates specific visibility of elements for Public account session in
- * ChromeOS. Calls applySessionTypeVisibility_ method.
- *
- * @param {Document} document Document that should processed.
- */
- UIAccountTweaks.applyPublicSessionVisibility = function(document) {
- if (!UIAccountTweaks.loggedInAsPublicAccount())
- return;
- UIAccountTweaks.applySessionTypeVisibility_(document, SESSION_TYPE_PUBLIC);
- };
-
- /**
- * Disables and marks page elements for specified session type.
- * Adds #-disabled css class to all elements within given subtree,
- * disables interactive elements (input/select/button), and removes href
- * attribute from <a> elements.
- *
- * @param {!Element} element Root element of DOM subtree that should be
- * disabled.
- * @param {string} sessionType session type specificator.
- */
- UIAccountTweaks.disableElementsForSessionType = function(
- element, sessionType) {
- UIAccountTweaks.disableElementForSessionType_(element, sessionType);
-
- // Walk the tree, searching each ELEMENT node.
- var walker = document.createTreeWalker(
- element, NodeFilter.SHOW_ELEMENT, null, false);
-
- var node = walker.nextNode();
- while (node) {
- UIAccountTweaks.disableElementForSessionType_(
- /** @type {!Element} */ (node), sessionType);
- node = walker.nextNode();
- }
- };
-
- /**
- * Disables single element for given session type.
- * Adds *sessionType*-disabled css class, adds disabled attribute for
- * appropriate elements (input/select/button), and removes href attribute from
- * <a> element.
- *
- * @private
- * @param {!Element} element Element that should be disabled.
- * @param {string} sessionType account session Type specificator.
- */
- UIAccountTweaks.disableElementForSessionType_ = function(
- element, sessionType) {
- element.classList.add(sessionType + '-disabled');
- if (element.nodeName == 'INPUT' || element.nodeName == 'SELECT' ||
- element.nodeName == 'BUTTON') {
- element.disabled = true;
- } else if (element.nodeName == 'A') {
- element.onclick = function() {
- return false;
- };
- }
- };
-
- // Export
- return {UIAccountTweaks: UIAccountTweaks};
-
-});
diff --git a/chromium/ui/webui/resources/js/cr/ui/menu.js b/chromium/ui/webui/resources/js/cr/ui/menu.js
index 487ff8553c9..ed00ed84f6a 100644
--- a/chromium/ui/webui/resources/js/cr/ui/menu.js
+++ b/chromium/ui/webui/resources/js/cr/ui/menu.js
@@ -78,6 +78,7 @@ cr.define('cr.ui', function() {
* Clears menu.
*/
clear: function() {
+ this.selectedItem = null;
this.textContent = '';
},
diff --git a/chromium/ui/webui/resources/webui_resources.grd b/chromium/ui/webui/resources/webui_resources.grd
index 24f7ea9c074..e8eb478d2a4 100644
--- a/chromium/ui/webui/resources/webui_resources.grd
+++ b/chromium/ui/webui/resources/webui_resources.grd
@@ -496,18 +496,8 @@ without changes to the corresponding grd file. -->
file="js/web_ui_listener_behavior.js" type="chrome_html" />
<structure name="IDR_WEBUI_JS_WEBUI_RESOURCE_TEST"
file="js/webui_resource_test.js" type="chrome_html" />
- <if expr="chromeos">
- <structure name="IDR_WEBUI_CSS_UI_ACCOUNT_TWEAKS"
- file="css/chromeos/ui_account_tweaks.css"
- type="chrome_html" />
- <structure name="IDR_WEBUI_HTML_UI_ACCOUNT_TWEAKS"
- file="html/chromeos/ui_account_tweaks.html"
- type="chrome_html" />
- <structure name="IDR_WEBUI_JS_UI_ACCOUNT_TWEAKS"
- file="js/chromeos/ui_account_tweaks.js"
- type="chrome_html" />
- </if>
<if expr="not is_android">
+ <part file="cr_components_resources.grdp" />
<part file="cr_elements_resources.grdp" />
<part file="polymer_resources.grdp" />
</if>