blob: f5ca74c1d59d47b0a8608adc7159c8c48ff0f4e9 [file] [log] [blame]
// 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}
*/
proxyIsUserModified_: 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.proxyIsUserModified_ = false;
this.proxy_ = this.createDefaultProxySettings_();
this.updateProxy_();
},
/** @private */
networkPropertiesChanged_: function() {
if (this.proxyIsUserModified_)
return; // Ignore update.
this.updateProxy_();
},
/** @private */
updateProxy_: function() {
if (!this.networkProperties)
return;
/** @type {!CrOnc.ProxySettings} */
const proxy = this.createDefaultProxySettings_();
// For shared networks with unmanaged proxy settings, ignore any saved
// proxy settings (use the default values).
if (this.isShared_()) {
const property = this.getProxySettingsTypeProperty_();
if (!this.isControlled(property) && !this.useSharedProxies) {
this.setProxyAsync_(proxy);
return; // Proxy settings will be ignored.
}
}
/** @type {!chrome.networkingPrivate.ManagedProxySettings|undefined} */
const 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.getActiveProperties(proxySettings.Manual.HTTPProxy)) ||
{Host: '', Port: 80};
proxy.Manual.SecureHTTPProxy =
/** @type {!CrOnc.ProxyLocation|undefined} */ (
CrOnc.getActiveProperties(
proxySettings.Manual.SecureHTTPProxy)) ||
{Host: '', Port: 80};
proxy.Manual.FTPProxy =
/** @type {!CrOnc.ProxyLocation|undefined} */ (
CrOnc.getActiveProperties(proxySettings.Manual.FTPProxy)) ||
{Host: '', Port: 80};
proxy.Manual.SOCKS =
/** @type {!CrOnc.ProxyLocation|undefined} */ (
CrOnc.getActiveProperties(proxySettings.Manual.SOCKS)) ||
{Host: '', Port: 80};
const 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.HTTPProxy.Host &&
!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.
const ipv4 =
CrOnc.getIPConfigForType(this.networkProperties, CrOnc.IPType.IPV4);
this.WPAD_ = (ipv4 && ipv4.WebProxyAutoDiscoveryUrl) ||
this.i18n('networkProxyWpadNone');
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.proxyIsUserModified_ = false;
});
},
/** @private */
useSameProxyChanged_: function() {
this.proxyIsUserModified_ = 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() {
const proxy =
/** @type {!CrOnc.ProxySettings} */ (Object.assign({}, this.proxy_));
if (proxy.Type == CrOnc.ProxySettingsType.MANUAL) {
const manual = proxy.Manual;
const 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.proxyIsUserModified_ = false;
},
/**
* Event triggered when the selected proxy type changes.
* @param {!Event} event
* @private
*/
onTypeChange_: function(event) {
const target = /** @type {!HTMLSelectElement} */ (event.target);
const type = /** @type {chrome.networkingPrivate.ProxySettingsType} */ (
target.value);
this.set('proxy_.Type', type);
let proxyTypeChangeIsReady;
let elementToFocus;
switch (type) {
case CrOnc.ProxySettingsType.DIRECT:
case CrOnc.ProxySettingsType.WPAD:
// No addtional values are required, send the type change.
proxyTypeChangeIsReady = true;
break;
case CrOnc.ProxySettingsType.PAC:
elementToFocus = this.$$('#pacInput');
// If a PAC is already defined, send the type change now, otherwise wait
// until the user provides a PAC value.
proxyTypeChangeIsReady = !!this.proxy_.PAC;
break;
case CrOnc.ProxySettingsType.MANUAL:
// Manual proxy configuration includes multiple input fields, so wait
// until the 'send' button is clicked.
proxyTypeChangeIsReady = false;
elementToFocus = this.$$('#manualProxy network-proxy-input');
break;
}
// If the new proxy type is fully configured, send it, otherwise set
// |proxyIsUserModified_| to true so that property updates do not
// overwrite user changes.
if (proxyTypeChangeIsReady)
this.sendProxyChange_();
else
this.proxyIsUserModified_ = true;
if (elementToFocus) {
this.async(() => {
elementToFocus.focus();
});
}
},
/** @private */
onPACChange_: function() {
this.sendProxyChange_();
},
/** @private */
onProxyInputChange_: function() {
this.proxyIsUserModified_ = true;
},
/** @private */
onAddProxyExclusionTap_: function() {
const value = this.$.proxyExclusion.value;
if (!value)
return;
this.push('proxy_.ExcludeDomains', value);
// Clear input.
this.$.proxyExclusion.value = '';
this.proxyIsUserModified_ = true;
},
/**
* @param {!Event} event
* @private
*/
onAddProxyExclusionKeypress_: function(event) {
if (event.key != 'Enter')
return;
event.stopPropagation();
this.onAddProxyExclusionTap_();
},
/**
* Event triggered when the proxy exclusion list changes.
* @param {!Event} event The remove proxy exclusions change event.
* @private
*/
onProxyExclusionsChange_: function(event) {
this.proxyIsUserModified_ = 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.
const 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.proxyIsUserModified_)
return false;
const manual = this.proxy_.Manual;
const 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;
},
});