summaryrefslogtreecommitdiff
path: root/chromium/ui/webui/resources/cr_components/chromeos
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/ui/webui/resources/cr_components/chromeos')
-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
19 files changed, 2612 insertions, 0 deletions
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>