| Index: chrome/browser/resources/policy.js
|
| diff --git a/chrome/browser/resources/policy.js b/chrome/browser/resources/policy.js
|
| index 064a6c5fc774f39a5d6f218ad742fdb4aac46cc4..2c52c616df0a8bdd2f65ce6209243f31ebc0ec81 100644
|
| --- a/chrome/browser/resources/policy.js
|
| +++ b/chrome/browser/resources/policy.js
|
| @@ -1,277 +1,422 @@
|
| -// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| +// Copyright (c) 2013 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.
|
|
|
| -/**
|
| - * This variable structure is here to document the structure that the template
|
| - * expects to correctly populate the page.
|
| - */
|
| -var policyDataFormat = {
|
| - // Whether any of the policies in 'policies' have a value.
|
| - 'anyPoliciesSet': true,
|
| -
|
| - 'policies': [
|
| - {
|
| - 'level': 'managed',
|
| - 'name': 'AllowXYZ',
|
| - 'set': true,
|
| - 'scope': 'Machine',
|
| - 'status': 'ok',
|
| - 'value': true
|
| - }
|
| - ],
|
| - 'status': {
|
| - 'deviceFetchInterval': '8min',
|
| - 'deviceId': 'D2AC39A2-3C8FC-E2C0-E45D2DC3782C',
|
| - 'deviceLastFetchTime': '9:50 PM',
|
| - 'devicePolicyDomain': 'google.com',
|
| - 'deviceStatusMessage': 'OK',
|
| - 'displayDeviceStatus': true,
|
| - 'displayStatusSection': true,
|
| - 'displayUserStatus': true,
|
| - 'user': 'simo@google.com',
|
| - 'userFetchInterval': '8min',
|
| - 'userId': 'D2AC39A2-3C8FC-E2C0-E45D2DC3782C',
|
| - 'userLastFetchTime': '9:50 PM',
|
| - 'userStatusMessage': 'OK'
|
| - }
|
| -};
|
| -
|
| -cr.define('policies', function() {
|
| +cr.define('policy', function() {
|
| + /**
|
| + * A box that shows the status of cloud policy for a device or user.
|
| + * @constructor
|
| + * @extends {HTMLFieldSetElement}
|
| + */
|
| + var StatusBox = cr.ui.define(function() {
|
| + var node = $('status-box-template').cloneNode(true);
|
| + node.removeAttribute('id');
|
| + return node;
|
| + });
|
|
|
| - function Policy() {
|
| - }
|
| + StatusBox.prototype = {
|
| + // Set up the prototype chain.
|
| + __proto__: HTMLFieldSetElement.prototype,
|
|
|
| - cr.addSingletonGetter(Policy);
|
| + /**
|
| + * Initialization function for the cr.ui framework.
|
| + */
|
| + decorate: function() {
|
| + },
|
|
|
| - Policy.prototype = {
|
| /**
|
| - * True if none of the received policies are actually set, false otherwise.
|
| - * @type {boolean}
|
| + * Populate the box with the given cloud policy status.
|
| + * @param {string} scope The policy scope, either "device" or "user".
|
| + * @param {Object} status Dictionary with information about the status.
|
| */
|
| - noActivePolicies_: false,
|
| + initialize: function(scope, status) {
|
| + if (scope == 'device') {
|
| + // For device policy, set the appropriate title and populate the topmost
|
| + // status item with the domain the device is enrolled into.
|
| + this.querySelector('.legend').textContent =
|
| + loadTimeData.getString('statusDevice');
|
| + var domain = this.querySelector('.domain');
|
| + domain.textContent = status.domain;
|
| + domain.parentElement.hidden = false;
|
| + } else {
|
| + // For user policy, set the appropriate title and populate the topmost
|
| + // status item with the username that policies apply to.
|
| + this.querySelector('.legend').textContent =
|
| + loadTimeData.getString('statusUser');
|
| + // Populate the topmost item with the username.
|
| + var username = this.querySelector('.username');
|
| + username.textContent = status.username;
|
| + username.parentElement.hidden = false;
|
| + }
|
| + // Populate all remaining items.
|
| + this.querySelector('.client-id').textContent = status.clientId || '';
|
| + this.querySelector('.time-since-last-refresh').textContent =
|
| + status.timeSinceLastRefresh || '';
|
| + this.querySelector('.refresh-interval').textContent =
|
| + status.refreshInterval || '';
|
| + this.querySelector('.status').textContent = status.status || '';
|
| + },
|
| + };
|
| +
|
| + /**
|
| + * A single policy's entry in the policy table.
|
| + * @constructor
|
| + * @extends {HTMLTableSectionElement}
|
| + */
|
| + var Policy = cr.ui.define(function() {
|
| + var node = $('policy-template').cloneNode(true);
|
| + node.removeAttribute('id');
|
| + return node;
|
| + });
|
| +
|
| + Policy.prototype = {
|
| + // Set up the prototype chain.
|
| + __proto__: HTMLTableSectionElement.prototype,
|
|
|
| /**
|
| - * The current search term for filtering of the policy table.
|
| - * @type {string}
|
| - * @private
|
| + * Initialization function for the cr.ui framework.
|
| */
|
| - searchTerm_: '',
|
| + decorate: function() {
|
| + this.updateToggleExpandedValueText_();
|
| + this.querySelector('.toggle-expanded-value').addEventListener(
|
| + 'click', this.toggleExpandedValue_.bind(this));
|
| + },
|
|
|
| /**
|
| - * Takes the |policyData| argument and populates the page with this data. It
|
| - * expects an object structure like the policyDataFormat above.
|
| - * @param {Object} policyData Detailed info about policies.
|
| + * Populate the table columns with information about the policy name, value
|
| + * and status.
|
| + * @param {string} name The policy name.
|
| + * @param {Object} value Dictionary with information about the policy value.
|
| + * @param {boolean} unknown Whether the policy name is not recognized.
|
| */
|
| - renderTemplate: function(policyData) {
|
| - this.noActivePolicies_ = !policyData.anyPoliciesSet;
|
| -
|
| - if (this.noActivePolicies_)
|
| - $('no-policies').hidden = false;
|
| - if (policyData.status.displayStatusSection)
|
| - $('status-section').hidden = false;
|
| -
|
| - // This is the javascript code that processes the template:
|
| - var input = new JsEvalContext(policyData);
|
| - var output = $('data-template');
|
| - jstProcess(input, output);
|
| -
|
| - var toggles = document.querySelectorAll('.policy-set * .toggler');
|
| - for (var i = 0; i < toggles.length; i++) {
|
| - toggles[i].hidden = true;
|
| - toggles[i].onclick = function() {
|
| - Policy.getInstance().toggleCellExpand_(this);
|
| - };
|
| + initialize: function(name, value, unknown) {
|
| + this.name = name;
|
| + this.unset = !value;
|
| +
|
| + // Populate the name column.
|
| + this.querySelector('.name').textContent = name;
|
| +
|
| + // Populate the remaining columns with policy scope, level and value if a
|
| + // value has been set. Otherwise, leave them blank.
|
| + if (value) {
|
| + this.querySelector('.scope').textContent =
|
| + loadTimeData.getString(value.scope == 'user' ?
|
| + 'scopeUser' : 'scopeDevice');
|
| + this.querySelector('.level').textContent =
|
| + loadTimeData.getString(value.level == 'recommended' ?
|
| + 'levelRecommended' : 'levelMandatory');
|
| + this.querySelector('.value').textContent = value.value;
|
| + this.querySelector('.expanded-value').textContent = value.value;
|
| }
|
|
|
| - var containers = document.querySelectorAll('.text-container');
|
| - for (var i = 0; i < containers.length; i++)
|
| - this.initTextContainer_(containers[i]);
|
| + // Populate the status column.
|
| + var status;
|
| + if (!value) {
|
| + // If the policy value has not been set, show an error message.
|
| + status = loadTimeData.getString('unset');
|
| + } else if (unknown) {
|
| + // If the policy name is not recognized, show an error message.
|
| + status = loadTimeData.getString('unknown');
|
| + } else if (value.error) {
|
| + // If an error occurred while parsing the policy value, show the error
|
| + // message.
|
| + status = value.error;
|
| + } else {
|
| + // Otherwise, indicate that the policy value was parsed correctly.
|
| + status = loadTimeData.getString('ok');
|
| + }
|
| + this.querySelector('.status').textContent = status;
|
| },
|
|
|
| /**
|
| - * Filters the table of policies by name.
|
| - * @param {string} term The search string.
|
| + * Check the table columns for overflow. Most columns are automatically
|
| + * elided when overflow occurs. The only action required is to add a tooltip
|
| + * that shows the complete content. The value column is an exception. If
|
| + * overflow occurs here, the contents is replaced with a link that toggles
|
| + * the visibility of an additional row containing the complete value.
|
| */
|
| - filterTable: function(term) {
|
| - this.searchTerm_ = term.toLowerCase();
|
| - var table = $('policy-table');
|
| - var showUnsent = $('toggle-unsent-policies').checked;
|
| - for (var r = 1; r < table.rows.length; r++) {
|
| - var row = table.rows[r];
|
| -
|
| - // Don't change visibility of policies that aren't set if the checkbox
|
| - // isn't checked.
|
| - if (!showUnsent && row.className == 'policy-unset')
|
| - continue;
|
| -
|
| - var nameCell = row.querySelector('.policy-name');
|
| - var cellContents = nameCell.textContent;
|
| - row.hidden =
|
| - !(cellContents.toLowerCase().indexOf(this.searchTerm_) >= 0);
|
| + checkOverflow: function() {
|
| + // Set a tooltip on all overflowed columns except the value column.
|
| + var divs = this.querySelectorAll('div.elide');
|
| + for (var i = 0; i < divs.length; i++) {
|
| + var div = divs[i];
|
| + div.title = div.offsetWidth < div.scrollWidth ? div.textContent : '';
|
| }
|
| +
|
| + // Cache the width of the value column's contents when it is first shown.
|
| + // This is required to be able to check whether the contents would still
|
| + // overflow the column once it has been hidden and replaced by a link.
|
| + var valueContainer = this.querySelector('.value-container');
|
| + if (valueContainer.valueWidth == undefined) {
|
| + valueContainer.valueWidth =
|
| + valueContainer.querySelector('.value').offsetWidth;
|
| + }
|
| +
|
| + // Determine whether the contents of the value column overflows. The
|
| + // visibility of the contents, replacement link and additional row
|
| + // containing the complete value that depend on this are handled by CSS.
|
| + this.classList.toggle(
|
| + 'has-overflowed-value',
|
| + valueContainer.offsetWidth < valueContainer.valueWidth);
|
| },
|
|
|
| /**
|
| - * Updates the visibility of the policies depending on the state of the
|
| - * 'toggle-unsent-policies' checkbox.
|
| + * Update the text of the link that toggles the visibility of an additional
|
| + * row containing the complete policy value, depending on the toggle state.
|
| + * @private
|
| */
|
| - updatePolicyVisibility: function() {
|
| - if ($('toggle-unsent-policies').checked)
|
| - $('policies').style.display = '';
|
| - else if (this.noActivePolicies_)
|
| - $('policies').style.display = 'none';
|
| -
|
| - var tableRows = document.getElementsByClassName('policy-unset');
|
| - for (var i = 0; i < tableRows.length; i++)
|
| - tableRows[i].hidden = !($('toggle-unsent-policies').checked);
|
| -
|
| - // Filter table again in case a search was active.
|
| - this.filterTable(this.searchTerm_);
|
| + updateToggleExpandedValueText_: function(event) {
|
| + this.querySelector('.toggle-expanded-value').textContent =
|
| + loadTimeData.getString(
|
| + this.classList.contains('show-overflowed-value') ?
|
| + 'hideExpandedValue' : 'showExpandedValue');
|
| },
|
|
|
| /**
|
| - * Expands or collapses a table cell that has overflowing text.
|
| - * @param {Object} toggler The toggler that was clicked on.
|
| + * Toggle the visibility of an additional row containing the complete policy
|
| + * value.
|
| * @private
|
| */
|
| - toggleCellExpand_: function(toggler) {
|
| - var textContainer = toggler.parentElement;
|
| - textContainer.collapsed = !textContainer.collapsed;
|
| -
|
| - if (textContainer.collapsed)
|
| - this.collapseCell_(textContainer);
|
| - else
|
| - this.expandCell_(textContainer);
|
| + toggleExpandedValue_: function() {
|
| + this.classList.toggle('show-overflowed-value');
|
| + this.updateToggleExpandedValueText_();
|
| },
|
| + };
|
| +
|
| + /**
|
| + * A table of policies and their values.
|
| + * @constructor
|
| + * @extends {HTMLTableSectionElement}
|
| + */
|
| + var PolicyTable = cr.ui.define('tbody');
|
| +
|
| + PolicyTable.prototype = {
|
| + // Set up the prototype chain.
|
| + __proto__: HTMLTableSectionElement.prototype,
|
|
|
| /**
|
| - * Collapses all expanded table cells and updates the visibility of the
|
| - * toggles accordingly. Should be called before the policy information in
|
| - * the table is updated.
|
| + * Initialization function for the cr.ui framework.
|
| */
|
| - collapseExpandedCells: function() {
|
| - var textContainers = document.querySelectorAll('.text-expanded');
|
| - for (var i = 0; i < textContainers.length; i++)
|
| - this.collapseCell_(textContainers[i]);
|
| + decorate: function() {
|
| + this.policies_ = {};
|
| + this.filterPattern_ = '';
|
| + window.addEventListener('resize', this.checkOverflow_.bind(this));
|
| },
|
|
|
| /**
|
| - * Expands a table cell so that all the text it contains is visible.
|
| - * @param {Object} textContainer The cell's div element that contains the
|
| - * text.
|
| - * @private
|
| + * Initialize the list of all known policies.
|
| + * @param {Object} names Dictionary containing all known policy names.
|
| */
|
| - expandCell_: function(textContainer) {
|
| - textContainer.classList.remove('text-collapsed');
|
| - textContainer.classList.add('text-expanded');
|
| - textContainer.querySelector('.expand').hidden = true;
|
| - textContainer.querySelector('.collapse').hidden = false;
|
| + setPolicyNames: function(names) {
|
| + this.policies_ = names;
|
| + this.setPolicyValues({});
|
| },
|
|
|
| /**
|
| - * Collapses a table cell so that overflowing text is hidden.
|
| - * @param {Object} textContainer The cell's div element that contains the
|
| - * text.
|
| - * @private
|
| + * Populate the table with the currently set policy values and any errors
|
| + * detected while parsing these.
|
| + * @param {Object} values Dictionary containing the current policy values.
|
| + */
|
| + setPolicyValues: function(values) {
|
| + // Remove all policies from the table.
|
| + var policies = this.getElementsByTagName('tbody');
|
| + while (policies.length > 0)
|
| + this.removeChild(policies.item(0));
|
| +
|
| + // First, add known policies whose value is currently set.
|
| + var unset = [];
|
| + for (var name in this.policies_) {
|
| + if (name in values)
|
| + this.setPolicyValue_(name, values[name], false);
|
| + else
|
| + unset.push(name);
|
| + }
|
| +
|
| + // Second, add policies whose value is currently set but whose name is not
|
| + // recognized.
|
| + for (var name in values) {
|
| + if (!(name in this.policies_))
|
| + this.setPolicyValue_(name, values[name], true);
|
| + }
|
| +
|
| + // Finally, add known policies whose value is not currently set.
|
| + for (var i = 0; i < unset.length; i++)
|
| + this.setPolicyValue_(unset[i], undefined, false);
|
| +
|
| + // Filter the policies.
|
| + this.filter();
|
| + },
|
| +
|
| + /**
|
| + * Set the filter pattern. Only policies whose name contains |pattern| are
|
| + * shown in the policy table. The filter is case insensitive. It can be
|
| + * disabled by setting |pattern| to an empty string.
|
| + * @param {string} pattern The filter pattern.
|
| + */
|
| + setFilterPattern: function(pattern) {
|
| + this.filterPattern_ = pattern.toLowerCase();
|
| + this.filter();
|
| + },
|
| +
|
| + /**
|
| + * Filter policies. Only policies whose name contains the filter pattern are
|
| + * shown in the table. Furthermore, policies whose value is not currently
|
| + * set are only shown if the corresponding checkbox is checked.
|
| */
|
| - collapseCell_: function(textContainer) {
|
| - textContainer.classList.remove('text-expanded');
|
| - textContainer.classList.add('text-collapsed');
|
| - textContainer.querySelector('.expand').hidden = false;
|
| - textContainer.querySelector('.collapse').hidden = true;
|
| + filter: function() {
|
| + var showUnset = $('show-unset').checked;
|
| + var policies = this.getElementsByTagName('tbody');
|
| + for (var i = 0; i < policies.length; i++) {
|
| + var policy = policies[i];
|
| + policy.hidden =
|
| + policy.unset && !showUnset ||
|
| + policy.name.toLowerCase().indexOf(this.filterPattern_) == -1;
|
| + }
|
| + this.parentElement.classList.toggle(
|
| + 'empty', !this.querySelector('tbody:not([hidden])'));
|
| + setTimeout(this.checkOverflow_.bind(this), 0);
|
| },
|
|
|
| /**
|
| - * Initializes a text container, showing the expand toggle if necessary.
|
| - * @param {Object} textContainer The text container element.
|
| + * Check the table columns for overflow.
|
| + * @private
|
| */
|
| - initTextContainer_: function(textContainer) {
|
| - textContainer.collapsed = true;
|
| - var textValue = textContainer.querySelector('.text-value');
|
| -
|
| - // If the text is wider than the text container, the expand toggler should
|
| - // appear.
|
| - if (textContainer.offsetWidth < textValue.offsetWidth ||
|
| - textContainer.offsetHeight < textValue.offsetHeight) {
|
| - this.collapseCell_(textContainer);
|
| + checkOverflow_: function() {
|
| + var policies = this.getElementsByTagName('tbody');
|
| + for (var i = 0; i < policies.length; i++) {
|
| + if (!policies[i].hidden)
|
| + policies[i].checkOverflow();
|
| }
|
| - }
|
| + },
|
| +
|
| + /**
|
| + * Add a policy with the given |name| and |value| to the table.
|
| + * @param {string} name The policy name.
|
| + * @param {Object} value Dictionary with information about the policy value.
|
| + * @param {boolean} unknown Whether the policy name is not recoginzed.
|
| + * @private
|
| + */
|
| + setPolicyValue_: function(name, value, unknown) {
|
| + var policy = new Policy;
|
| + policy.initialize(name, value, unknown);
|
| + this.appendChild(policy);
|
| + },
|
| };
|
|
|
| /**
|
| - * Asks the C++ PolicyUIHandler to get details about policies and status
|
| - * information. The PolicyUIHandler should reply to returnData() (below).
|
| + * A singelton object that handles communication between browser and WebUI.
|
| + * @constructor
|
| */
|
| - Policy.requestData = function() {
|
| - chrome.send('requestData');
|
| - };
|
| + function Page() {
|
| + }
|
| +
|
| + // Make Page a singleton.
|
| + cr.addSingletonGetter(Page);
|
|
|
| /**
|
| - * Called by the C++ PolicyUIHandler when it has the requested data.
|
| - * @param {Object} policyData The policy information in the format described
|
| - * by the policyDataFormat.
|
| + * Provide a list of all known policies to the UI. Called by the browser on
|
| + * page load.
|
| + * @param {Object} names Dictionary containing all known policy names.
|
| */
|
| - Policy.returnData = function(policyData) {
|
| - var policy = Policy.getInstance();
|
| - policy.collapseExpandedCells();
|
| - policy.renderTemplate(policyData);
|
| - policy.updatePolicyVisibility();
|
| + Page.setPolicyNames = function(names) {
|
| + this.getInstance().policyTable.setPolicyNames(names);
|
| };
|
|
|
| /**
|
| - * Called by the C++ PolicyUIHandler when a requested policy refresh has
|
| - * completed.
|
| + * Provide a list of the currently set policy values and any errors detected
|
| + * while parsing these to the UI. Called by the browser on page load and
|
| + * whenever policy values change.
|
| + * @param {Object} values Dictionary containing the current policy values.
|
| */
|
| - Policy.refreshDone = function() {
|
| - $('fetch-policies-button').disabled = false;
|
| + Page.setPolicyValues = function(values) {
|
| + this.getInstance().policyTable.setPolicyValues(values);
|
| };
|
|
|
| /**
|
| - * Asks the C++ PolicyUIHandler to re-fetch policy information.
|
| + * Provide the current cloud policy status to the UI. Called by the browser on
|
| + * page load if cloud policy is present and whenever the status changes.
|
| + * @param {Object} status Dictionary containing the current policy status.
|
| */
|
| - Policy.triggerPolicyFetch = function() {
|
| - chrome.send('fetchPolicy');
|
| + Page.setStatus = function(status) {
|
| + this.getInstance().setStatus(status);
|
| };
|
|
|
| /**
|
| - * Determines whether a policy should be visible or not.
|
| - * @param {Object} policy An entry in the 'policies' array given by the above
|
| - * PolicyDataFormat.
|
| + * Notify the UI that a request to reload policy values has completed. Called
|
| + * by the browser after a request to reload policy has been sent by the UI.
|
| */
|
| - Policy.shouldDisplayPolicy = function(policy) {
|
| - return $('toggle-unsent-policies').checked || policy.set;
|
| + Page.reloadPoliciesDone = function() {
|
| + this.getInstance().reloadPoliciesDone();
|
| };
|
|
|
| - /**
|
| - * Initializes the page and loads the list of policies and the policy
|
| - * status data.
|
| - */
|
| - Policy.initialize = function() {
|
| - Policy.requestData();
|
| -
|
| - // Set HTML event handlers.
|
| - $('fetch-policies-button').onclick = function(event) {
|
| - this.disabled = true;
|
| - Policy.triggerPolicyFetch();
|
| - };
|
| -
|
| - $('toggle-unsent-policies').onchange = function(event) {
|
| - Policy.getInstance().updatePolicyVisibility();
|
| - };
|
| -
|
| - $('search-field').onsearch = function(event) {
|
| - Policy.getInstance().filterTable(this.value);
|
| - };
|
| + Page.prototype = {
|
| + /**
|
| + * Main initialization function. Called by the browser on page load.
|
| + */
|
| + initialize: function() {
|
| + uber.onContentFrameLoaded();
|
| + this.policyTable = $('policy-table');
|
| + cr.ui.decorate(this.policyTable, PolicyTable);
|
| +
|
| + // Place the initial focus on the filter input field.
|
| + $('filter').focus();
|
| +
|
| + var self = this;
|
| + $('filter').onsearch = function(event) {
|
| + self.policyTable.setFilterPattern(this.value);
|
| + };
|
| + $('reload-policies').onclick = function(event) {
|
| + this.disabled = true;
|
| + chrome.send('reloadPolicies');
|
| + };
|
| + $('show-unset').onchange = this.policyTable.filter.bind(this.policyTable);
|
| +
|
| + // Notify the browser that the page has loaded, causing it to send the
|
| + // list of all known policies, the current policy values and the cloud
|
| + // policy status.
|
| + chrome.send('initialized');
|
| + },
|
| +
|
| + /**
|
| + * Update the status section of the page to show the current cloud policy
|
| + * status.
|
| + * @param {Object} status Dictionary containing the current policy status.
|
| + */
|
| + setStatus: function(status) {
|
| + // Remove any existing status boxes.
|
| + var container = $('status-box-container');
|
| + while (container.firstChild)
|
| + container.removeChild(container.firstChild);
|
| + // Hide the status section.
|
| + var section = $('status');
|
| + section.hidden = true;
|
| +
|
| + // Add a status box for each scope that has a cloud policy status.
|
| + for (var scope in status) {
|
| + var box = new StatusBox;
|
| + box.initialize(scope, status[scope]);
|
| + container.appendChild(box);
|
| + // Show the status section.
|
| + section.hidden = false;
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Re-enable the reload policies button when the previous request to reload
|
| + * policies values has completed.
|
| + */
|
| + reloadPoliciesDone: function() {
|
| + $('reload-policies').disabled = false;
|
| + },
|
| };
|
|
|
| - // Export
|
| return {
|
| - Policy: Policy
|
| + Page: Page
|
| };
|
| });
|
|
|
| -var Policy = policies.Policy;
|
| -
|
| -// Get data and have it displayed upon loading.
|
| -document.addEventListener('DOMContentLoaded', policies.Policy.initialize);
|
| +// Have the main initialization function be called when the page finishes
|
| +// loading.
|
| +document.addEventListener(
|
| + 'DOMContentLoaded',
|
| + policy.Page.getInstance().initialize.bind(policy.Page.getInstance()));
|
|
|