blob: 2d1c08ee297fcf3f8f2b506faa32ac8c2f935d45 [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 information about WiFi,
* WiMAX, or virtual networks.
*/
Polymer({
is: 'settings-internet-subpage',
behaviors: [
CrPolicyNetworkBehavior,
settings.RouteObserverBehavior,
I18nBehavior,
],
properties: {
/**
* Highest priority connected network or null. Provided by
* settings-internet-page (but set in network-summary).
* @type {?CrOnc.NetworkStateProperties|undefined}
*/
defaultNetwork: Object,
/**
* Device state for the network type. Note: when both Cellular and Tether
* are available this will always be set to the Cellular device state and
* |tetherDeviceState| will be set to the Tether device state.
* @type {!CrOnc.DeviceStateProperties|undefined}
*/
deviceState: Object,
/**
* If both Cellular and Tether technologies exist, we combine the subpages
* and set this to the device state for Tether.
* @type {!CrOnc.DeviceStateProperties|undefined}
*/
tetherDeviceState: Object,
/** @type {!chrome.networkingPrivate.GlobalPolicy|undefined} */
globalPolicy: Object,
/**
* List of third party VPN providers.
* @type
* {!Array<!chrome.networkingPrivate.ThirdPartyVPNProperties>|undefined}
*/
thirdPartyVpnProviders: Array,
/**
* List of Arc VPN providers.
* @type {!Array<!settings.ArcVpnProvider>|undefined}
*/
arcVpnProviders: Array,
/**
* Interface for networkingPrivate calls, passed from internet_page.
* @type {!NetworkingPrivate}
*/
networkingPrivate: Object,
showSpinner: {
type: Boolean,
notify: true,
value: false,
},
/**
* List of all network state data for the network type.
* @private {!Array<!CrOnc.NetworkStateProperties>}
*/
networkStateList_: {
type: Array,
value: function() {
return [];
},
},
/**
* Dictionary of lists of network states for third party VPNs.
* @private {!Object<!Array<!CrOnc.NetworkStateProperties>>}
*/
thirdPartyVpns_: {
type: Object,
value: function() {
return {};
},
},
/**
* Dictionary of lists of network states for Arc VPNs.
* @private {!Object<!Array<!CrOnc.NetworkStateProperties>>}
*/
arcVpns_: {
type: Object,
value: function() {
return {};
}
},
/**
* List of potential Tether hosts whose "Google Play Services" notifications
* are disabled (these notifications are required to use Instant Tethering).
* @private {!Array<string>}
*/
notificationsDisabledDeviceNames_: {
type: Array,
value: function() {
return [];
},
},
},
listeners: {'network-list-changed': 'getNetworkStateList_'},
observers: ['deviceStateChanged_(networkingPrivate, deviceState)'],
/** @private {number|null} */
scanIntervalId_: null,
/** @private {settings.InternetPageBrowserProxy} */
browserProxy_: null,
/** @override */
created: function() {
this.browserProxy_ = settings.InternetPageBrowserProxyImpl.getInstance();
},
/** @override */
ready: function() {
this.browserProxy_.setGmsCoreNotificationsDisabledDeviceNamesCallback(
this.onNotificationsDisabledDeviceNamesReceived_.bind(this));
this.browserProxy_.requestGmsCoreNotificationsDisabledDeviceNames();
},
/** override */
detached: function() {
this.stopScanning_();
},
/**
* settings.RouteObserverBehavior
* @param {!settings.Route} route
* @protected
*/
currentRouteChanged: function(route) {
if (route != settings.routes.INTERNET_NETWORKS) {
this.stopScanning_();
return;
}
// Clear any stale data.
this.networkStateList_ = [];
this.thirdPartyVpns_ = {};
this.arcVpns_ = {};
// Request the list of networks and start scanning if necessary.
this.getNetworkStateList_();
this.updateScanning_();
},
/** @private */
deviceStateChanged_: function() {
this.showSpinner = !!this.deviceState.Scanning;
// Scans should only be triggered by the "networks" subpage.
if (settings.getCurrentRoute() != settings.routes.INTERNET_NETWORKS) {
this.stopScanning_();
return;
}
this.updateScanning_();
},
/** @private */
updateScanning_: function() {
if (!this.deviceState)
return;
if (this.shouldStartScan_()) {
this.startScanning_();
return;
}
// deviceState probably changed, re-request networks.
this.getNetworkStateList_();
},
/**
* @return {boolean}
* @private
*/
shouldStartScan_: function() {
// Scans should be kicked off from the Wi-Fi networks subpage.
if (this.deviceState.Type == CrOnc.Type.WI_FI)
return true;
// Scans should be kicked off from the Mobile data subpage, as long as it
// includes Tether networks.
if (this.deviceState.Type == CrOnc.Type.TETHER ||
(this.deviceState.Type == CrOnc.Type.CELLULAR &&
this.tetherDeviceState)) {
return true;
}
return false;
},
/** @private */
startScanning_: function() {
if (this.scanIntervalId_ != null)
return;
const INTERVAL_MS = 10 * 1000;
this.networkingPrivate.requestNetworkScan();
this.scanIntervalId_ = window.setInterval(() => {
this.networkingPrivate.requestNetworkScan();
}, INTERVAL_MS);
},
/** @private */
stopScanning_: function() {
if (this.scanIntervalId_ == null)
return;
window.clearInterval(this.scanIntervalId_);
this.scanIntervalId_ = null;
},
/** @private */
getNetworkStateList_: function() {
if (!this.deviceState)
return;
const filter = {
networkType: this.deviceState.Type,
visible: true,
configured: false
};
this.networkingPrivate.getNetworks(filter, this.onGetNetworks_.bind(this));
},
/**
* @param {!Array<!CrOnc.NetworkStateProperties>} networkStates
* @private
*/
onGetNetworks_: function(networkStates) {
if (!this.deviceState)
return; // Edge case when device states change before this callback.
// For the Cellular/Mobile subpage, request Tether networks if available.
if (this.deviceState.Type == CrOnc.Type.CELLULAR &&
this.tetherDeviceState) {
const filter = {
networkType: CrOnc.Type.TETHER,
visible: true,
configured: false
};
this.networkingPrivate.getNetworks(filter, tetherNetworkStates => {
this.networkStateList_ = networkStates.concat(tetherNetworkStates);
});
return;
}
// For VPNs, separate out third party VPNs and Arc VPNs.
if (this.deviceState.Type == CrOnc.Type.VPN) {
const builtinNetworkStates = [];
const thirdPartyVpns = {};
const arcVpns = {};
for (let i = 0; i < networkStates.length; ++i) {
const state = networkStates[i];
const providerType = this.get('VPN.ThirdPartyVPN.ProviderName', state);
if (providerType) {
thirdPartyVpns[providerType] = thirdPartyVpns[providerType] || [];
thirdPartyVpns[providerType].push(state);
} else if (this.get('VPN.Type', state) == 'ARCVPN') {
const arcProviderName = this.get('VPN.Host', state);
if (state.ConnectionState != CrOnc.ConnectionState.CONNECTED)
continue;
arcVpns[arcProviderName] = arcVpns[arcProviderName] || [];
arcVpns[arcProviderName].push(state);
} else {
builtinNetworkStates.push(state);
}
}
networkStates = builtinNetworkStates;
this.thirdPartyVpns_ = thirdPartyVpns;
this.arcVpns_ = arcVpns;
}
this.networkStateList_ = networkStates;
},
/**
* @param {!Array<string>} notificationsDisabledDeviceNames
* @private
*/
onNotificationsDisabledDeviceNamesReceived_: function(
notificationsDisabledDeviceNames) {
this.notificationsDisabledDeviceNames_ = notificationsDisabledDeviceNames;
},
/**
* @param {!CrOnc.DeviceStateProperties|undefined} deviceState
* @return {boolean} Whether or not the device state is enabled.
* @private
*/
deviceIsEnabled_: function(deviceState) {
return !!deviceState && deviceState.State == CrOnc.DeviceState.ENABLED;
},
/**
* @param {!CrOnc.DeviceStateProperties|undefined} deviceState
* @param {string} onstr
* @param {string} offstr
* @return {string}
* @private
*/
getOffOnString_: function(deviceState, onstr, offstr) {
return this.deviceIsEnabled_(deviceState) ? onstr : offstr;
},
/**
* @param {!CrOnc.DeviceStateProperties|undefined} deviceState
* @return {boolean}
* @private
*/
enableToggleIsVisible_: function(deviceState) {
return !!deviceState && deviceState.Type != CrOnc.Type.ETHERNET &&
deviceState.Type != CrOnc.Type.VPN;
},
/**
* @param {!CrOnc.DeviceStateProperties|undefined} deviceState
* @return {boolean}
* @private
*/
enableToggleIsEnabled_: function(deviceState) {
return !!deviceState && deviceState.State != CrOnc.DeviceState.PROHIBITED;
},
/**
* @param {!CrOnc.DeviceStateProperties|undefined} deviceState
* @return {string}
* @private
*/
getToggleA11yString_: function(deviceState) {
if (!this.enableToggleIsVisible_(deviceState))
return '';
switch (deviceState.Type) {
case CrOnc.Type.TETHER:
case CrOnc.Type.CELLULAR:
return this.i18n('internetToggleMobileA11yLabel');
case CrOnc.Type.WI_FI:
return this.i18n('internetToggleWiFiA11yLabel');
case CrOnc.Type.WI_MAX:
return this.i18n('internetToggleWiMAXA11yLabel');
}
assertNotReached();
return '';
},
/**
* @param {!chrome.networkingPrivate.ThirdPartyVPNProperties} vpnState
* @return {string}
* @private
*/
getAddThirdPartyVpnA11yString_: function(vpnState) {
return this.i18n('internetAddThirdPartyVPN', vpnState.ProviderName);
},
/**
* @param {!settings.ArcVpnProvider} arcVpn
* @return {string}
* @private
*/
getAddArcVpnAllyString_: function(arcVpn) {
return this.i18n('internetAddArcVPNProvider', arcVpn.ProviderName);
},
/**
* @param {!chrome.networkingPrivate.GlobalPolicy} globalPolicy
* @return {boolean}
* @private
*/
allowAddConnection_: function(globalPolicy) {
return globalPolicy && !globalPolicy.AllowOnlyPolicyNetworksToConnect;
},
/**
* @param {!CrOnc.DeviceStateProperties|undefined} deviceState
* @param {!chrome.networkingPrivate.GlobalPolicy} globalPolicy
* @return {boolean}
* @private
*/
showAddButton_: function(deviceState, globalPolicy) {
if (!deviceState || deviceState.Type != CrOnc.Type.WI_FI)
return false;
if (!this.deviceIsEnabled_(deviceState))
return false;
return this.allowAddConnection_(globalPolicy);
},
/** @private */
onAddButtonTap_: function() {
assert(this.deviceState);
const type = this.deviceState.Type;
assert(type != CrOnc.Type.CELLULAR);
if (loadTimeData.getBoolean('networkSettingsConfig'))
this.fire('show-config', {GUID: '', Type: type});
else
chrome.send('addNetwork', [type]);
},
/**
* @param {!{model:
* !{item: !chrome.networkingPrivate.ThirdPartyVPNProperties},
* }} event
* @private
*/
onAddThirdPartyVpnTap_: function(event) {
const provider = event.model.item;
this.browserProxy_.addThirdPartyVpn(CrOnc.Type.VPN, provider.ExtensionID);
},
/**
* @param {!{model:
* !{item: !settings.ArcVpnProvider},
* }} event
* @private
*/
onAddArcVpnTap_: function(event) {
const provider = event.model.item;
settings.InternetPageBrowserProxyImpl.getInstance().addThirdPartyVpn(
CrOnc.Type.VPN, provider.AppID);
},
/**
* @param {!CrOnc.DeviceStateProperties|undefined} deviceState
* @return {boolean}
* @private
*/
knownNetworksIsVisible_: function(deviceState) {
return !!deviceState && deviceState.Type == CrOnc.Type.WI_FI;
},
/**
* Event triggered when the known networks button is tapped.
* @private
*/
onKnownNetworksTap_: function() {
assert(this.deviceState.Type == CrOnc.Type.WI_FI);
this.fire('show-known-networks', {Type: this.deviceState.Type});
},
/**
* Event triggered when the enable button is toggled.
* @param {!Event} event
* @private
*/
onDeviceEnabledTap_: function(event) {
assert(this.deviceState);
this.fire('device-enabled-toggled', {
enabled: !this.deviceIsEnabled_(this.deviceState),
type: this.deviceState.Type
});
// Make sure this does not propagate to onDetailsTap_.
event.stopPropagation();
},
/**
* @param {!Object<!Array<!CrOnc.NetworkStateProperties>>} thirdPartyVpns
* @param {!chrome.networkingPrivate.ThirdPartyVPNProperties} vpnState
* @return {!Array<!CrOnc.NetworkStateProperties>}
* @private
*/
getThirdPartyVpnNetworks_: function(thirdPartyVpns, vpnState) {
return thirdPartyVpns[vpnState.ProviderName] || [];
},
/**
* @param {!Object<!Array<!CrOnc.NetworkStateProperties>>} thirdPartyVpns
* @param {!chrome.networkingPrivate.ThirdPartyVPNProperties} vpnState
* @return {boolean}
* @private
*/
haveThirdPartyVpnNetwork_: function(thirdPartyVpns, vpnState) {
const list = this.getThirdPartyVpnNetworks_(thirdPartyVpns, vpnState);
return !!list.length;
},
/**
* @param {!Object<!Array<!CrOnc.NetworkStateProperties>>} arcVpns
* @param {!settings.ArcVpnProvider} arcVpnProvider
* @return {!Array<!CrOnc.NetworkStateProperties>}
* @private
*/
getArcVpnNetworks_: function(arcVpns, arcVpnProvider) {
return arcVpns[arcVpnProvider.PackageName] || [];
},
/**
* @param {!Object<!Array<!CrOnc.NetworkStateProperties>>} arcVpns
* @param {!settings.ArcVpnProvider} arcVpnProvider
* @return {boolean}
* @private
*/
haveArcVpnNetwork_: function(arcVpns, arcVpnProvider) {
const list = this.getArcVpnNetworks_(arcVpns, arcVpnProvider);
return !!list.length;
},
/**
* Event triggered when a network list item is selected.
* @param {!{target: HTMLElement, detail: !CrOnc.NetworkStateProperties}} e
* @private
*/
onNetworkSelected_: function(e) {
assert(this.globalPolicy);
assert(this.defaultNetwork !== undefined);
const state = e.detail;
e.target.blur();
if (this.canConnect_(state, this.globalPolicy, this.defaultNetwork)) {
this.fire('network-connect', {networkProperties: state});
return;
}
this.fire('show-detail', state);
},
/**
* Determines whether or not a network state can be connected to.
* @param {!CrOnc.NetworkStateProperties} state The network state.
* @param {!chrome.networkingPrivate.GlobalPolicy} globalPolicy
* @param {?CrOnc.NetworkStateProperties} defaultNetwork
* @private
*/
canConnect_: function(state, globalPolicy, defaultNetwork) {
if (state.ConnectionState != CrOnc.ConnectionState.NOT_CONNECTED)
return false;
if (state.Type == CrOnc.Type.WI_FI && globalPolicy &&
globalPolicy.AllowOnlyPolicyNetworksToConnect &&
!this.isPolicySource(state.Source)) {
return false;
}
if (state.Type == CrOnc.Type.VPN &&
(!defaultNetwork ||
defaultNetwork.ConnectionState != CrOnc.ConnectionState.CONNECTED)) {
return false;
}
return true;
},
/**
* @param {!CrOnc.DeviceStateProperties|undefined} deviceState
* @param {!CrOnc.DeviceStateProperties|undefined} tetherDeviceState
* @return {boolean}
* @private
*/
tetherToggleIsVisible_: function(deviceState, tetherDeviceState) {
return !!deviceState && deviceState.Type == CrOnc.Type.CELLULAR &&
!!tetherDeviceState;
},
/**
* @param {!CrOnc.DeviceStateProperties|undefined} deviceState
* @param {!CrOnc.DeviceStateProperties|undefined} tetherDeviceState
* @return {boolean}
* @private
*/
tetherToggleIsEnabled_: function(deviceState, tetherDeviceState) {
return this.tetherToggleIsVisible_(deviceState, tetherDeviceState) &&
this.enableToggleIsEnabled_(tetherDeviceState) &&
tetherDeviceState.State != CrOnc.DeviceState.UNINITIALIZED;
},
/**
* @param {!Event} event
* @private
*/
onTetherEnabledTap_: function(event) {
this.fire('device-enabled-toggled', {
enabled: !this.deviceIsEnabled_(this.tetherDeviceState),
type: CrOnc.Type.TETHER,
});
event.stopPropagation();
},
/**
* @param {*} lhs
* @param {*} rhs
* @return {boolean}
* @private
*/
isEqual_: function(lhs, rhs) {
return lhs === rhs;
},
/**
* @param {!Array<!CrOnc.NetworkStateProperties>} networkStateList
* @return {boolean}
* @private
*/
shouldShowNetworkList_: function(networkStateList) {
return networkStateList.length > 0;
},
/**
* @param {!CrOnc.DeviceStateProperties|undefined} deviceState
* @param {!CrOnc.DeviceStateProperties|undefined} tetherDeviceState
* @return {string}
* @private
*/
getNoNetworksString_: function(deviceState, tetherDeviceState) {
const type = deviceState.Type;
if (type == CrOnc.Type.TETHER ||
(type == CrOnc.Type.CELLULAR && this.tetherDeviceState)) {
return this.i18nAdvanced('internetNoNetworksMobileData');
}
return this.i18n('internetNoNetworks');
},
/**
* @param {!Array<string>} notificationsDisabledDeviceNames
* @return {boolean}
* @private
*/
showGmsCoreNotificationsSection_: function(notificationsDisabledDeviceNames) {
return notificationsDisabledDeviceNames.length > 0;
},
/**
* @param {!Array<string>} notificationsDisabledDeviceNames
* @return {string}
* @private
*/
getGmsCoreNotificationsDevicesString_: function(
notificationsDisabledDeviceNames) {
if (notificationsDisabledDeviceNames.length == 1) {
return this.i18n(
'gmscoreNotificationsOneDeviceSubtitle',
notificationsDisabledDeviceNames[0]);
}
if (notificationsDisabledDeviceNames.length == 2) {
return this.i18n(
'gmscoreNotificationsTwoDevicesSubtitle',
notificationsDisabledDeviceNames[0],
notificationsDisabledDeviceNames[1]);
}
return this.i18n('gmscoreNotificationsManyDevicesSubtitle');
},
});