diff options
author | anitawoodruff@chromium.org <anitawoodruff@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-20 18:24:14 +0000 |
---|---|---|
committer | anitawoodruff@chromium.org <anitawoodruff@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-20 18:24:14 +0000 |
commit | 5ef5e68e9a1d7ef50c3efc0b8f6fffa50be0b02b (patch) | |
tree | 78f7631b17db22d44cfb61cff81af88644313a0f | |
parent | 16a274f342161d245e7e37f9932726dfab951fd8 (diff) | |
download | chromium_src-5ef5e68e9a1d7ef50c3efc0b8f6fffa50be0b02b.zip chromium_src-5ef5e68e9a1d7ef50c3efc0b8f6fffa50be0b02b.tar.gz chromium_src-5ef5e68e9a1d7ef50c3efc0b8f6fffa50be0b02b.tar.bz2 |
Adding extension policies to chrome://policy page
Currently, only Chrome policies are displayed on the chrome://policy page.
This fix also displays any configured policies which exist for individual
extensions, listed by extension id.
BUG=248533
Review URL: https://chromiumcodereview.appspot.com/16689004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@207487 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/resources/policy.css | 46 | ||||
-rw-r--r-- | chrome/browser/resources/policy.html | 36 | ||||
-rw-r--r-- | chrome/browser/resources/policy.js | 171 | ||||
-rw-r--r-- | chrome/browser/ui/webui/policy_ui.cc | 105 | ||||
-rw-r--r-- | chrome/browser/ui/webui/policy_ui_browsertest.cc | 2 |
5 files changed, 287 insertions, 73 deletions
diff --git a/chrome/browser/resources/policy.css b/chrome/browser/resources/policy.css index 990d55f..cdf701d 100644 --- a/chrome/browser/resources/policy.css +++ b/chrome/browser/resources/policy.css @@ -56,24 +56,52 @@ div.label { } #show-unset-container { + float: right; text-align: right; } html[dir='rtl'] #show-unset-container { + float: left; text-align: left; } +div.reload-policies-button { + float: left; +} + +html[dir='rtl'] div.reload-policies-button { + float: right; +} + +div.show-unset-checkbox { + float: right; +} + +html[dir='rtl'] div.show-unset-checkbox { + float: left; +} + +section.reload-show-unset-section { + padding-bottom: 30px; + padding-top: 15px; +} + +section.status-box-section { + clear: both; +} + +div.table-description { + color: rgb(100, 100, 100); +} + div.no-policies-set { color: rgb(180, 180, 180); font-size: 125%; - margin-top: 70px; + margin-bottom: 10px; + margin-top: 20px; text-align: center; } -section:not(.empty) > div.no-policies-set { - display: none; -} - table { border-collapse: collapse; margin-bottom: 5px; @@ -86,6 +114,14 @@ section.empty > table { display: none; } +section:not(.empty) > div.no-policies-set { + display: none; +} + +section.policy-table-section { + padding-bottom: 10px; +} + th, td { border: 1px solid rgb(217, 217, 217); diff --git a/chrome/browser/resources/policy.html b/chrome/browser/resources/policy.html index bba2afc..fff1829 100644 --- a/chrome/browser/resources/policy.html +++ b/chrome/browser/resources/policy.html @@ -29,33 +29,23 @@ <header> <h1 i18n-content="title"></h1> </header> - <section> - <button id="reload-policies" i18n-content="reloadPolicies"></button> - </section> - <section id="status" hidden> - <h3 i18n-content="status"></h3> - <div id="status-box-container"></span> - </section> - <section class="empty"> - <h3 i18n-content="title"></h3> - <div id="show-unset-container"> + <section class="reload-show-unset-section"> + <div class="reload-policies-button"> + <button id="reload-policies" i18n-content="reloadPolicies"></button> + </div> + <div id="show-unset-container" class="show-unset-checkbox"> <label> <input id="show-unset" type="checkbox"></input> <span i18n-content="showUnset"></span> </label> </div> - <div class="no-policies-set" i18n-content="noPoliciesSet"></div> - <table id="policy-table"> - <thead> - <tr> - <th i18n-content="headerScope"></th> - <th i18n-content="headerLevel"></th> - <th i18n-content="headerName"></th> - <th i18n-content="headerValue"></th> - <th i18n-content="headerStatus"></th> - </tr> - </thead> - </table> + </section> + <section id="status-section" class="status-box-section" hidden> + <h3 i18n-content="status"></h3> + <div id="status-box-container"></div> + </section> + <section id="main-section" class="empty"> + <!-- This is where policy tables get dynamically added. --> </section> </div> <div hidden> @@ -104,7 +94,7 @@ <button class="toggle-expanded-value link-button"></button> </div> </td> - <td> + <td class="status-container"> <div class="status elide"></div> </td> </tr> diff --git a/chrome/browser/resources/policy.js b/chrome/browser/resources/policy.js index 2c52c61..a044bc9 100644 --- a/chrome/browser/resources/policy.js +++ b/chrome/browser/resources/policy.js @@ -88,8 +88,9 @@ cr.define('policy', function() { * @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. + * @param {boolean} includeStatus Whether the table has a status column. */ - initialize: function(name, value, unknown) { + initialize: function(name, value, unknown, includeStatus) { this.name = name; this.unset = !value; @@ -109,23 +110,29 @@ cr.define('policy', function() { this.querySelector('.expanded-value').textContent = value.value; } - // 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; + if (includeStatus) { + // 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; } else { - // Otherwise, indicate that the policy value was parsed correctly. - status = loadTimeData.getString('ok'); + // Remove status column. + this.querySelector('.status-container').remove(); + this.querySelector('.expanded-value').setAttribute('colspan', 4); } - this.querySelector('.status').textContent = status; }, /** @@ -298,7 +305,8 @@ cr.define('policy', function() { */ setPolicyValue_: function(name, value, unknown) { var policy = new Policy; - policy.initialize(name, value, unknown); + var includeStatus = this.querySelector('.status-column') != null; + policy.initialize(name, value, unknown, includeStatus); this.appendChild(policy); }, }; @@ -319,7 +327,8 @@ cr.define('policy', function() { * @param {Object} names Dictionary containing all known policy names. */ Page.setPolicyNames = function(names) { - this.getInstance().policyTable.setPolicyNames(names); + var table = this.getInstance().policyTables['chrome']; + table.setPolicyNames(names); }; /** @@ -329,7 +338,21 @@ cr.define('policy', function() { * @param {Object} values Dictionary containing the current policy values. */ Page.setPolicyValues = function(values) { - this.getInstance().policyTable.setPolicyValues(values); + var page = this.getInstance(); + + if (values.hasOwnProperty('chromePolicies')) { + var table = page.policyTables['chrome']; + table.setPolicyValues(values.chromePolicies); + } + + if (values.hasOwnProperty('extensionPolicies')) { + for (var extensionId in values.extensionPolicies) { + var tableName = values.extensionPolicies[extensionId].name; + var table = page.getOrCreateTable('extension-' + extensionId, tableName, + 'ID: ' + extensionId, false); + table.setPolicyValues(values.extensionPolicies[extensionId].policies); + } + } }; /** @@ -355,21 +378,32 @@ cr.define('policy', function() { */ initialize: function() { uber.onContentFrameLoaded(); - this.policyTable = $('policy-table'); - cr.ui.decorate(this.policyTable, PolicyTable); + + this.mainSection = $('main-section'); + this.policyTables = {}; + + var chromeTable = this.getOrCreateTable('chrome', 'Chrome policies', '', + true); // Place the initial focus on the filter input field. $('filter').focus(); var self = this; $('filter').onsearch = function(event) { - self.policyTable.setFilterPattern(this.value); + for (policyTable in self.policyTables) { + self.policyTables[policyTable].setFilterPattern(this.value); + } }; $('reload-policies').onclick = function(event) { this.disabled = true; chrome.send('reloadPolicies'); }; - $('show-unset').onchange = this.policyTable.filter.bind(this.policyTable); + + $('show-unset').onchange = function() { + for (policyTable in self.policyTables) { + self.policyTables[policyTable].filter(); + } + }; // 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 @@ -378,6 +412,95 @@ cr.define('policy', function() { }, /** + * Gets the existing policy table for the given id, or if none exists, + * creates a new policy table section, adds the section to the page, + * and returns the new table from that section. + * @param {string} id The key for the table in policyTables. + * @param {string} label_title Title for this policy table. + * @param {string} label_content Description for the policy table. + * @return {Element} Policy table associated with the given id. + */ + getOrCreateTable: function(id, label_title, label_content, includeStatus) { + if (!this.policyTables.hasOwnProperty(id)) { + var newSection = this.createPolicyTableSection(id, label_title, + label_content, includeStatus); + this.mainSection.appendChild(newSection); + } + return this.policyTables[id]; + }, + + /** + * Creates a new section containing a title, description and table of + * policies. + * @param {string} id Used as key when storing new table in policyTables. + * @param {string} label_title Title for this policy table. + * @param {string} label_content Description for the policy table. + * @param {boolean} includeStatus Whether to display a status column. + * @return {Element} The newly created section. + */ + createPolicyTableSection: function(id, label_title, label_content, + includeStatus) { + var section = document.createElement('section'); + section.setAttribute('class', 'policy-table-section'); + + // Add title and description. + var title = window.document.createElement('h3'); + title.textContent = label_title; + section.appendChild(title); + + if (label_content) { + var description = window.document.createElement('div'); + description.classList.add('table-description'); + description.textContent = label_content; + section.appendChild(description); + } + + // Add 'No Policies Set' element. + var noPolicies = window.document.createElement('div'); + noPolicies.classList.add('no-policies-set'); + noPolicies.textContent = loadTimeData.getString('noPoliciesSet'); + section.appendChild(noPolicies); + + // Add table of policies. + var newTable = this.createPolicyTable(includeStatus); + this.policyTables[id] = newTable; + section.appendChild(newTable); + + return section; + }, + + /** + * Creates a new table for displaying policies. + * @param {boolean} includeStatus Whether to include a status column. + * @return {Element} The newly created table. + */ + createPolicyTable: function(includeStatus) { + var newTable = window.document.createElement('table'); + var tableHead = window.document.createElement('thead'); + var tableRow = window.document.createElement('tr'); + var tableHeadings = ['headerScope', 'headerLevel', + 'headerName', 'headerValue']; + + for (var i = 0; i < tableHeadings.length; i++) { + var tableHeader = window.document.createElement('th'); + tableHeader.textContent = loadTimeData.getString(tableHeadings[i]); + tableRow.appendChild(tableHeader); + } + + if (includeStatus) { + var statusHeader = window.document.createElement('th'); + statusHeader.classList.add('status-column'); + statusHeader.textContent = loadTimeData.getString('headerStatus'); + tableRow.appendChild(statusHeader); + } + + tableHead.appendChild(tableRow); + newTable.appendChild(tableHead); + cr.ui.decorate(newTable, PolicyTable); + return newTable; + }, + + /** * Update the status section of the page to show the current cloud policy * status. * @param {Object} status Dictionary containing the current policy status. @@ -388,7 +511,7 @@ cr.define('policy', function() { while (container.firstChild) container.removeChild(container.firstChild); // Hide the status section. - var section = $('status'); + var section = $('status-section'); section.hidden = true; // Add a status box for each scope that has a cloud policy status. diff --git a/chrome/browser/ui/webui/policy_ui.cc b/chrome/browser/ui/webui/policy_ui.cc index 2ee0e85..f6358e4 100644 --- a/chrome/browser/ui/webui/policy_ui.cc +++ b/chrome/browser/ui/webui/policy_ui.cc @@ -15,6 +15,8 @@ #include "base/time.h" #include "base/values.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/policy/browser_policy_connector.h" #include "chrome/browser/policy/cloud/cloud_policy_client.h" #include "chrome/browser/policy/cloud/cloud_policy_constants.h" @@ -32,6 +34,9 @@ #include "chrome/browser/policy/profile_policy_connector_factory.h" #include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_set.h" +#include "chrome/common/extensions/manifest.h" #include "chrome/common/time_format.h" #include "chrome/common/url_constants.h" #include "content/public/browser/web_ui.h" @@ -286,6 +291,14 @@ class PolicyUIHandler : public content::WebUIMessageHandler, // information is sent. void SendStatus() const; + // Inserts a description of each policy in |policy_map| into |values|, using + // the optional errors in |errors| to determine the status of each policy. + void GetPolicyValues(const policy::PolicyMap& policy_map, + policy::PolicyErrorMap* errors, + base::DictionaryValue* values) const; + + void GetChromePolicyValues(base::DictionaryValue* values) const; + void HandleInitialized(const base::ListValue* args); void HandleReloadPolicies(const base::ListValue* args); @@ -425,6 +438,7 @@ PolicyUIHandler::PolicyUIHandler() PolicyUIHandler::~PolicyUIHandler() { GetPolicyService()->RemoveObserver(policy::POLICY_DOMAIN_CHROME, this); + GetPolicyService()->RemoveObserver(policy::POLICY_DOMAIN_EXTENSIONS, this); } void PolicyUIHandler::RegisterMessages() { @@ -472,6 +486,7 @@ void PolicyUIHandler::RegisterMessages() { user_status_provider_->SetStatusChangeCallback(update_callback); device_status_provider_->SetStatusChangeCallback(update_callback); GetPolicyService()->AddObserver(policy::POLICY_DOMAIN_CHROME, this); + GetPolicyService()->AddObserver(policy::POLICY_DOMAIN_EXTENSIONS, this); web_ui()->RegisterMessageCallback( "initialized", @@ -485,8 +500,6 @@ void PolicyUIHandler::RegisterMessages() { void PolicyUIHandler::OnPolicyUpdated(const policy::PolicyNamespace& ns, const policy::PolicyMap& previous, const policy::PolicyMap& current) { - DCHECK_EQ(policy::POLICY_DOMAIN_CHROME, ns.domain); - DCHECK(ns.component_id.empty()); SendPolicyValues(); } @@ -502,24 +515,56 @@ void PolicyUIHandler::SendPolicyNames() const { } void PolicyUIHandler::SendPolicyValues() const { - // Make a copy that can be modified, since some policy values are modified - // before being displayed. - policy::PolicyMap map; - map.CopyFrom(GetPolicyService()->GetPolicies(policy::PolicyNamespace( - policy::POLICY_DOMAIN_CHROME, std::string()))); - - // Get a list of all the errors in the policy values. - const policy::ConfigurationPolicyHandlerList* handler_list = - g_browser_process->browser_policy_connector()->GetHandlerList(); - policy::PolicyErrorMap errors; - handler_list->ApplyPolicySettings(map, NULL, &errors); + base::DictionaryValue all_policies; + + // Add chrome policies. + base::DictionaryValue* chrome_policies = new base::DictionaryValue; + GetChromePolicyValues(chrome_policies); + all_policies.Set("chromePolicies", chrome_policies); + + // Get extensions. + extensions::ExtensionSystem* extension_system = + extensions::ExtensionSystem::Get(Profile::FromWebUI(web_ui())); + const ExtensionSet* extensions = + extension_system->extension_service()->extensions(); + + // Add policies for each extension. + base::DictionaryValue* extension_values = new base::DictionaryValue; + for (ExtensionSet::const_iterator it = extensions->begin(); + it != extensions->end(); ++it) { + const extensions::Extension* extension = *it; + + // Skip this extension if it's a component extension. + if (extension->location() == extensions::Manifest::COMPONENT) + continue; + + base::DictionaryValue* extension_value = new base::DictionaryValue; + + // Add name. + extension_value->SetString("name", extension->name()); + + // Add policies. + base::DictionaryValue* extension_policies = new base::DictionaryValue; + policy::PolicyNamespace policy_namespace = policy::PolicyNamespace( + policy::POLICY_DOMAIN_EXTENSIONS, extension->id()); + policy::PolicyErrorMap empty_error_map; + GetPolicyValues(GetPolicyService()->GetPolicies(policy_namespace), + &empty_error_map, extension_policies); + extension_value->Set("policies", extension_policies); + + // Add entry to the dictionary. + extension_values->Set(extension->id(), extension_value); + } + all_policies.Set("extensionPolicies", extension_values); - // Convert dictionary values to strings for display. - handler_list->PrepareForDisplaying(&map); + web_ui()->CallJavascriptFunction("policy.Page.setPolicyValues", all_policies); +} - base::DictionaryValue values; +void PolicyUIHandler::GetPolicyValues(const policy::PolicyMap& map, + policy::PolicyErrorMap* errors, + base::DictionaryValue* values) const { for (policy::PolicyMap::const_iterator entry = map.begin(); - entry != map.end(); ++entry) { + entry != map.end(); ++entry) { base::DictionaryValue* value = new base::DictionaryValue; value->Set("value", entry->second.value->DeepCopy()); if (entry->second.scope == policy::POLICY_SCOPE_USER) @@ -530,13 +575,33 @@ void PolicyUIHandler::SendPolicyValues() const { value->SetString("level", "recommended"); else value->SetString("level", "mandatory"); - string16 error = errors.GetErrors(entry->first); + string16 error = errors->GetErrors(entry->first); if (!error.empty()) value->SetString("error", error); - values.Set(entry->first, value); + values->Set(entry->first, value); } +} + +void PolicyUIHandler::GetChromePolicyValues( + base::DictionaryValue* values) const { + policy::PolicyService* policy_service = GetPolicyService(); + policy::PolicyMap map; + + // Make a copy that can be modified, since some policy values are modified + // before being displayed. + map.CopyFrom(policy_service->GetPolicies( + policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, std::string()))); + + // Get a list of all the errors in the policy values. + const policy::ConfigurationPolicyHandlerList* handler_list = + g_browser_process->browser_policy_connector()->GetHandlerList(); + policy::PolicyErrorMap errors; + handler_list->ApplyPolicySettings(map, NULL, &errors); + + // Convert dictionary values to strings for display. + handler_list->PrepareForDisplaying(&map); - web_ui()->CallJavascriptFunction("policy.Page.setPolicyValues", values); + GetPolicyValues(map, &errors, values); } void PolicyUIHandler::SendStatus() const { diff --git a/chrome/browser/ui/webui/policy_ui_browsertest.cc b/chrome/browser/ui/webui/policy_ui_browsertest.cc index 13fc4ff..2d8193f 100644 --- a/chrome/browser/ui/webui/policy_ui_browsertest.cc +++ b/chrome/browser/ui/webui/policy_ui_browsertest.cc @@ -122,7 +122,7 @@ void PolicyUITest::VerifyPolicies( // Retrieve the text contents of the policy table cells for all policies. const std::string javascript = "var entries = document.querySelectorAll(" - " 'table#policy-table > tbody');" + " 'section.policy-table-section > * > tbody');" "var policies = [];" "for (var i = 0; i < entries.length; ++i) {" " var items = entries[i].querySelectorAll('tr > td');" |