Add new certificateProvider extension API.
[chromium-blink-merge.git] / chrome / browser / resources / options / chromeos / internet_detail.js
blobf5328b3fcb500892127f353999ae79647e54049e
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // require: onc_data.js
7 // NOTE(stevenjb): This code is in the process of being converted to be
8 // compatible with the networkingPrivate extension API:
9 // * The network property dictionaries are being converted to use ONC values.
10 // * chrome.send calls will be replaced with chrome.networkingPrivate calls.
11 // See crbug.com/279351 for more info.
13 cr.define('options.internet', function() {
14   var OncData = cr.onc.OncData;
15   var Page = cr.ui.pageManager.Page;
16   var PageManager = cr.ui.pageManager.PageManager;
17   /** @const */ var IPAddressField = options.internet.IPAddressField;
19   /** @const */ var GoogleNameServers = ['8.8.4.4', '8.8.8.8'];
20   /** @const */ var CarrierSprint = 'Sprint';
21   /** @const */ var CarrierVerizon = 'Verizon Wireless';
23   /**
24    * Helper function to set hidden attribute for elements matching a selector.
25    * @param {string} selector CSS selector for extracting a list of elements.
26    * @param {boolean} hidden New hidden value.
27    */
28   function updateHidden(selector, hidden) {
29     var elements = cr.doc.querySelectorAll(selector);
30     for (var i = 0, el; el = elements[i]; i++) {
31       el.hidden = hidden;
32     }
33   }
35   /**
36    * Helper function to update the properties of the data object from the
37    * properties in the update object.
38    * @param {Object} data Object to update.
39    * @param {Object} update Object containing the updated properties.
40    */
41   function updateDataObject(data, update) {
42     for (var prop in update) {
43       if (prop in data)
44         data[prop] = update[prop];
45     }
46   }
48   /**
49    * Monitor pref change of given element.
50    * @param {Element} el Target element.
51    */
52   function observePrefsUI(el) {
53     Preferences.getInstance().addEventListener(el.pref, handlePrefUpdate);
54   }
56   /**
57    * UI pref change handler.
58    * @param {Event} e The update event.
59    */
60   function handlePrefUpdate(e) {
61     DetailsInternetPage.getInstance().updateControls();
62   }
64   /**
65    * Simple helper method for converting a field to a string. It is used to
66    * easily assign an empty string from fields that may be unknown or undefined.
67    * @param {Object} value that should be converted to a string.
68    * @return {string} the result.
69    */
70   function stringFromValue(value) {
71     return value ? String(value) : '';
72   }
74   /**
75    * @param {string} action An action to send to coreOptionsUserMetricsAction.
76    */
77   function sendChromeMetricsAction(action) {
78     chrome.send('coreOptionsUserMetricsAction', [action]);
79   }
81   /**
82    * Send metrics to Chrome when the detailed page is opened.
83    * @param {string} type The ONC type of the network being shown.
84    * @param {string} state The ONC network state.
85    */
86   function sendShowDetailsMetrics(type, state) {
87     if (type == 'WiFi') {
88       sendChromeMetricsAction('Options_NetworkShowDetailsWifi');
89       if (state != 'NotConnected')
90         sendChromeMetricsAction('Options_NetworkShowDetailsWifiConnected');
91     } else if (type == 'Cellular') {
92       sendChromeMetricsAction('Options_NetworkShowDetailsCellular');
93       if (state != 'NotConnected')
94         sendChromeMetricsAction('Options_NetworkShowDetailsCellularConnected');
95     } else if (type == 'VPN') {
96       sendChromeMetricsAction('Options_NetworkShowDetailsVPN');
97       if (state != 'NotConnected')
98         sendChromeMetricsAction('Options_NetworkShowDetailsVPNConnected');
99     }
100   }
102   /**
103    * Returns the netmask as a string for a given prefix length.
104    * @param {number} prefixLength The ONC routing prefix length.
105    * @return {string} The corresponding netmask.
106    */
107   function prefixLengthToNetmask(prefixLength) {
108     // Return the empty string for invalid inputs.
109     if (prefixLength < 0 || prefixLength > 32)
110       return '';
111     var netmask = '';
112     for (var i = 0; i < 4; ++i) {
113       var remainder = 8;
114       if (prefixLength >= 8) {
115         prefixLength -= 8;
116       } else {
117         remainder = prefixLength;
118         prefixLength = 0;
119       }
120       if (i > 0)
121         netmask += '.';
122       var value = 0;
123       if (remainder != 0)
124         value = ((2 << (remainder - 1)) - 1) << (8 - remainder);
125       netmask += value.toString();
126     }
127     return netmask;
128   }
130   /**
131    * Returns the prefix length from the netmask string.
132    * @param {string} netmask The netmask string, e.g. 255.255.255.0.
133    * @return {number} The corresponding netmask or -1 if invalid.
134    */
135   function netmaskToPrefixLength(netmask) {
136     var prefixLength = 0;
137     var tokens = netmask.split('.');
138     if (tokens.length != 4)
139       return -1;
140     for (var i = 0; i < tokens.length; ++i) {
141       var token = tokens[i];
142       // If we already found the last mask and the current one is not
143       // '0' then the netmask is invalid. For example, 255.224.255.0
144       if (prefixLength / 8 != i) {
145         if (token != '0')
146           return -1;
147       } else if (token == '255') {
148         prefixLength += 8;
149       } else if (token == '254') {
150         prefixLength += 7;
151       } else if (token == '252') {
152         prefixLength += 6;
153       } else if (token == '248') {
154         prefixLength += 5;
155       } else if (token == '240') {
156         prefixLength += 4;
157       } else if (token == '224') {
158         prefixLength += 3;
159       } else if (token == '192') {
160         prefixLength += 2;
161       } else if (token == '128') {
162         prefixLength += 1;
163       } else if (token == '0') {
164         prefixLength += 0;
165       } else {
166         // mask is not a valid number.
167         return -1;
168       }
169     }
170     return prefixLength;
171   }
173   // Returns true if we should show the 'View Account' button for |onc|.
174   // TODO(stevenjb): We should query the Mobile Config API for whether or not to
175   // show the 'View Account' button once it is integrated with Settings.
176   function shouldShowViewAccountButton(onc) {
177     var activationState = onc.getActiveValue('Cellular.ActivationState');
178     if (activationState != 'Activating' && activationState != 'Activated')
179       return false;
181     // If no online payment URL was provided by Shill, only show 'View Account'
182     // for Verizon Wireless.
183     if (!onc.getActiveValue('Cellular.PaymentPortal.Url') &&
184         onc.getActiveValue('Cellular.Carrier') != CarrierVerizon) {
185       return false;
186     }
188     // 'View Account' should only be shown for connected networks, or
189     // disconnected LTE networks with a valid MDN.
190     var connectionState = onc.getActiveValue('ConnectionState');
191     if (connectionState != 'Connected') {
192       var technology = onc.getActiveValue('Cellular.NetworkTechnology');
193       if (technology != 'LTE' && technology != 'LTEAdvanced')
194         return false;
195       if (!onc.getActiveValue('Cellular.MDN'))
196         return false;
197     }
199     return true;
200   }
202   /////////////////////////////////////////////////////////////////////////////
203   // DetailsInternetPage class:
205   /**
206    * Encapsulated handling of ChromeOS internet details overlay page.
207    * @constructor
208    * @extends {cr.ui.pageManager.Page}
209    */
210   function DetailsInternetPage() {
211     // If non-negative, indicates a custom entry in select-apn.
212     this.userApnIndex_ = -1;
214     // The custom APN properties associated with entry |userApnIndex_|.
215     this.userApn_ = {};
217     // The currently selected APN entry in $('select-apn') (which may or may not
218     // == userApnIndex_).
219     this.selectedApnIndex_ = -1;
221     // We show the Proxy configuration tab for remembered networks and when
222     // configuring a proxy from the login screen.
223     this.showProxy_ = false;
225     Page.call(this, 'detailsInternetPage', '', 'details-internet-page');
226   }
228   cr.addSingletonGetter(DetailsInternetPage);
230   DetailsInternetPage.prototype = {
231     __proto__: Page.prototype,
233     /** @override */
234     initializePage: function() {
235       Page.prototype.initializePage.call(this);
236       this.initializePageContents_();
238       chrome.networkingPrivate.onNetworksChanged.addListener(
239           this.onNetworksChanged_.bind(this));
241       this.showNetworkDetails_();
242     },
244     /**
245      * Automatically shows the network details dialog if network information
246      * is included in the URL.
247      */
248     showNetworkDetails_: function() {
249       var guid = parseQueryParams(window.location).guid;
250       if (!guid || !guid.length)
251         return;
252       chrome.send('loadVPNProviders');
253       chrome.networkingPrivate.getManagedProperties(
254           guid, DetailsInternetPage.initializeDetailsPage);
255     },
257     /**
258      * networkingPrivate callback when networks change.
259      * @param {Array<string>} changes List of GUIDs whose properties have
260      *     changed.
261      * @private
262      */
263     onNetworksChanged_: function(changes) {
264       if (!this.onc_)
265         return;
266       var guid = this.onc_.guid();
267       if (changes.indexOf(guid) != -1) {
268         chrome.networkingPrivate.getManagedProperties(
269           guid, DetailsInternetPage.updateConnectionData);
270       }
271     },
273     /**
274      * Initializes the contents of the page.
275      */
276     initializePageContents_: function() {
277       $('details-internet-dismiss').addEventListener('click', function(event) {
278         DetailsInternetPage.setDetails();
279       });
281       $('details-internet-login').addEventListener('click', function(event) {
282         DetailsInternetPage.setDetails();
283         DetailsInternetPage.loginFromDetails();
284       });
286       $('details-internet-disconnect').addEventListener('click',
287                                                         function(event) {
288         DetailsInternetPage.setDetails();
289         DetailsInternetPage.disconnectNetwork();
290       });
292       $('details-internet-configure').addEventListener('click',
293                                                        function(event) {
294         DetailsInternetPage.setDetails();
295         DetailsInternetPage.configureNetwork();
296       });
298       $('activate-details').addEventListener('click', function(event) {
299         DetailsInternetPage.activateFromDetails();
300       });
302       $('view-account-details').addEventListener('click', function(event) {
303         chrome.send('showMorePlanInfo',
304                     [DetailsInternetPage.getInstance().onc_.guid()]);
305         PageManager.closeOverlay();
306       });
308       $('cellular-apn-use-default').addEventListener('click', function(event) {
309         DetailsInternetPage.getInstance().setDefaultApn_();
310       });
312       $('cellular-apn-set').addEventListener('click', function(event) {
313         DetailsInternetPage.getInstance().setApn_($('cellular-apn').value);
314       });
316       $('cellular-apn-cancel').addEventListener('click', function(event) {
317         DetailsInternetPage.getInstance().cancelApn_();
318       });
320       $('select-apn').addEventListener('change', function(event) {
321         DetailsInternetPage.getInstance().selectApn_();
322       });
324       $('sim-card-lock-enabled').addEventListener('click', function(event) {
325         var newValue = $('sim-card-lock-enabled').checked;
326         // Leave value as is because user needs to enter PIN code first.
327         // When PIN will be entered and value changed,
328         // we'll update UI to reflect that change.
329         $('sim-card-lock-enabled').checked = !newValue;
330         var operation = newValue ? 'setLocked' : 'setUnlocked';
331         chrome.send('simOperation', [operation]);
332       });
333       $('change-pin').addEventListener('click', function(event) {
334         chrome.send('simOperation', ['changePin']);
335       });
337       // Proxy
338       ['proxy-host-single-port',
339        'secure-proxy-port',
340        'socks-port',
341        'ftp-proxy-port',
342        'proxy-host-port'
343       ].forEach(function(id) {
344         options.PrefPortNumber.decorate($(id));
345       });
347       options.proxyexceptions.ProxyExceptions.decorate($('ignored-host-list'));
348       $('remove-host').addEventListener('click',
349                                         this.handleRemoveProxyExceptions_);
350       $('add-host').addEventListener('click', this.handleAddProxyException_);
351       $('direct-proxy').addEventListener('click', this.disableManualProxy_);
352       $('manual-proxy').addEventListener('click', this.enableManualProxy_);
353       $('auto-proxy').addEventListener('click', this.disableManualProxy_);
354       $('proxy-all-protocols').addEventListener('click',
355                                                 this.toggleSingleProxy_);
356       $('proxy-use-pac-url').addEventListener('change',
357                                               this.handleAutoConfigProxy_);
359       observePrefsUI($('direct-proxy'));
360       observePrefsUI($('manual-proxy'));
361       observePrefsUI($('auto-proxy'));
362       observePrefsUI($('proxy-all-protocols'));
363       observePrefsUI($('proxy-use-pac-url'));
365       $('ip-automatic-configuration-checkbox').addEventListener('click',
366         this.handleIpAutoConfig_);
367       $('automatic-dns-radio').addEventListener('click',
368         this.handleNameServerTypeChange_);
369       $('google-dns-radio').addEventListener('click',
370         this.handleNameServerTypeChange_);
371       $('user-dns-radio').addEventListener('click',
372         this.handleNameServerTypeChange_);
374       // We only load this string if we have the string data available
375       // because the proxy settings page on the login screen re-uses the
376       // proxy sub-page from the internet options, and it doesn't ever
377       // show the DNS settings, so we don't need this string there.
378       // The string isn't available because
379       // chrome://settings-frame/strings.js (where the string is
380       // stored) is not accessible from the login screen.
381       // TODO(pneubeck): Remove this once i18n of the proxy dialog on the login
382       // page is fixed. http://crbug.com/242865
383       if (loadTimeData.data_) {
384         $('google-dns-label').innerHTML =
385             loadTimeData.getString('googleNameServers');
386       }
387     },
389     /**
390      * Handler for "add" event fired from userNameEdit.
391      * @param {Event} e Add event fired from userNameEdit.
392      * @private
393      */
394     handleAddProxyException_: function(e) {
395       var exception = $('new-host').value;
396       $('new-host').value = '';
398       exception = exception.trim();
399       if (exception)
400         $('ignored-host-list').addException(exception);
401     },
403     /**
404      * Handler for when the remove button is clicked
405      * @param {Event} e The click event.
406      * @private
407      */
408     handleRemoveProxyExceptions_: function(e) {
409       var selectedItems = $('ignored-host-list').selectedItems;
410       for (var x = 0; x < selectedItems.length; x++) {
411         $('ignored-host-list').removeException(selectedItems[x]);
412       }
413     },
415     /**
416      * Handler for when the IP automatic configuration checkbox is clicked.
417      * @param {Event} e The click event.
418      * @private
419      */
420     handleIpAutoConfig_: function(e) {
421       var checked = $('ip-automatic-configuration-checkbox').checked;
422       var fields = [$('ip-address'), $('ip-netmask'), $('ip-gateway')];
423       for (var i = 0; i < fields.length; ++i) {
424         fields[i].editable = !checked;
425         if (checked) {
426           var model = fields[i].model;
427           model.value = model.automatic;
428           fields[i].model = model;
429         }
430       }
431       if (!checked)
432         $('ip-address').focus();
433     },
435     /**
436      * Handler for when the name server selection changes.
437      * @param {Event} event The click event.
438      * @private
439      */
440     handleNameServerTypeChange_: function(event) {
441       var type = event.target.value;
442       DetailsInternetPage.updateNameServerDisplay(type);
443     },
445     /**
446      * Gets the IPConfig ONC Object.
447      * @param {string} nameServerType The selected name server type:
448      *   'automatic', 'google', or 'user'.
449      * @return {Object} The IPConfig ONC object.
450      * @private
451      */
452     getIpConfig_: function(nameServerType) {
453       var ipConfig = {};
454       // If 'ip-address' is empty, automatic configuration will be used.
455       if (!$('ip-automatic-configuration-checkbox').checked &&
456           $('ip-address').model.value) {
457         ipConfig['IPAddress'] = $('ip-address').model.value;
458         var netmask = $('ip-netmask').model.value;
459         var routingPrefix = 0;
460         if (netmask) {
461           routingPrefix = netmaskToPrefixLength(netmask);
462           if (routingPrefix == -1) {
463             console.error('Invalid netmask: ' + netmask);
464             routingPrefix = 0;
465           }
466         }
467         ipConfig['RoutingPrefix'] = routingPrefix;
468         ipConfig['Gateway'] = $('ip-gateway').model.value || '';
469       }
471       // Note: If no nameserver fields are set, automatic configuration will be
472       // used. TODO(stevenjb): Validate input fields.
473       if (nameServerType != 'automatic') {
474         var userNameServers = [];
475         if (nameServerType == 'google') {
476           userNameServers = GoogleNameServers.slice();
477         } else if (nameServerType == 'user') {
478           for (var i = 1; i <= 4; ++i) {
479             var nameServerField = $('ipconfig-dns' + i);
480             // Skip empty values.
481             if (nameServerField && nameServerField.model &&
482                 nameServerField.model.value) {
483               userNameServers.push(nameServerField.model.value);
484             }
485           }
486         }
487         if (userNameServers.length)
488           ipConfig['NameServers'] = userNameServers.sort();
489       }
490       return ipConfig;
491     },
493     /**
494      * Creates an indicator event for controlled properties using
495      * the same dictionary format as CoreOptionsHandler::CreateValueForPref.
496      * @param {string} name The name for the Event.
497      * @param {{value: *, controlledBy: *, recommendedValue: *}} propData
498      *     Property dictionary.
499      * @private
500      */
501     createControlledEvent_: function(name, propData) {
502       assert('value' in propData && 'controlledBy' in propData &&
503              'recommendedValue' in propData);
504       var event = new Event(name);
505       event.value = {
506         value: propData.value,
507         controlledBy: propData.controlledBy,
508         recommendedValue: propData.recommendedValue
509       };
510       return event;
511     },
513     /**
514      * Creates an indicator event for controlled properties using
515      * the ONC getManagedProperties dictionary format.
516      * @param {string} name The name for the Event.
517      * @param {Object} propData ONC managed network property dictionary.
518      * @private
519      */
520     createManagedEvent_: function(name, propData) {
521       var event = new Event(name);
522       event.value = {};
524       // Set the current value and recommended value.
525       var activeValue = propData['Active'];
526       var effective = propData['Effective'];
527       if (activeValue == undefined)
528         activeValue = propData[effective];
529       event.value.value = activeValue;
531       // If a property is editable then it is not enforced, and 'controlledBy'
532       // is set to 'recommended' unless effective == {User|Shared}Setting, in
533       // which case the value was modified from the recommended value.
534       // Otherwise if 'Effective' is set to 'UserPolicy' or 'DevicePolicy' then
535       // the set value is mandated by the policy.
536       if (propData['UserEditable']) {
537         if (effective == 'UserPolicy')
538           event.value.controlledBy = 'recommended';
539         event.value.recommendedValue = propData['UserPolicy'];
540       } else if (propData['DeviceEditable']) {
541         if (effective == 'DevicePolicy')
542           event.value.controlledBy = 'recommended';
543         event.value.recommendedValue = propData['DevicePolicy'];
544       } else if (effective == 'UserPolicy' || effective == 'DevicePolicy') {
545         event.value.controlledBy = 'policy';
546       }
548       return event;
549     },
551     /**
552      * Update details page controls.
553      */
554     updateControls: function() {
555       // Note: onc may be undefined when called from a pref update before
556       // initialized in initializeDetailsPage.
557       var onc = this.onc_;
559       // Always show the ipconfig section. TODO(stevenjb): Improve the display
560       // for unconnected networks. Currently the IP address fields may be
561       // blank if the network is not connected.
562       $('ipconfig-section').hidden = false;
563       $('ipconfig-dns-section').hidden = false;
565       // Network type related.
566       updateHidden('#details-internet-page .cellular-details',
567                    this.type_ != 'Cellular');
568       updateHidden('#details-internet-page .wifi-details',
569                    this.type_ != 'WiFi');
570       updateHidden('#details-internet-page .wimax-details',
571                    this.type_ != 'WiMAX');
572       updateHidden('#details-internet-page .vpn-details', this.type_ != 'VPN');
573       updateHidden('#details-internet-page .proxy-details', !this.showProxy_);
575       // Cellular
576       if (onc && this.type_ == 'Cellular') {
577         // Hide gsm/cdma specific elements.
578         if (onc.getActiveValue('Cellular.Family') == 'GSM')
579           updateHidden('#details-internet-page .cdma-only', true);
580         else
581           updateHidden('#details-internet-page .gsm-only', true);
582       }
584       // Wifi
586       // Hide network tab for VPN.
587       updateHidden('#details-internet-page .network-details',
588                    this.type_ == 'VPN');
590       // Password and shared.
591       var source = onc ? onc.getSource() : 'None';
592       var shared = (source == 'Device' || source == 'DevicePolicy');
593       var security = onc ? onc.getWiFiSecurity() : 'None';
594       updateHidden('#details-internet-page #password-details',
595                    this.type_ != 'WiFi' || security == 'None');
596       updateHidden('#details-internet-page #wifi-shared-network', !shared);
597       updateHidden('#details-internet-page #prefer-network', source == 'None');
599       // WiMAX.
600       updateHidden('#details-internet-page #wimax-shared-network', !shared);
602       // Proxy
603       this.updateProxyBannerVisibility_();
604       this.toggleSingleProxy_();
605       if ($('manual-proxy').checked)
606         this.enableManualProxy_();
607       else
608         this.disableManualProxy_();
609     },
611     /**
612      * Updates info banner visibility state. This function shows the banner
613      * if proxy is managed or shared-proxies is off for shared network.
614      * @private
615      */
616     updateProxyBannerVisibility_: function() {
617       var bannerDiv = $('network-proxy-info-banner');
618       if (!loadTimeData.data_) {
619         // TODO(pneubeck): This temporarily prevents an exception below until
620         // i18n of the proxy dialog on the login page is
621         // fixed. http://crbug.com/242865
622         bannerDiv.hidden = true;
623         return;
624       }
626       // Show banner and determine its message if necessary.
627       var controlledBy = $('direct-proxy').controlledBy;
628       if (!controlledBy || controlledBy == '') {
629         bannerDiv.hidden = true;
630       } else {
631         bannerDiv.hidden = false;
632         // The possible banner texts are loaded in proxy_handler.cc.
633         var bannerText = 'proxyBanner' + controlledBy.charAt(0).toUpperCase() +
634                          controlledBy.slice(1);
635         $('banner-text').textContent = loadTimeData.getString(bannerText);
636       }
637     },
639     /**
640      * Handler for when the user clicks on the checkbox to allow a
641      * single proxy usage.
642      * @private
643      */
644     toggleSingleProxy_: function() {
645       if ($('proxy-all-protocols').checked) {
646         $('multi-proxy').hidden = true;
647         $('single-proxy').hidden = false;
648       } else {
649         $('multi-proxy').hidden = false;
650         $('single-proxy').hidden = true;
651       }
652     },
654     /**
655      * Handler for when the user clicks on the checkbox to enter
656      * auto configuration URL.
657      * @private
658      */
659     handleAutoConfigProxy_: function() {
660       $('proxy-pac-url').disabled = !$('proxy-use-pac-url').checked;
661     },
663     /**
664      * Handler for selecting a radio button that will disable the manual
665      * controls.
666      * @private
667      */
668     disableManualProxy_: function() {
669       $('ignored-host-list').disabled = true;
670       $('new-host').disabled = true;
671       $('remove-host').disabled = true;
672       $('add-host').disabled = true;
673       $('proxy-all-protocols').disabled = true;
674       $('proxy-host-name').disabled = true;
675       $('proxy-host-port').disabled = true;
676       $('proxy-host-single-name').disabled = true;
677       $('proxy-host-single-port').disabled = true;
678       $('secure-proxy-host-name').disabled = true;
679       $('secure-proxy-port').disabled = true;
680       $('ftp-proxy').disabled = true;
681       $('ftp-proxy-port').disabled = true;
682       $('socks-host').disabled = true;
683       $('socks-port').disabled = true;
684       $('proxy-use-pac-url').disabled = $('auto-proxy').disabled ||
685                                         !$('auto-proxy').checked;
686       $('proxy-pac-url').disabled = $('proxy-use-pac-url').disabled ||
687                                     !$('proxy-use-pac-url').checked;
688       $('auto-proxy-parms').hidden = !$('auto-proxy').checked;
689       $('manual-proxy-parms').hidden = !$('manual-proxy').checked;
690       sendChromeMetricsAction('Options_NetworkManualProxy_Disable');
691     },
693     /**
694      * Handler for selecting a radio button that will enable the manual
695      * controls.
696      * @private
697      */
698     enableManualProxy_: function() {
699       $('ignored-host-list').redraw();
700       var allDisabled = $('manual-proxy').disabled;
701       $('ignored-host-list').disabled = allDisabled;
702       $('new-host').disabled = allDisabled;
703       $('remove-host').disabled = allDisabled;
704       $('add-host').disabled = allDisabled;
705       $('proxy-all-protocols').disabled = allDisabled;
706       $('proxy-host-name').disabled = allDisabled;
707       $('proxy-host-port').disabled = allDisabled;
708       $('proxy-host-single-name').disabled = allDisabled;
709       $('proxy-host-single-port').disabled = allDisabled;
710       $('secure-proxy-host-name').disabled = allDisabled;
711       $('secure-proxy-port').disabled = allDisabled;
712       $('ftp-proxy').disabled = allDisabled;
713       $('ftp-proxy-port').disabled = allDisabled;
714       $('socks-host').disabled = allDisabled;
715       $('socks-port').disabled = allDisabled;
716       $('proxy-use-pac-url').disabled = true;
717       $('proxy-pac-url').disabled = true;
718       $('auto-proxy-parms').hidden = !$('auto-proxy').checked;
719       $('manual-proxy-parms').hidden = !$('manual-proxy').checked;
720       sendChromeMetricsAction('Options_NetworkManualProxy_Enable');
721     },
723     /**
724      * Helper method called from initializeDetailsPage and updateConnectionData.
725      * Updates visibility/enabled of the login/disconnect/configure buttons.
726      * @private
727      */
728     updateConnectionButtonVisibilty_: function() {
729       var onc = this.onc_;
730       if (this.type_ == 'Ethernet') {
731         // Ethernet can never be connected or disconnected and can always be
732         // configured (e.g. to set security).
733         $('details-internet-login').hidden = true;
734         $('details-internet-disconnect').hidden = true;
735         $('details-internet-configure').hidden = false;
736         return;
737       }
739       var connectable = onc.getActiveValue('Connectable');
740       var connectState = onc.getActiveValue('ConnectionState');
741       if (connectState == 'NotConnected') {
742         $('details-internet-disconnect').hidden = true;
743         $('details-internet-login').hidden = false;
744         // Connecting to an unconfigured network might trigger certificate
745         // installation UI. Until that gets handled here, always enable the
746         // Connect button for built-in networks.
747         var enabled = (this.type_ != 'VPN') ||
748                       (onc.getActiveValue('VPN.Type') != 'ThirdPartyVPN') ||
749                       connectable;
750         $('details-internet-login').disabled = !enabled;
751       } else {
752         $('details-internet-login').hidden = true;
753         $('details-internet-disconnect').hidden = false;
754       }
756       var showConfigure = false;
757       if (this.type_ == 'VPN') {
758         showConfigure = true;
759       } else if (this.type_ == 'WiMAX' && connectState == 'NotConnected') {
760         showConfigure = true;
761       } else if (this.type_ == 'WiFi') {
762         showConfigure = (connectState == 'NotConnected' &&
763                          (!connectable || onc.getWiFiSecurity() != 'None'));
764       }
765       $('details-internet-configure').hidden = !showConfigure;
766     },
768     /**
769      * Helper method called from initializeDetailsPage and updateConnectionData.
770      * Updates the connection state property and account / sim card links.
771      * @private
772      */
773     updateDetails_: function() {
774       var onc = this.onc_;
776       var connectionStateString = onc.getTranslatedValue('ConnectionState');
777       $('connection-state').textContent = connectionStateString;
779       var type = this.type_;
780       var showViewAccount = false;
781       var showActivate = false;
782       if (type == 'WiFi') {
783         $('wifi-connection-state').textContent = connectionStateString;
784       } else if (type == 'WiMAX') {
785         $('wimax-connection-state').textContent = connectionStateString;
786       } else if (type == 'Cellular') {
787         $('activation-state').textContent =
788             onc.getTranslatedValue('Cellular.ActivationState');
789         if (onc.getActiveValue('Cellular.Family') == 'GSM') {
790           var lockEnabled =
791               onc.getActiveValue('Cellular.SIMLockStatus.LockEnabled');
792           $('sim-card-lock-enabled').checked = lockEnabled;
793           $('change-pin').hidden = !lockEnabled;
794         }
795         showViewAccount = shouldShowViewAccountButton(onc);
796         var activationState = onc.getActiveValue('Cellular.ActivationState');
797         showActivate = (activationState == 'NotActivated' ||
798                         activationState == 'PartiallyActivated');
799       }
801       $('view-account-details').hidden = !showViewAccount;
802       $('activate-details').hidden = !showActivate;
803       // If activation is not complete, hide the login button.
804       if (showActivate)
805         $('details-internet-login').hidden = true;
806     },
808     /**
809      * Helper method called from initializeDetailsPage and updateConnectionData.
810      * Updates the fields in the header section of the details frame.
811      * @private
812      */
813     populateHeader_: function() {
814       var onc = this.onc_;
816       $('network-details-title').textContent =
817           this.networkTitle_ || onc.getTranslatedValue('Name');
819       var connectionStateString = onc.getTranslatedValue('ConnectionState');
820       $('network-details-subtitle-status').textContent = connectionStateString;
822       var typeKey;
823       var type = this.type_;
824       if (type == 'Ethernet')
825         typeKey = 'ethernetTitle';
826       else if (type == 'WiFi')
827         typeKey = 'wifiTitle';
828       else if (type == 'WiMAX')
829         typeKey = 'wimaxTitle';
830       else if (type == 'Cellular')
831         typeKey = 'cellularTitle';
832       else if (type == 'VPN')
833         typeKey = 'vpnTitle';
834       else
835         typeKey = null;
836       var typeLabel = $('network-details-subtitle-type');
837       var typeSeparator = $('network-details-subtitle-separator');
838       if (typeKey) {
839         typeLabel.textContent = loadTimeData.getString(typeKey);
840         typeLabel.hidden = false;
841         typeSeparator.hidden = false;
842       } else {
843         typeLabel.hidden = true;
844         typeSeparator.hidden = true;
845       }
846     },
848     /**
849      * Helper method to insert a 'user' option into the Apn list.
850      * @param {Object} userOption The 'user' apn dictionary
851      * @private
852      */
853     insertApnUserOption_: function(userOption) {
854       // Add the 'user' option before the last option ('other')
855       var apnSelector = $('select-apn');
856       assert(apnSelector.length > 0);
857       var otherOption = apnSelector[apnSelector.length - 1];
858       apnSelector.add(userOption, otherOption);
859       this.userApnIndex_ = apnSelector.length - 2;
860       this.selectedApnIndex_ = this.userApnIndex_;
861     },
863     /**
864      * Helper method called from initializeApnList to populate the Apn list.
865      * @param {Array} apnList List of available APNs.
866      * @private
867      */
868     populateApnList_: function(apnList) {
869       var onc = this.onc_;
870       var apnSelector = $('select-apn');
871       assert(apnSelector.length == 1);
872       var otherOption = apnSelector[0];
873       var activeApn = onc.getActiveValue('Cellular.APN.AccessPointName');
874       var lastGoodApn =
875           onc.getActiveValue('Cellular.LastGoodAPN.AccessPointName');
876       for (var i = 0; i < apnList.length; i++) {
877         var apnDict = apnList[i];
878         var localizedName = apnDict['LocalizedName'];
879         var name = localizedName ? localizedName : apnDict['Name'];
880         var accessPointName = apnDict['AccessPointName'];
881         var option = document.createElement('option');
882         option.textContent =
883             name ? (name + ' (' + accessPointName + ')') : accessPointName;
884         option.value = i;
885         // Insert new option before 'other' option.
886         apnSelector.add(option, otherOption);
887         if (this.selectedApnIndex_ != -1)
888           continue;
889         // If this matches the active Apn name, or LastGoodApn name (or there
890         // is no last good APN), set it as the selected Apn.
891         if ((activeApn == accessPointName) ||
892             (!activeApn && (!lastGoodApn || lastGoodApn == accessPointName))) {
893           this.selectedApnIndex_ = i;
894         }
895       }
896       if (this.selectedApnIndex_ == -1 && activeApn) {
897         this.userApn_ = activeApn;
898         // Create a 'user' entry for any active apn not in the list.
899         var userOption = document.createElement('option');
900         userOption.textContent = activeApn;
901         userOption.value = -1;
902         this.insertApnUserOption_(userOption);
903       }
904     },
906     /**
907      * Helper method called from initializeDetailsPage to initialize the Apn
908      * list.
909      * @private
910      */
911     initializeApnList_: function() {
912       this.selectedApnIndex_ = -1;
913       this.userApnIndex_ = -1;
915       var onc = this.onc_;
916       var apnSelector = $('select-apn');
918       // Clear APN lists, keep only last element, 'other'.
919       while (apnSelector.length != 1)
920         apnSelector.remove(0);
922       var apnList = onc.getActiveValue('Cellular.APNList');
923       if (apnList) {
924         // Populate the list with the existing APNs.
925         this.populateApnList_(apnList);
926       } else {
927         // Create a single 'default' entry.
928         var otherOption = apnSelector[0];
929         var defaultOption = document.createElement('option');
930         defaultOption.textContent =
931             loadTimeData.getString('cellularApnUseDefault');
932         defaultOption.value = -1;
933         // Add 'default' entry before 'other' option
934         apnSelector.add(defaultOption, otherOption);
935         assert(apnSelector.length == 2);  // 'default', 'other'
936         this.selectedApnIndex_ = 0;  // Select 'default'
937       }
938       assert(this.selectedApnIndex_ >= 0);
939       apnSelector.selectedIndex = this.selectedApnIndex_;
940       updateHidden('.apn-list-view', false);
941       updateHidden('.apn-details-view', true);
942     },
944     /**
945      * Helper function for setting APN properties.
946      * @param {Object} apnValue Dictionary of APN properties.
947      * @private
948      */
949     setActiveApn_: function(apnValue) {
950       var activeApn = {};
951       var apnName = apnValue['AccessPointName'];
952       if (apnName) {
953         activeApn['AccessPointName'] = apnName;
954         activeApn['Username'] = stringFromValue(apnValue['Username']);
955         activeApn['Password'] = stringFromValue(apnValue['Password']);
956       }
957       // Set the cached ONC data.
958       this.onc_.setProperty('Cellular.APN', activeApn);
959       // Set an ONC object with just the APN values.
960       var oncData = new OncData({});
961       oncData.setProperty('Cellular.APN', activeApn);
962       chrome.networkingPrivate.setProperties(this.onc_.guid(),
963                                              oncData.getData());
964     },
966     /**
967      * Event Listener for the cellular-apn-use-default button.
968      * @private
969      */
970     setDefaultApn_: function() {
971       var apnSelector = $('select-apn');
973       // Remove the 'user' entry if it exists.
974       if (this.userApnIndex_ != -1) {
975         assert(this.userApnIndex_ < apnSelector.length - 1);
976         apnSelector.remove(this.userApnIndex_);
977         this.userApnIndex_ = -1;
978       }
980       var apnList = this.onc_.getActiveValue('Cellular.APNList');
981       var iApn = (apnList != undefined && apnList.length > 0) ? 0 : -1;
982       apnSelector.selectedIndex = iApn;
983       this.selectedApnIndex_ = iApn;
985       // Clear any user APN entry to inform Chrome to use the default APN.
986       this.setActiveApn_({});
988       updateHidden('.apn-list-view', false);
989       updateHidden('.apn-details-view', true);
990     },
992     /**
993      * Event Listener for the cellular-apn-set button.
994      * @private
995      */
996     setApn_: function(apnValue) {
997       if (apnValue == '')
998         return;
1000       var apnSelector = $('select-apn');
1002       var activeApn = {};
1003       activeApn['AccessPointName'] = stringFromValue(apnValue);
1004       activeApn['Username'] = stringFromValue($('cellular-apn-username').value);
1005       activeApn['Password'] = stringFromValue($('cellular-apn-password').value);
1006       this.setActiveApn_(activeApn);
1007       // Set the user selected APN.
1008       this.userApn_ = activeApn;
1010       // Remove any existing 'user' entry.
1011       if (this.userApnIndex_ != -1) {
1012         assert(this.userApnIndex_ < apnSelector.length - 1);
1013         apnSelector.remove(this.userApnIndex_);
1014         this.userApnIndex_ = -1;
1015       }
1017       // Create a new 'user' entry with the new active apn.
1018       var option = document.createElement('option');
1019       option.textContent = activeApn['AccessPointName'];
1020       option.value = -1;
1021       option.selected = true;
1022       this.insertApnUserOption_(option);
1024       updateHidden('.apn-list-view', false);
1025       updateHidden('.apn-details-view', true);
1026     },
1028     /**
1029      * Event Listener for the cellular-apn-cancel button.
1030      * @private
1031      */
1032     cancelApn_: function() { this.initializeApnList_(); },
1034     /**
1035      * Event Listener for the select-apn button.
1036      * @private
1037      */
1038     selectApn_: function() {
1039       var onc = this.onc_;
1040       var apnSelector = $('select-apn');
1041       if (apnSelector[apnSelector.selectedIndex].value != -1) {
1042         var apnList = onc.getActiveValue('Cellular.APNList');
1043         var apnIndex = apnSelector.selectedIndex;
1044         assert(apnIndex < apnList.length);
1045         this.selectedApnIndex_ = apnIndex;
1046         this.setActiveApn_(apnList[apnIndex]);
1047       } else if (apnSelector.selectedIndex == this.userApnIndex_) {
1048         this.selectedApnIndex_ = apnSelector.selectedIndex;
1049         this.setActiveApn_(this.userApn_);
1050       } else { // 'Other'
1051         var apnDict;
1052         if (this.userApn_['AccessPointName']) {
1053           // Fill in the details fields with the existing 'user' config.
1054           apnDict = this.userApn_;
1055         } else {
1056           // No 'user' config, use the current values.
1057           apnDict = {};
1058           apnDict['AccessPointName'] =
1059               onc.getActiveValue('Cellular.APN.AccessPointName');
1060           apnDict['Username'] = onc.getActiveValue('Cellular.APN.Username');
1061           apnDict['Password'] = onc.getActiveValue('Cellular.APN.Password');
1062         }
1063         $('cellular-apn').value = stringFromValue(apnDict['AccessPointName']);
1064         $('cellular-apn-username').value = stringFromValue(apnDict['Username']);
1065         $('cellular-apn-password').value = stringFromValue(apnDict['Password']);
1066         updateHidden('.apn-list-view', true);
1067         updateHidden('.apn-details-view', false);
1068       }
1069     }
1070   };
1072   /**
1073    * Enables or Disables all buttons that provide operations on the cellular
1074    * network.
1075    */
1076   DetailsInternetPage.changeCellularButtonsState = function(disable) {
1077     var buttonsToDisableList =
1078         new Array('details-internet-login',
1079                   'details-internet-disconnect',
1080                   'details-internet-configure',
1081                   'activate-details',
1082                   'view-account-details');
1084     for (var i = 0; i < buttonsToDisableList.length; ++i) {
1085       var button = $(buttonsToDisableList[i]);
1086       button.disabled = disable;
1087     }
1088   };
1090   /**
1091    * If the network is not already activated, starts the activation process or
1092    * shows the activation UI. Otherwise does nothing.
1093    */
1094   DetailsInternetPage.activateCellular = function(guid) {
1095     chrome.networkingPrivate.getProperties(guid, function(properties) {
1096       var oncData = new OncData(properties);
1097       if (oncData.getActiveValue('Cellular.ActivationState') == 'Activated') {
1098         return;
1099       }
1100       var carrier = oncData.getActiveValue('Cellular.Carrier');
1101       if (carrier == CarrierSprint) {
1102         // Sprint is directly ativated, call startActivate().
1103         chrome.networkingPrivate.startActivate(guid, '');
1104       } else {
1105         chrome.send('showMorePlanInfo', [guid]);
1106       }
1107     });
1108   };
1110   /**
1111    * Performs minimal initialization of the InternetDetails dialog in
1112    * preparation for showing proxy-settings.
1113    */
1114   DetailsInternetPage.initializeProxySettings = function() {
1115     DetailsInternetPage.getInstance().initializePageContents_();
1116   };
1118   /**
1119    * Displays the InternetDetails dialog with only the proxy settings visible.
1120    */
1121   DetailsInternetPage.showProxySettings = function() {
1122     var detailsPage = DetailsInternetPage.getInstance();
1123     $('network-details-header').hidden = true;
1124     $('activate-details').hidden = true;
1125     $('view-account-details').hidden = true;
1126     $('web-proxy-auto-discovery').hidden = true;
1127     detailsPage.showProxy_ = true;
1128     updateHidden('#internet-tab', true);
1129     updateHidden('#details-tab-strip', true);
1130     updateHidden('#details-internet-page .action-area', true);
1131     detailsPage.updateControls();
1132     detailsPage.visible = true;
1133     sendChromeMetricsAction('Options_NetworkShowProxyTab');
1134   };
1136   /**
1137    * Initializes even handling for keyboard driven flow.
1138    */
1139   DetailsInternetPage.initializeKeyboardFlow = function() {
1140     keyboard.initializeKeyboardFlow();
1141   };
1143   DetailsInternetPage.updateProxySettings = function(type) {
1144       var proxyHost = null,
1145           proxyPort = null;
1147       if (type == 'cros.session.proxy.singlehttp') {
1148         proxyHost = 'proxy-host-single-name';
1149         proxyPort = 'proxy-host-single-port';
1150       } else if (type == 'cros.session.proxy.httpurl') {
1151         proxyHost = 'proxy-host-name';
1152         proxyPort = 'proxy-host-port';
1153       } else if (type == 'cros.session.proxy.httpsurl') {
1154         proxyHost = 'secure-proxy-host-name';
1155         proxyPort = 'secure-proxy-port';
1156       } else if (type == 'cros.session.proxy.ftpurl') {
1157         proxyHost = 'ftp-proxy';
1158         proxyPort = 'ftp-proxy-port';
1159       } else if (type == 'cros.session.proxy.socks') {
1160         proxyHost = 'socks-host';
1161         proxyPort = 'socks-port';
1162       } else {
1163         return;
1164       }
1166       var hostValue = $(proxyHost).value;
1167       if (hostValue.indexOf(':') !== -1) {
1168         if (hostValue.match(/:/g).length == 1) {
1169           hostValue = hostValue.split(':');
1170           $(proxyHost).value = hostValue[0];
1171           $(proxyPort).value = hostValue[1];
1172         }
1173       }
1174   };
1176   DetailsInternetPage.loginFromDetails = function() {
1177     DetailsInternetPage.configureOrConnect();
1178     PageManager.closeOverlay();
1179   };
1181   /**
1182    * This function identifies unconfigured networks and networks that are
1183    * likely to fail (e.g. due to a bad passphrase on a previous connect
1184    * attempt). For such networks a configure dialog will be opened. Otherwise
1185    * a connection will be attempted.
1186    */
1187   DetailsInternetPage.configureOrConnect = function() {
1188     var detailsPage = DetailsInternetPage.getInstance();
1189     if (detailsPage.type_ == 'WiFi')
1190       sendChromeMetricsAction('Options_NetworkConnectToWifi');
1191     else if (detailsPage.type_ == 'VPN')
1192       sendChromeMetricsAction('Options_NetworkConnectToVPN');
1194     var onc = detailsPage.onc_;
1195     var guid = onc.guid();
1196     var type = onc.getActiveValue('Type');
1198     // Built-in VPNs do not correctly set 'Connectable', so we always show the
1199     // configuration UI.
1200     if (type == 'VPN') {
1201       if (onc.getActiveValue('VPN.Type') != 'ThirdPartyVPN') {
1202         chrome.send('configureNetwork', [guid]);
1203         return;
1204       }
1205     }
1207     // If 'Connectable' is false for WiFi or WiMAX, Shill requires
1208     // additional configuration to connect, so show the configuration UI.
1209     if ((type == 'WiFi' || type == 'WiMAX') &&
1210         !onc.getActiveValue('Connectable')) {
1211       chrome.send('configureNetwork', [guid]);
1212       return;
1213     }
1215     // Secure WiFi networks with ErrorState set most likely require
1216     // configuration (e.g. a correct passphrase) before connecting.
1217     if (type == 'WiFi' && onc.getWiFiSecurity() != 'None') {
1218       var errorState = onc.getActiveValue('ErrorState');
1219       if (errorState && errorState != 'Unknown') {
1220         chrome.send('configureNetwork', [guid]);
1221         return;
1222       }
1223     }
1225     // Cellular networks need to be activated before they can be connected to.
1226     if (type == 'Cellular') {
1227       var activationState = onc.getActiveValue('Cellular.ActivationState');
1228       if (activationState != 'Activated' && activationState != 'Unknown') {
1229         DetailsInternetPage.activateCellular(guid);
1230         return;
1231       }
1232     }
1234     chrome.networkingPrivate.startConnect(guid);
1235   };
1237   DetailsInternetPage.disconnectNetwork = function() {
1238     var detailsPage = DetailsInternetPage.getInstance();
1239     if (detailsPage.type_ == 'WiFi')
1240       sendChromeMetricsAction('Options_NetworkDisconnectWifi');
1241     else if (detailsPage.type_ == 'VPN')
1242       sendChromeMetricsAction('Options_NetworkDisconnectVPN');
1243     chrome.networkingPrivate.startDisconnect(detailsPage.onc_.guid());
1244     PageManager.closeOverlay();
1245   };
1247   DetailsInternetPage.configureNetwork = function() {
1248     var detailsPage = DetailsInternetPage.getInstance();
1249     // This is an explicit request to show the configure dialog; do not show
1250     // the enrollment dialog for networks missing a certificate.
1251     var forceShow = true;
1252     chrome.send('configureNetwork', [detailsPage.onc_.guid(), forceShow]);
1253     PageManager.closeOverlay();
1254   };
1256   DetailsInternetPage.activateFromDetails = function() {
1257     var detailsPage = DetailsInternetPage.getInstance();
1258     if (detailsPage.type_ == 'Cellular')
1259       DetailsInternetPage.activateCellular(detailsPage.onc_.guid());
1260     PageManager.closeOverlay();
1261   };
1263   /**
1264    * Event handler called when the details page is closed. Sends changed
1265    * properties to Chrome and closes the overlay.
1266    */
1267   DetailsInternetPage.setDetails = function() {
1268     var detailsPage = DetailsInternetPage.getInstance();
1269     var type = detailsPage.type_;
1270     var oncData = new OncData({});
1271     var autoConnectCheckboxId = '';
1272     if (type == 'WiFi') {
1273       var preferredCheckbox =
1274           assertInstanceof($('prefer-network-wifi'), HTMLInputElement);
1275       if (!preferredCheckbox.hidden && !preferredCheckbox.disabled) {
1276         var kPreferredPriority = 1;
1277         var priority = preferredCheckbox.checked ? kPreferredPriority : 0;
1278         oncData.setProperty('Priority', priority);
1279         sendChromeMetricsAction('Options_NetworkSetPrefer');
1280       }
1281       autoConnectCheckboxId = 'auto-connect-network-wifi';
1282     } else if (type == 'WiMAX') {
1283       autoConnectCheckboxId = 'auto-connect-network-wimax';
1284     } else if (type == 'Cellular') {
1285       autoConnectCheckboxId = 'auto-connect-network-cellular';
1286     } else if (type == 'VPN') {
1287       var providerType = detailsPage.onc_.getActiveValue('VPN.Type');
1288       if (providerType != 'ThirdPartyVPN') {
1289         oncData.setProperty('VPN.Type', providerType);
1290         oncData.setProperty('VPN.Host', $('inet-server-hostname').value);
1291         autoConnectCheckboxId = 'auto-connect-network-vpn';
1292       }
1293     }
1294     if (autoConnectCheckboxId != '') {
1295       var autoConnectCheckbox =
1296           assertInstanceof($(autoConnectCheckboxId), HTMLInputElement);
1297       if (!autoConnectCheckbox.hidden && !autoConnectCheckbox.disabled) {
1298         var autoConnectKey = type + '.AutoConnect';
1299         oncData.setProperty(autoConnectKey, !!autoConnectCheckbox.checked);
1300         sendChromeMetricsAction('Options_NetworkAutoConnect');
1301       }
1302     }
1304     var nameServerTypes = ['automatic', 'google', 'user'];
1305     var nameServerType = 'automatic';
1306     for (var i = 0; i < nameServerTypes.length; ++i) {
1307       if ($(nameServerTypes[i] + '-dns-radio').checked) {
1308         nameServerType = nameServerTypes[i];
1309         break;
1310       }
1311     }
1312     var ipConfig = detailsPage.getIpConfig_(nameServerType);
1313     var ipAddressType = ('IPAddress' in ipConfig) ? 'Static' : 'DHCP';
1314     var nameServersType = ('NameServers' in ipConfig) ? 'Static' : 'DHCP';
1315     oncData.setProperty('IPAddressConfigType', ipAddressType);
1316     oncData.setProperty('NameServersConfigType', nameServersType);
1317     oncData.setProperty('StaticIPConfig', ipConfig);
1319     var data = oncData.getData();
1320     if (Object.keys(data).length > 0) {
1321       // TODO(stevenjb): Only set changed properties.
1322       chrome.networkingPrivate.setProperties(detailsPage.onc_.guid(), data);
1323     }
1325     PageManager.closeOverlay();
1326   };
1328   /**
1329    * Event handler called when the name server type changes.
1330    * @param {string} type The selected name sever type, 'automatic', 'google',
1331    *                      or 'user'.
1332    */
1333   DetailsInternetPage.updateNameServerDisplay = function(type) {
1334     var editable = type == 'user';
1335     var fields = [$('ipconfig-dns1'), $('ipconfig-dns2'),
1336                   $('ipconfig-dns3'), $('ipconfig-dns4')];
1337     for (var i = 0; i < fields.length; ++i) {
1338       fields[i].editable = editable;
1339     }
1340     if (editable)
1341       $('ipconfig-dns1').focus();
1343     var automaticDns = $('automatic-dns-display');
1344     var googleDns = $('google-dns-display');
1345     var userDns = $('user-dns-settings');
1346     switch (type) {
1347       case 'automatic':
1348         automaticDns.setAttribute('selected', '');
1349         googleDns.removeAttribute('selected');
1350         userDns.removeAttribute('selected');
1351         break;
1352       case 'google':
1353         automaticDns.removeAttribute('selected');
1354         googleDns.setAttribute('selected', '');
1355         userDns.removeAttribute('selected');
1356         break;
1357       case 'user':
1358         automaticDns.removeAttribute('selected');
1359         googleDns.removeAttribute('selected');
1360         userDns.setAttribute('selected', '');
1361         break;
1362     }
1363   };
1365   /**
1366    * Method called from Chrome when the ONC properties for the displayed
1367    * network may have changed.
1368    * @param {Object} oncData The updated ONC dictionary for the network.
1369    */
1370   DetailsInternetPage.updateConnectionData = function(oncData) {
1371     var detailsPage = DetailsInternetPage.getInstance();
1372     if (!detailsPage.visible)
1373       return;
1375     if (oncData.GUID != detailsPage.onc_.guid())
1376       return;
1378     // Update our cached data object.
1379     detailsPage.onc_ = new OncData(oncData);
1381     detailsPage.populateHeader_();
1382     detailsPage.updateConnectionButtonVisibilty_();
1383     detailsPage.updateDetails_();
1384   };
1386   /**
1387    * Initializes the details page with the provided ONC data.
1388    * @param {Object} oncData Dictionary of ONC properties.
1389    */
1390   DetailsInternetPage.initializeDetailsPage = function(oncData) {
1391     var onc = new OncData(oncData);
1393     var detailsPage = DetailsInternetPage.getInstance();
1394     detailsPage.onc_ = onc;
1395     var type = onc.getActiveValue('Type');
1396     detailsPage.type_ = type;
1398     sendShowDetailsMetrics(type, onc.getActiveValue('ConnectionState'));
1400     if (type == 'VPN') {
1401       // Cache the dialog title, which will contain the provider name in the
1402       // case of a third-party VPN provider. This caching is important as the
1403       // provider may go away while the details dialog is being shown, causing
1404       // subsequent updates to be unable to determine the correct title.
1405       detailsPage.networkTitle_ = options.VPNProviders.formatNetworkName(onc);
1406     } else {
1407       delete detailsPage.networkTitle_;
1408     }
1410     detailsPage.populateHeader_();
1411     detailsPage.updateConnectionButtonVisibilty_();
1412     detailsPage.updateDetails_();
1414     // TODO(stevenjb): Some of the setup below should be moved to
1415     // updateDetails_() so that updates are reflected in the UI.
1417     // Only show proxy for remembered networks.
1418     var remembered = onc.getSource() != 'None';
1419     if (remembered) {
1420       detailsPage.showProxy_ = true;
1421       // Inform Chrome which network to use for proxy configuration.
1422       chrome.send('selectNetwork', [detailsPage.onc_.guid()]);
1423     } else {
1424       detailsPage.showProxy_ = false;
1425     }
1427     $('web-proxy-auto-discovery').hidden = true;
1429     var restricted = onc.getActiveValue('RestrictedConnectivity');
1430     var restrictedString = loadTimeData.getString(
1431         restricted ? 'restrictedYes' : 'restrictedNo');
1433     // These objects contain an 'automatic' property that is displayed when
1434     // ip-automatic-configuration-checkbox is checked, and a 'value' property
1435     // that is displayed when unchecked and used to set the associated ONC
1436     // property for StaticIPConfig on commit.
1437     var inetAddress = {};
1438     var inetNetmask = {};
1439     var inetGateway = {};
1441     var inetNameServersString;
1443     var ipconfigList = onc.getActiveValue('IPConfigs');
1444     if (Array.isArray(ipconfigList)) {
1445       for (var i = 0; i < ipconfigList.length; ++i) {
1446         var ipconfig = ipconfigList[i];
1447         var ipType = ipconfig['Type'];
1448         if (ipType != 'IPv4') {
1449           // TODO(stevenjb): Handle IPv6 properties.
1450           continue;
1451         }
1452         var address = ipconfig['IPAddress'];
1453         inetAddress.automatic = address;
1454         inetAddress.value = address;
1455         var netmask = prefixLengthToNetmask(ipconfig['RoutingPrefix']);
1456         inetNetmask.automatic = netmask;
1457         inetNetmask.value = netmask;
1458         var gateway = ipconfig['Gateway'];
1459         inetGateway.automatic = gateway;
1460         inetGateway.value = gateway;
1461         if ('WebProxyAutoDiscoveryUrl' in ipconfig) {
1462           $('web-proxy-auto-discovery').hidden = false;
1463           $('web-proxy-auto-discovery-url').value =
1464               ipconfig['WebProxyAutoDiscoveryUrl'];
1465         }
1466         if ('NameServers' in ipconfig) {
1467           var inetNameServers = ipconfig['NameServers'];
1468           inetNameServers = inetNameServers.sort();
1469           inetNameServersString = inetNameServers.join(',');
1470         }
1471         break;  // Use the first IPv4 entry.
1472       }
1473     }
1475     // Override the 'automatic' properties with the saved DHCP values if the
1476     // saved value is set, and set any unset 'value' properties.
1477     var savedNameServersString;
1478     var savedIpAddress = onc.getActiveValue('SavedIPConfig.IPAddress');
1479     if (savedIpAddress != undefined) {
1480       inetAddress.automatic = savedIpAddress;
1481       if (!inetAddress.value)
1482         inetAddress.value = savedIpAddress;
1483     }
1484     var savedPrefix = onc.getActiveValue('SavedIPConfig.RoutingPrefix');
1485     if (savedPrefix != undefined) {
1486       assert(typeof savedPrefix == 'number');
1487       var savedNetmask = prefixLengthToNetmask(
1488           /** @type {number} */(savedPrefix));
1489       inetNetmask.automatic = savedNetmask;
1490       if (!inetNetmask.value)
1491         inetNetmask.value = savedNetmask;
1492     }
1493     var savedGateway = onc.getActiveValue('SavedIPConfig.Gateway');
1494     if (savedGateway != undefined) {
1495       inetGateway.automatic = savedGateway;
1496       if (!inetGateway.value)
1497         inetGateway.value = savedGateway;
1498     }
1500     var savedNameServers = onc.getActiveValue('SavedIPConfig.NameServers');
1501     if (savedNameServers) {
1502       savedNameServers = savedNameServers.sort();
1503       savedNameServersString = savedNameServers.join(',');
1504     }
1506     var ipAutoConfig = 'automatic';
1507     if (onc.getActiveValue('IPAddressConfigType') == 'Static') {
1508       ipAutoConfig = 'user';
1509       var staticIpAddress = onc.getActiveValue('StaticIPConfig.IPAddress');
1510       inetAddress.user = staticIpAddress;
1511       inetAddress.value = staticIpAddress;
1513       var staticPrefix = onc.getActiveValue('StaticIPConfig.RoutingPrefix');
1514       if (typeof staticPrefix != 'number')
1515         staticPrefix = 0;
1516       var staticNetmask = prefixLengthToNetmask(
1517           /** @type {number} */ (staticPrefix));
1518       inetNetmask.user = staticNetmask;
1519       inetNetmask.value = staticNetmask;
1521       var staticGateway = onc.getActiveValue('StaticIPConfig.Gateway');
1522       inetGateway.user = staticGateway;
1523       inetGateway.value = staticGateway;
1524     }
1526     var staticNameServersString;
1527     if (onc.getActiveValue('NameServersConfigType') == 'Static') {
1528       var staticNameServers = onc.getActiveValue('StaticIPConfig.NameServers');
1529       staticNameServers = staticNameServers.sort();
1530       staticNameServersString = staticNameServers.join(',');
1531     }
1533     $('ip-automatic-configuration-checkbox').checked =
1534         ipAutoConfig == 'automatic';
1536     inetAddress.autoConfig = ipAutoConfig;
1537     inetNetmask.autoConfig = ipAutoConfig;
1538     inetGateway.autoConfig = ipAutoConfig;
1540     var configureAddressField = function(field, model) {
1541       IPAddressField.decorate(field);
1542       field.model = model;
1543       field.editable = model.autoConfig == 'user';
1544     };
1545     configureAddressField($('ip-address'), inetAddress);
1546     configureAddressField($('ip-netmask'), inetNetmask);
1547     configureAddressField($('ip-gateway'), inetGateway);
1549     // Set Nameserver fields. Nameservers are 'automatic' by default. If a
1550     // static namerserver is set, use that unless it does not match a non
1551     // empty 'NameServers' value (indicating that the custom nameservers are
1552     // invalid or not being applied for some reason). TODO(stevenjb): Only
1553     // set these properites if they change so that invalid custom values do
1554     // not get lost.
1555     var nameServerType = 'automatic';
1556     if (staticNameServersString &&
1557         (!inetNameServersString ||
1558          staticNameServersString == inetNameServersString)) {
1559       if (staticNameServersString == GoogleNameServers.join(','))
1560         nameServerType = 'google';
1561       else
1562         nameServerType = 'user';
1563     }
1564     if (nameServerType == 'automatic')
1565       $('automatic-dns-display').textContent = inetNameServersString;
1566     else
1567       $('automatic-dns-display').textContent = savedNameServersString;
1568     $('google-dns-display').textContent = GoogleNameServers.join(',');
1570     var nameServersUser = [];
1571     if (staticNameServers) {
1572       nameServersUser = staticNameServers;
1573     } else if (savedNameServers) {
1574       // Pre-populate with values provided by DHCP server.
1575       nameServersUser = savedNameServers;
1576     }
1578     var nameServerModels = [];
1579     for (var i = 0; i < 4; ++i)
1580       nameServerModels.push({value: nameServersUser[i] || ''});
1582     $(nameServerType + '-dns-radio').checked = true;
1583     configureAddressField($('ipconfig-dns1'), nameServerModels[0]);
1584     configureAddressField($('ipconfig-dns2'), nameServerModels[1]);
1585     configureAddressField($('ipconfig-dns3'), nameServerModels[2]);
1586     configureAddressField($('ipconfig-dns4'), nameServerModels[3]);
1588     DetailsInternetPage.updateNameServerDisplay(nameServerType);
1590     var macAddress = onc.getActiveValue('MacAddress');
1591     if (macAddress) {
1592       $('hardware-address').textContent = macAddress;
1593       $('hardware-address-row').style.display = 'table-row';
1594     } else {
1595       // This is most likely a device without a hardware address.
1596       $('hardware-address-row').style.display = 'none';
1597     }
1599     var setOrHideParent = function(field, property) {
1600       if (property != undefined) {
1601         $(field).textContent = property;
1602         $(field).parentElement.hidden = false;
1603       } else {
1604         $(field).parentElement.hidden = true;
1605       }
1606     };
1608     var networkName = onc.getTranslatedValue('Name');
1610     // Signal strength as percentage (for WiFi and WiMAX).
1611     var signalStrength;
1612     if (type == 'WiFi' || type == 'WiMAX')
1613       signalStrength = onc.getActiveValue(type + '.SignalStrength');
1614     if (!signalStrength)
1615       signalStrength = 0;
1616     var strengthFormat = loadTimeData.getString('inetSignalStrengthFormat');
1617     var strengthString = strengthFormat.replace('$1', signalStrength);
1619     if (type == 'WiFi') {
1620       OptionsPage.showTab($('wifi-network-nav-tab'));
1621       $('wifi-restricted-connectivity').textContent = restrictedString;
1622       var ssid = onc.getActiveValue('WiFi.SSID');
1623       $('wifi-ssid').textContent = ssid ? ssid : networkName;
1624       setOrHideParent('wifi-bssid', onc.getActiveValue('WiFi.BSSID'));
1625       var security = onc.getWiFiSecurity();
1626       if (security == 'None')
1627         security = undefined;
1628       setOrHideParent('wifi-security', security);
1629       // Frequency is in MHz.
1630       var frequency = onc.getActiveValue('WiFi.Frequency');
1631       if (!frequency)
1632         frequency = 0;
1633       var frequencyFormat = loadTimeData.getString('inetFrequencyFormat');
1634       frequencyFormat = frequencyFormat.replace('$1', frequency);
1635       $('wifi-frequency').textContent = frequencyFormat;
1636       $('wifi-signal-strength').textContent = strengthString;
1637       setOrHideParent('wifi-hardware-address',
1638                       onc.getActiveValue('MacAddress'));
1639       var priority = onc.getActiveValue('Priority');
1640       $('prefer-network-wifi').checked = priority > 0;
1641       $('prefer-network-wifi').disabled = !remembered;
1642       $('auto-connect-network-wifi').checked =
1643           onc.getActiveValue('WiFi.AutoConnect');
1644       $('auto-connect-network-wifi').disabled = !remembered;
1645     } else if (type == 'WiMAX') {
1646       OptionsPage.showTab($('wimax-network-nav-tab'));
1647       $('wimax-restricted-connectivity').textContent = restrictedString;
1649       $('auto-connect-network-wimax').checked =
1650           onc.getActiveValue('WiMAX.AutoConnect');
1651       $('auto-connect-network-wimax').disabled = !remembered;
1652       var identity = onc.getActiveValue('WiMAX.EAP.Identity');
1653       setOrHideParent('wimax-eap-identity', identity);
1654       $('wimax-signal-strength').textContent = strengthString;
1655     } else if (type == 'Cellular') {
1656       OptionsPage.showTab($('cellular-conn-nav-tab'));
1658       var isGsm = onc.getActiveValue('Cellular.Family') == 'GSM';
1660       $('service-name').textContent = networkName;
1662       // TODO(stevenjb): Ideally many of these should be localized.
1663       $('network-technology').textContent =
1664           onc.getActiveValue('Cellular.NetworkTechnology');
1665       $('roaming-state').textContent =
1666           onc.getTranslatedValue('Cellular.RoamingState');
1667       $('cellular-restricted-connectivity').textContent = restrictedString;
1668       $('error-state').textContent = onc.getActiveValue('ErrorState');
1669       $('manufacturer').textContent =
1670           onc.getActiveValue('Cellular.Manufacturer');
1671       $('model-id').textContent = onc.getActiveValue('Cellular.ModelID');
1672       $('firmware-revision').textContent =
1673           onc.getActiveValue('Cellular.FirmwareRevision');
1674       $('hardware-revision').textContent =
1675           onc.getActiveValue('Cellular.HardwareRevision');
1676       $('mdn').textContent = onc.getActiveValue('Cellular.MDN');
1678       // Show ServingOperator properties only if available.
1679       var servingOperatorName =
1680           onc.getActiveValue('Cellular.ServingOperator.Name');
1681       var servingOperatorCode =
1682           onc.getActiveValue('Cellular.ServingOperator.Code');
1683       if (servingOperatorName != undefined &&
1684           servingOperatorCode != undefined) {
1685         $('operator-name').textContent = servingOperatorName;
1686         $('operator-code').textContent = servingOperatorCode;
1687       } else {
1688         $('operator-name').parentElement.hidden = true;
1689         $('operator-code').parentElement.hidden = true;
1690       }
1691       // Make sure that GSM/CDMA specific properties that shouldn't be hidden
1692       // are visible.
1693       updateHidden('#details-internet-page .gsm-only', false);
1694       updateHidden('#details-internet-page .cdma-only', false);
1696       // Show IMEI/ESN/MEID/MIN/PRL only if they are available.
1697       setOrHideParent('esn', onc.getActiveValue('Cellular.ESN'));
1698       setOrHideParent('imei', onc.getActiveValue('Cellular.IMEI'));
1699       setOrHideParent('meid', onc.getActiveValue('Cellular.MEID'));
1700       setOrHideParent('min', onc.getActiveValue('Cellular.MIN'));
1701       setOrHideParent('prl-version', onc.getActiveValue('Cellular.PRLVersion'));
1703       if (isGsm) {
1704         $('iccid').textContent = onc.getActiveValue('Cellular.ICCID');
1705         $('imsi').textContent = onc.getActiveValue('Cellular.IMSI');
1706         detailsPage.initializeApnList_();
1707       }
1708       $('auto-connect-network-cellular').checked =
1709           onc.getActiveValue('Cellular.AutoConnect');
1710       $('auto-connect-network-cellular').disabled = false;
1711     } else if (type == 'VPN') {
1712       OptionsPage.showTab($('vpn-nav-tab'));
1713       var providerType = onc.getActiveValue('VPN.Type');
1714       var isThirdPartyVPN = providerType == 'ThirdPartyVPN';
1715       $('vpn-tab').classList.toggle('third-party-vpn-provider',
1716                                     isThirdPartyVPN);
1718       $('inet-service-name').textContent = networkName;
1719       $('inet-provider-type').textContent =
1720           onc.getTranslatedValue('VPN.Type');
1722       if (isThirdPartyVPN) {
1723         $('inet-provider-name').textContent = '';
1724         var extensionID = onc.getActiveValue('VPN.ThirdPartyVPN.ExtensionID');
1725         var providers = options.VPNProviders.getProviders();
1726         for (var i = 0; i < providers.length; ++i) {
1727           if (extensionID == providers[i].extensionID) {
1728             $('inet-provider-name').textContent = providers[i].name;
1729             break;
1730           }
1731         }
1732       } else {
1733         var usernameKey;
1734         if (providerType == 'OpenVPN')
1735           usernameKey = 'VPN.OpenVPN.Username';
1736         else if (providerType == 'L2TP-IPsec')
1737           usernameKey = 'VPN.L2TP.Username';
1739         if (usernameKey) {
1740           $('inet-username').parentElement.hidden = false;
1741           $('inet-username').textContent = onc.getActiveValue(usernameKey);
1742         } else {
1743           $('inet-username').parentElement.hidden = true;
1744         }
1745         var inetServerHostname = $('inet-server-hostname');
1746         inetServerHostname.value = onc.getActiveValue('VPN.Host');
1747         inetServerHostname.resetHandler = function() {
1748           PageManager.hideBubble();
1749           var recommended = onc.getRecommendedValue('VPN.Host');
1750           if (recommended != undefined)
1751             inetServerHostname.value = recommended;
1752         };
1753         $('auto-connect-network-vpn').checked =
1754             onc.getActiveValue('VPN.AutoConnect');
1755         $('auto-connect-network-vpn').disabled = false;
1756       }
1757     } else {
1758       OptionsPage.showTab($('internet-nav-tab'));
1759     }
1761     // Update controlled option indicators.
1762     var indicators = cr.doc.querySelectorAll(
1763         '#details-internet-page .controlled-setting-indicator');
1764     for (var i = 0; i < indicators.length; i++) {
1765       var managed = indicators[i].hasAttribute('managed');
1766       // TODO(stevenjb): Eliminate support for 'data' once 39 is stable.
1767       var attributeName = managed ? 'managed' : 'data';
1768       var propName = indicators[i].getAttribute(attributeName);
1769       if (!propName)
1770         continue;
1771       var propValue = managed ?
1772           onc.getManagedProperty(propName) :
1773           onc.getActiveValue(propName);
1774       // If the property is unset or unmanaged (i.e. not an Object) skip it.
1775       if (propValue == undefined || (typeof propValue != 'object'))
1776         continue;
1777       var event;
1778       if (managed)
1779         event = detailsPage.createManagedEvent_(propName, propValue);
1780       else
1781         event = detailsPage.createControlledEvent_(propName,
1782             /** @type {{value: *, controlledBy: *, recommendedValue: *}} */(
1783                 propValue));
1784       indicators[i].handlePrefChange(event);
1785       var forElement = $(indicators[i].getAttribute('internet-detail-for'));
1786       if (forElement) {
1787         if (event.value.controlledBy == 'policy')
1788           forElement.disabled = true;
1789         if (forElement.resetHandler)
1790           indicators[i].resetHandler = forElement.resetHandler;
1791       }
1792     }
1794     detailsPage.updateControls();
1796     // Don't show page name in address bar and in history to prevent people
1797     // navigate here by hand and solve issue with page session restore.
1798     PageManager.showPageByName('detailsInternetPage', false);
1799   };
1801   return {
1802     DetailsInternetPage: DetailsInternetPage
1803   };