blob: 5a0c574f24aed660bb2a87dd0f7ebf13edc53414 [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
* 'internet-detail-dialog' is used in the login screen to show a subset of
* internet details and allow configuration of proxy, IP, and nameservers.
*/
Polymer({
is: 'internet-detail-dialog',
behaviors: [CrPolicyNetworkBehavior, I18nBehavior],
properties: {
/** The network GUID to display details for. */
guid: String,
/**
* The current properties for the network matching |guid|.
* @type {!CrOnc.NetworkProperties|undefined}
*/
networkProperties: Object,
/**
* Interface for networkingPrivate calls, passed from internet_page.
* @type {NetworkingPrivate}
*/
networkingPrivate: {
type: Object,
value: chrome.networkingPrivate,
},
},
/**
* Listener function for chrome.networkingPrivate.onNetworksChanged event.
* @type {?function(!Array<string>)}
* @private
*/
networksChangedListener_: null,
/**
* Set to true to once the initial properties have been received. This
* prevents setProperties from being called when setting default properties.
* @private {boolean}
*/
networkPropertiesReceived_: false,
/** @override */
attached: function() {
var dialogArgs = chrome.getVariableValue('dialogArguments');
var type, name;
if (dialogArgs) {
var args = JSON.parse(dialogArgs);
this.guid = args.guid || '';
type = args.type || 'WiFi';
name = args.name || type;
} else {
// For debugging
var params = new URLSearchParams(document.location.search.substring(1));
this.guid = params.get('guid') || '';
type = params.get('type') || 'WiFi';
name = params.get('name') || type;
}
if (!this.guid) {
console.error('Invalid guid');
this.close_();
}
if (!this.networksChangedListener_) {
this.networksChangedListener_ = this.onNetworksChangedEvent_.bind(this);
this.networkingPrivate.onNetworksChanged.addListener(
this.networksChangedListener_);
}
// Set basic networkProperties until they are loaded.
this.networkProperties = {
GUID: this.guid,
Type: type,
ConnectionState: CrOnc.ConnectionState.NOT_CONNECTED,
Name: {Active: name},
};
this.networkPropertiesReceived_ = false;
this.getNetworkDetails_();
},
/** @override */
detached: function() {
if (this.networksChangedListener_) {
this.networkingPrivate.onNetworksChanged.removeListener(
this.networksChangedListener_);
this.networksChangedListener_ = null;
}
},
/** @private */
close_: function() {
chrome.send('dialogClose');
},
/**
* networkingPrivate.onNetworksChanged event callback.
* @param {!Array<string>} networkIds The list of changed network GUIDs.
* @private
*/
onNetworksChangedEvent_: function(networkIds) {
if (networkIds.includes(this.guid))
this.getNetworkDetails_();
},
/**
* Calls networkingPrivate.getProperties for this.guid.
* @private
*/
getNetworkDetails_: function() {
assert(!!this.guid);
this.networkingPrivate.getManagedProperties(
this.guid, this.getPropertiesCallback_.bind(this));
},
/**
* networkingPrivate.getProperties callback.
* @param {!CrOnc.NetworkProperties} properties The network properties.
* @private
*/
getPropertiesCallback_: function(properties) {
if (chrome.runtime.lastError) {
var message = chrome.runtime.lastError.message;
if (message == 'Error.InvalidNetworkGuid') {
console.error('Details page: GUID no longer exists: ' + this.guid);
} else {
console.error(
'Unexpected networkingPrivate.getManagedProperties error: ' +
message + ' For: ' + this.guid);
}
this.close_();
return;
}
if (!properties) {
console.error('No properties for: ' + this.guid);
this.close_();
return;
}
this.networkProperties = properties;
this.networkPropertiesReceived_ = true;
},
/**
* @param {!chrome.networkingPrivate.NetworkConfigProperties} onc The ONC
* network properties.
* @private
*/
setNetworkProperties_: function(onc) {
if (!this.networkPropertiesReceived_)
return;
assert(!!this.guid);
this.networkingPrivate.setProperties(this.guid, onc, () => {
if (chrome.runtime.lastError) {
// An error typically indicates invalid input; request the properties
// to update any invalid fields.
this.getNetworkDetails_();
}
});
},
/**
* @return {!chrome.networkingPrivate.NetworkConfigProperties} An ONC
* dictionary with just the Type property set. Used for passing properties
* to setNetworkProperties_.
* @private
*/
getEmptyNetworkProperties_: function() {
return {Type: this.networkProperties.Type};
},
/**
* @param {!CrOnc.NetworkProperties} networkProperties
* @return {string}
* @private
*/
getStateText_: function(networkProperties) {
if (!networkProperties.ConnectionState)
return '';
return this.i18n('Onc' + networkProperties.ConnectionState);
},
/**
* @param {!CrOnc.NetworkProperties} networkProperties
* @return {string}
* @private
*/
getNameText_: function(networkProperties) {
return CrOnc.getNetworkName(networkProperties);
},
/**
* @param {!CrOnc.NetworkProperties} networkProperties
* @return {boolean} True if the network is connected.
* @private
*/
isConnectedState_: function(networkProperties) {
return networkProperties.ConnectionState == CrOnc.ConnectionState.CONNECTED;
},
/**
* @param {!CrOnc.NetworkProperties} networkProperties
* @return {boolean}
* @private
*/
isRemembered_: function(networkProperties) {
var source = networkProperties.Source;
return !!source && source != CrOnc.Source.NONE;
},
/**
* @param {!CrOnc.NetworkProperties} networkProperties
* @return {boolean}
* @private
*/
isRememberedOrConnected_: function(networkProperties) {
return this.isRemembered_(networkProperties) ||
this.isConnectedState_(networkProperties);
},
/**
* @param {!CrOnc.NetworkProperties} networkProperties
* @return {boolean}
* @private
*/
isCellular_: function(networkProperties) {
return networkProperties.Type == CrOnc.Type.CELLULAR &&
!!networkProperties.Cellular;
},
/**
* @param {!CrOnc.NetworkProperties} networkProperties
* @return {boolean}
* @private
*/
showCellularSim_: function(networkProperties) {
return networkProperties.Type == CrOnc.Type.CELLULAR &&
!!networkProperties.Cellular &&
networkProperties.Cellular.Family != 'CDMA';
},
/**
* @param {!CrOnc.NetworkProperties} networkProperties
* @return {boolean}
* @private
*/
showCellularChooseNetwork_: function(networkProperties) {
return networkProperties.Type == CrOnc.Type.CELLULAR &&
!!this.get('Cellular.SupportNetworkScan', this.networkProperties);
},
/**
* @param {!CrOnc.NetworkProperties} networkProperties
* @return {boolean}
* @private
*/
showConnect_: function(networkProperties) {
return networkProperties.Type != CrOnc.Type.ETHERNET &&
networkProperties.ConnectionState ==
CrOnc.ConnectionState.NOT_CONNECTED;
},
/**
* @param {!CrOnc.NetworkProperties} networkProperties
* @return {boolean}
* @private
*/
showDisconnect_: function(networkProperties) {
return networkProperties.Type != CrOnc.Type.ETHERNET &&
networkProperties.ConnectionState !=
CrOnc.ConnectionState.NOT_CONNECTED;
},
/**
* @param {!CrOnc.NetworkProperties} networkProperties
* @return {boolean}
* @private
*/
shouldShowProxyPolicyIndicator_: function(networkProperties) {
var property = this.get('ProxySettings.Type', networkProperties);
return !!property &&
this.isNetworkPolicyEnforced(
/** @type {!CrOnc.ManagedProperty} */ (property));
},
/**
* @param {!CrOnc.NetworkProperties} networkProperties
* @return {boolean} Whether or not to enable the network connect button.
* @private
*/
enableConnect_: function(networkProperties) {
if (!this.showConnect_(networkProperties))
return false;
if (networkProperties.Type == CrOnc.Type.CELLULAR &&
CrOnc.isSimLocked(networkProperties)) {
return false;
}
return true;
},
/** @private */
onConnectTap_: function() {
var properties = this.networkProperties;
this.networkingPrivate.startConnect(properties.GUID, function() {
if (chrome.runtime.lastError) {
var message = chrome.runtime.lastError.message;
if (message == 'connecting' || message == 'connect-canceled' ||
message == 'connected' || message == 'Error.InvalidNetworkGuid') {
return;
}
console.error(
'Unexpected networkingPrivate.startConnect error: ' + message +
' For: ' + properties.GUID);
}
});
},
/** @private */
onDisconnectTap_: function() {
this.networkingPrivate.startDisconnect(this.guid);
},
/**
* Event triggered for elements associated with network properties.
* @param {!{detail: !{field: string, value: (string|!Object)}}} event
* @private
*/
onNetworkPropertyChange_: function(event) {
if (!this.networkProperties)
return;
var field = event.detail.field;
var value = event.detail.value;
var onc = this.getEmptyNetworkProperties_();
if (field == 'APN') {
CrOnc.setTypeProperty(onc, 'APN', value);
} else if (field == 'SIMLockStatus') {
CrOnc.setTypeProperty(onc, 'SIMLockStatus', value);
} else {
console.error('Unexpected property change event: ' + field);
return;
}
this.setNetworkProperties_(onc);
},
/**
* Event triggered when the IP Config or NameServers element changes.
* TODO(stevenjb): Move this logic down to network_ip_config.js and
* network_nameservers.js and remove it from here and internet_detail_page.js.
* @param {!{detail: !{field: string,
* value: (string|!CrOnc.IPConfigProperties|
* !Array<string>)}}} event
* The network-ip-config or network-nameservers change event.
* @private
*/
onIPConfigChange_: function(event) {
if (!this.networkProperties)
return;
var field = event.detail.field;
var value = event.detail.value;
// Get an empty ONC dictionary and set just the IP Config properties that
// need to change.
var onc = this.getEmptyNetworkProperties_();
var ipConfigType =
/** @type {chrome.networkingPrivate.IPConfigType|undefined} */ (
CrOnc.getActiveValue(this.networkProperties.IPAddressConfigType));
if (field == 'IPAddressConfigType') {
var newIpConfigType =
/** @type {chrome.networkingPrivate.IPConfigType} */ (value);
if (newIpConfigType == ipConfigType)
return;
onc.IPAddressConfigType = newIpConfigType;
} else if (field == 'NameServersConfigType') {
var nsConfigType =
/** @type {chrome.networkingPrivate.IPConfigType|undefined} */ (
CrOnc.getActiveValue(
this.networkProperties.NameServersConfigType));
var newNsConfigType =
/** @type {chrome.networkingPrivate.IPConfigType} */ (value);
if (newNsConfigType == nsConfigType)
return;
onc.NameServersConfigType = newNsConfigType;
} else if (field == 'StaticIPConfig') {
if (ipConfigType == CrOnc.IPConfigType.STATIC) {
var staticIpConfig = this.networkProperties.StaticIPConfig;
var ipConfigValue = /** @type {!Object} */ (value);
if (staticIpConfig &&
this.allPropertiesMatch_(staticIpConfig, ipConfigValue)) {
return;
}
}
onc.IPAddressConfigType = CrOnc.IPConfigType.STATIC;
if (!onc.StaticIPConfig) {
onc.StaticIPConfig =
/** @type {!chrome.networkingPrivate.IPConfigProperties} */ ({});
}
// Only copy Static IP properties.
var keysToCopy = ['Type', 'IPAddress', 'RoutingPrefix', 'Gateway'];
for (var i = 0; i < keysToCopy.length; ++i) {
var key = keysToCopy[i];
if (key in value)
onc.StaticIPConfig[key] = value[key];
}
} else if (field == 'NameServers') {
// If a StaticIPConfig property is specified and its NameServers value
// matches the new value, no need to set anything.
var nameServers = /** @type {!Array<string>} */ (value);
if (onc.NameServersConfigType == CrOnc.IPConfigType.STATIC &&
onc.StaticIPConfig && onc.StaticIPConfig.NameServers == nameServers) {
return;
}
onc.NameServersConfigType = CrOnc.IPConfigType.STATIC;
if (!onc.StaticIPConfig) {
onc.StaticIPConfig =
/** @type {!chrome.networkingPrivate.IPConfigProperties} */ ({});
}
onc.StaticIPConfig.NameServers = nameServers;
} else {
console.error('Unexpected change field: ' + field);
return;
}
// setValidStaticIPConfig will fill in any other properties from
// networkProperties. This is necessary since we update IP Address and
// NameServers independently.
CrOnc.setValidStaticIPConfig(onc, this.networkProperties);
this.setNetworkProperties_(onc);
},
/**
* Event triggered when the Proxy configuration element changes.
* @param {!{detail: {field: string, value: !CrOnc.ProxySettings}}} event
* The network-proxy change event.
* @private
*/
onProxyChange_: function(event) {
if (!this.networkProperties)
return;
if (event.detail.field != 'ProxySettings')
return;
var onc = this.getEmptyNetworkProperties_();
CrOnc.setProperty(
onc, 'ProxySettings', /** @type {!Object} */ (event.detail.value));
this.setNetworkProperties_(onc);
},
/**
* @param {!Array<string>} fields
* @return {boolean}
* @private
*/
hasVisibleFields_: function(fields) {
return fields.some((field) => {
var value = this.get(field, this.networkProperties);
return value !== undefined && value !== '';
});
},
/**
* @return {boolean}
* @private
*/
hasInfoFields_: function() {
return this.hasVisibleFields_(this.getInfoFields_());
},
/**
* @return {!Array<string>} The fields to display in the info section.
* @private
*/
getInfoFields_: function() {
/** @type {!Array<string>} */ var fields = [];
var type = this.networkProperties.Type;
if (type == CrOnc.Type.CELLULAR && !!this.networkProperties.Cellular) {
fields.push(
'Cellular.HomeProvider.Name', 'Cellular.ServingOperator.Name',
'Cellular.ActivationState', 'Cellular.RoamingState',
'RestrictedConnectivity', 'Cellular.MEID', 'Cellular.ESN',
'Cellular.ICCID', 'Cellular.IMEI', 'Cellular.IMSI', 'Cellular.MDN',
'Cellular.MIN');
} else if (type == CrOnc.Type.WI_FI) {
fields.push('RestrictedConnectivity');
} else if (type == CrOnc.Type.WI_MAX) {
fields.push('RestrictedConnectivity', 'WiMAX.EAP.Identity');
}
fields.push('MacAddress');
return fields;
},
/**
* @param {!Object} curValue
* @param {!Object} newValue
* @return {boolean} True if all properties set in |newValue| are equal to
* the corresponding properties in |curValue|. Note: Not all properties
* of |curValue| need to be specified in |newValue| for this to return
* true.
* @private
*/
allPropertiesMatch_: function(curValue, newValue) {
return Object.getOwnPropertyNames(newValue).every(
key => newValue[key] == curValue[key]);
}
});