blob: 8f2b2d7b745576f626613a0f77702119b7ecd590 [file] [log] [blame]
// 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.
(function() {
const mojom = chromeos.networkConfig.mojom;
/**
* @fileoverview
* 'settings-internet-page' is the settings page containing internet
* settings.
*/
Polymer({
is: 'settings-internet-page',
behaviors: [
CrNetworkListenerBehavior,
I18nBehavior,
settings.RouteObserverBehavior,
WebUIListenerBehavior,
],
properties: {
/** Preferences state. */
prefs: {
type: Object,
notify: true,
},
/**
* The device state for each network device type, keyed by NetworkType. Set
* by network-summary.
* @type {!Object<!OncMojo.DeviceStateProperties>|undefined}
* @private
*/
deviceStates: {
type: Object,
notify: true,
observer: 'onDeviceStatesChanged_',
},
/**
* Highest priority connected network or null. Set by network-summary.
* @type {?OncMojo.NetworkStateProperties|undefined}
*/
defaultNetwork: {
type: Object,
notify: true,
},
/**
* Set by internet-subpage. Controls spinner visibility in subpage header.
* @private
*/
showSpinner_: Boolean,
/**
* The network type for the networks subpage when shown.
* @type {chromeos.networkConfig.mojom.NetworkType}
* @private
*/
subpageType_: Number,
/**
* The network type for the known networks subpage when shown.
* @type {chromeos.networkConfig.mojom.NetworkType}
* @private
*/
knownNetworksType_: Number,
/**
* Whether the 'Add connection' section is expanded.
* @private
*/
addConnectionExpanded_: {
type: Boolean,
value: false,
},
/** @private {!chromeos.networkConfig.mojom.GlobalPolicy|undefined} */
globalPolicy_: Object,
/**
* Whether a managed network is available in the visible network list.
* @private {boolean}
*/
managedNetworkAvailable: {
type: Boolean,
value: false,
},
/**
* List of third party (Extension + Arc) VPN providers.
* @type {!Array<!chromeos.networkConfig.mojom.VpnProvider>}
* @private
*/
vpnProviders_: {
type: Array,
value: function() {
return [];
}
},
/** @private {boolean} */
showInternetConfig_: {
type: Boolean,
value: false,
},
/** @private {!Map<string, Element>} */
focusConfig_: {
type: Object,
value: function() {
return new Map();
},
},
},
/**
* Type of last detail page visited
* @private {chromeos.networkConfig.mojom.NetworkType|undefined}
*/
detailType_: undefined,
// Element event listeners
listeners: {
'device-enabled-toggled': 'onDeviceEnabledToggled_',
'network-connect': 'onNetworkConnect_',
'show-config': 'onShowConfig_',
'show-detail': 'onShowDetail_',
'show-known-networks': 'onShowKnownNetworks_',
'show-networks': 'onShowNetworks_',
},
/** @private {?settings.InternetPageBrowserProxy} */
browserProxy_: null,
/** @private {?chromeos.networkConfig.mojom.CrosNetworkConfigRemote} */
networkConfig_: null,
/** @override */
created: function() {
this.browserProxy_ = settings.InternetPageBrowserProxyImpl.getInstance();
this.networkConfig_ = network_config.MojoInterfaceProviderImpl.getInstance()
.getMojoServiceRemote();
},
/** @override */
attached: function() {
this.networkConfig_.getGlobalPolicy().then(response => {
this.globalPolicy_ = response.result;
});
this.onVpnProvidersChanged();
},
/**
* settings.RouteObserverBehavior
* @param {!settings.Route} route
* @param {!settings.Route} oldRoute
* @protected
*/
currentRouteChanged: function(route, oldRoute) {
if (route == settings.routes.INTERNET_NETWORKS) {
// Handle direct navigation to the networks page,
// e.g. chrome://settings/internet/networks?type=WiFi
const queryParams = settings.getQueryParameters();
const type = queryParams.get('type');
if (type) {
this.subpageType_ = OncMojo.getNetworkTypeFromString(type);
}
} else if (route == settings.routes.KNOWN_NETWORKS) {
// Handle direct navigation to the known networks page,
// e.g. chrome://settings/internet/knownNetworks?type=WiFi
const queryParams = settings.getQueryParameters();
const type = queryParams.get('type');
if (type) {
this.knownNetworksType_ = OncMojo.getNetworkTypeFromString(type);
}
} else if (
route != settings.routes.INTERNET && route != settings.routes.BASIC) {
// If we are navigating to a non internet section, do not set focus.
return;
}
if (!settings.routes.INTERNET ||
!settings.routes.INTERNET.contains(oldRoute)) {
return;
}
// Focus the subpage arrow where appropriate.
let element;
if (route == settings.routes.INTERNET_NETWORKS) {
// iron-list makes the correct timing to focus an item in the list
// very complicated, and the item may not exist, so just focus the
// entire list for now.
const subPage = this.$$('settings-internet-subpage');
if (subPage) {
element = subPage.$$('#networkList');
}
} else if (this.detailType_ !== undefined) {
const oncType = OncMojo.getNetworkTypeString(this.detailType_);
const rowForDetailType = this.$$('network-summary').$$(`#${oncType}`);
// Note: It is possible that the row is no longer present in the DOM
// (e.g., when a Cellular dongle is unplugged or when Instant Tethering
// becomes unavailable due to the Bluetooth controller disconnecting).
if (rowForDetailType) {
element = rowForDetailType.$$('.subpage-arrow');
}
}
if (element) {
this.focusConfig_.set(oldRoute.path, element);
} else {
this.focusConfig_.delete(oldRoute.path);
}
},
/** CrNetworkListenerBehavior override */
onVpnProvidersChanged: function() {
this.networkConfig_.getVpnProviders().then(response => {
const providers = response.providers;
providers.sort(this.compareVpnProviders_);
this.vpnProviders_ = providers;
});
},
/**
* Event triggered by a device state enabled toggle.
* @param {!CustomEvent<!{
* enabled: boolean,
* type: chromeos.networkConfig.mojom.NetworkType
* }>} event
* @private
*/
onDeviceEnabledToggled_: function(event) {
this.networkConfig_.setNetworkTypeEnabledState(
event.detail.type, event.detail.enabled);
},
/**
* @param {!CustomEvent<!{type: string, guid: ?string, name: ?string}>} event
* @private
*/
onShowConfig_: function(event) {
const type = OncMojo.getNetworkTypeFromString(event.detail.type);
if (!event.detail.guid) {
// New configuration
this.showConfig_(true /* configAndConnect */, type);
} else {
this.showConfig_(
false /* configAndConnect */, type, event.detail.guid,
event.detail.name);
}
},
/**
* @param {boolean} configAndConnect
* @param {chromeos.networkConfig.mojom.NetworkType} type
* @param {?string=} opt_guid
* @param {?string=} opt_name
* @private
*/
showConfig_: function(configAndConnect, type, opt_guid, opt_name) {
assert(
type != chromeos.networkConfig.mojom.NetworkType.kCellular &&
type != chromeos.networkConfig.mojom.NetworkType.kTether);
if (this.showInternetConfig_) {
return;
}
this.showInternetConfig_ = true;
// Async call to ensure dialog is stamped.
setTimeout(() => {
const configDialog =
/** @type {!InternetConfigElement} */ (this.$$('#configDialog'));
assert(!!configDialog);
configDialog.type = OncMojo.getNetworkTypeString(type);
configDialog.guid = opt_guid || '';
configDialog.name = opt_name || '';
configDialog.showConnect = configAndConnect;
configDialog.open();
});
},
/** @private */
onInternetConfigClose_: function() {
this.showInternetConfig_ = false;
},
/**
* @param {!CustomEvent<!OncMojo.NetworkStateProperties>} event
* @private
*/
onShowDetail_: function(event) {
const networkState = event.detail;
this.detailType_ = networkState.type;
const params = new URLSearchParams;
params.append('guid', networkState.guid);
params.append('type', OncMojo.getNetworkTypeString(networkState.type));
params.append('name', OncMojo.getNetworkStateDisplayName(networkState));
settings.navigateTo(settings.routes.NETWORK_DETAIL, params);
},
/**
* @param {!CustomEvent<chromeos.networkConfig.mojom.NetworkType>} event
* @private
*/
onShowNetworks_: function(event) {
this.showNetworksSubpage_(event.detail);
},
/**
* @return {string}
* @private
*/
getNetworksPageTitle_: function() {
// The shared Cellular/Tether subpage is referred to as "Mobile".
// TODO(khorimoto): Remove once Cellular/Tether are split into their own
// sections.
if (this.subpageType_ == mojom.NetworkType.kCellular ||
this.subpageType_ == mojom.NetworkType.kTether) {
return this.i18n('OncTypeMobile');
}
return this.i18n(
'OncType' + OncMojo.getNetworkTypeString(this.subpageType_));
},
/**
* @param {chromeos.networkConfig.mojom.NetworkType} subpageType
* @param {!Object<!OncMojo.DeviceStateProperties>|undefined} deviceStates
* @return {!OncMojo.DeviceStateProperties|undefined}
* @private
*/
getDeviceState_: function(subpageType, deviceStates) {
if (subpageType === undefined) {
return undefined;
}
// If both Tether and Cellular are enabled, use the Cellular device state
// when directly navigating to the Tether page.
if (subpageType == mojom.NetworkType.kTether &&
this.deviceStates[mojom.NetworkType.kCellular]) {
subpageType = mojom.NetworkType.kCellular;
}
return deviceStates[subpageType];
},
/**
* @param {!Object<!OncMojo.DeviceStateProperties>|undefined} deviceStates
* @return {!OncMojo.DeviceStateProperties|undefined}
* @private
*/
getTetherDeviceState_: function(deviceStates) {
return deviceStates[mojom.NetworkType.kTether];
},
/**
* @param {!OncMojo.DeviceStateProperties|undefined} newValue
* @param {!OncMojo.DeviceStateProperties|undefined} oldValue
* @private
*/
onDeviceStatesChanged_: function(newValue, oldValue) {
const wifiDeviceState =
this.getDeviceState_(mojom.NetworkType.kWiFi, newValue);
let managedNetworkAvailable = false;
if (wifiDeviceState) {
managedNetworkAvailable = !!wifiDeviceState.managedNetworkAvailable;
}
if (this.managedNetworkAvailable != managedNetworkAvailable) {
this.managedNetworkAvailable = managedNetworkAvailable;
}
if (this.detailType_ && !this.deviceStates[this.detailType_]) {
// If the device type associated with the current network has been
// removed (e.g., due to unplugging a Cellular dongle), the details page,
// if visible, displays controls which are no longer functional. If this
// case occurs, close the details page.
const detailPage = this.$$('settings-internet-detail-page');
if (detailPage) {
detailPage.close();
}
}
},
/**
* @param {!CustomEvent<chromeos.networkConfig.mojom.NetworkType>} event
* @private
*/
onShowKnownNetworks_: function(event) {
const type = event.detail;
this.detailType_ = type;
this.knownNetworksType_ = type;
const params = new URLSearchParams;
params.append('type', OncMojo.getNetworkTypeString(type));
settings.navigateTo(settings.routes.KNOWN_NETWORKS, params);
},
/** @private */
onAddWiFiTap_: function() {
this.showConfig_(
true /* configAndConnect */,
chromeos.networkConfig.mojom.NetworkType.kWiFi);
},
/** @private */
onAddVPNTap_: function() {
this.showConfig_(
true /* configAndConnect */,
chromeos.networkConfig.mojom.NetworkType.kVPN);
},
/**
* @param {!{model: !{item: !mojom.VpnProvider}}} event
* @private
*/
onAddThirdPartyVpnTap_: function(event) {
const provider = event.model.item;
this.browserProxy_.addThirdPartyVpn(provider.appId);
},
/**
* @param {chromeos.networkConfig.mojom.NetworkType} type
* @private
*/
showNetworksSubpage_: function(type) {
this.detailType_ = type;
const params = new URLSearchParams;
params.append('type', OncMojo.getNetworkTypeString(type));
this.subpageType_ = type;
settings.navigateTo(settings.routes.INTERNET_NETWORKS, params);
},
/**
* @param {!mojom.VpnProvider} vpnProvider1
* @param {!mojom.VpnProvider} vpnProvider2
* @return {number}
*/
compareVpnProviders_: function(vpnProvider1, vpnProvider2) {
// Show Extension VPNs before Arc VPNs.
if (vpnProvider1.type < vpnProvider2.type) {
return -1;
}
if (vpnProvider1.type > vpnProvider2.type) {
return 1;
}
// Show VPNs of the same type by lastLaunchTime.
if (vpnProvider1.lastLaunchTime.internalValue >
vpnProvider2.lastLaunchTime.internalValue) {
return -1;
}
if (vpnProvider1.lastLaunchTime.internalValue <
vpnProvider2.lastLaunchTime.internalValue) {
return 1;
}
return 0;
},
/**
* @param {!Array<!OncMojo.DeviceStateProperties>} deviceStates
* @return {boolean}
* @private
*/
wifiIsEnabled_: function(deviceStates) {
const wifi = deviceStates[mojom.NetworkType.kWiFi];
return !!wifi &&
wifi.deviceState ==
chromeos.networkConfig.mojom.DeviceStateType.kEnabled;
},
/**
* @param {!mojom.GlobalPolicy} globalPolicy
* @param {boolean} managedNetworkAvailable
* @return {boolean}
*/
allowAddConnection_: function(globalPolicy, managedNetworkAvailable) {
if (!globalPolicy) {
return true;
}
return !globalPolicy.allowOnlyPolicyNetworksToConnect &&
(!globalPolicy.allowOnlyPolicyNetworksToConnectIfAvailable ||
!managedNetworkAvailable);
},
/**
* @param {!mojom.VpnProvider} provider
* @return {string}
*/
getAddThirdPartyVpnLabel_: function(provider) {
return this.i18n('internetAddThirdPartyVPN', provider.providerName || '');
},
/**
* Handles UI requests to connect to a network.
* TODO(stevenjb): Handle Cellular activation, etc.
* @param {!CustomEvent<!{
* networkState: !OncMojo.NetworkStateProperties,
* bypassConnectionDialog: (boolean|undefined)
* }>} event
* @private
*/
onNetworkConnect_: function(event) {
const networkState = event.detail.networkState;
const type = networkState.type;
const displayName = OncMojo.getNetworkStateDisplayName(networkState);
if (!event.detail.bypassConnectionDialog &&
type == mojom.NetworkType.kTether &&
!networkState.typeState.tether.hasConnectedToHost) {
const params = new URLSearchParams;
params.append('guid', networkState.guid);
params.append('type', OncMojo.getNetworkTypeString(type));
params.append('name', displayName);
params.append('showConfigure', true.toString());
settings.navigateTo(settings.routes.NETWORK_DETAIL, params);
return;
}
const isMobile = OncMojo.networkTypeIsMobile(type);
if (!isMobile && (!networkState.connectable || !!networkState.errorState)) {
this.showConfig_(
true /* configAndConnect */, type, networkState.guid, displayName);
return;
}
this.networkConfig_.startConnect(networkState.guid).then(response => {
switch (response.result) {
case mojom.StartConnectResult.kSuccess:
return;
case mojom.StartConnectResult.kInvalidGuid:
case mojom.StartConnectResult.kInvalidState:
case mojom.StartConnectResult.kCanceled:
// TODO(stevenjb/khorimoto): Consider handling these cases.
return;
case mojom.StartConnectResult.kNotConfigured:
if (!isMobile) {
this.showConfig_(
true /* configAndConnect */, type, networkState.guid,
displayName);
}
return;
case mojom.StartConnectResult.kBlocked:
// This shouldn't happen, the UI should prevent this, fall through and
// show the error.
case mojom.StartConnectResult.kUnknown:
console.error(
'startConnect failed for: ' + networkState.guid +
' Error: ' + response.message);
return;
}
assertNotReached();
});
},
});
})();