diff options
author | bauerb@chromium.org <bauerb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-25 15:05:36 +0000 |
---|---|---|
committer | bauerb@chromium.org <bauerb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-25 15:05:36 +0000 |
commit | fcc5ab7f60a292e2969f2db9cbeab66a907e6eac (patch) | |
tree | e28c11d36702b6bacadf606fe7c1504300d4f537 | |
parent | 509206877839c46cbe63ae0c0c2b57ab3e8bffde (diff) | |
download | chromium_src-fcc5ab7f60a292e2969f2db9cbeab66a907e6eac.zip chromium_src-fcc5ab7f60a292e2969f2db9cbeab66a907e6eac.tar.gz chromium_src-fcc5ab7f60a292e2969f2db9cbeab66a907e6eac.tar.bz2 |
Landing patch from http://codereview.chromium.org/8746019/ (Javascript readability CL for bauerb).
The original CL didn't contain updates to the generated docs, and I can't change the base URL on it to include them.
TBR=mkwst@chromium.org
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/9169044
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@119069 0039d316-1c4b-4281-b951-d872f2087c98
12 files changed, 662 insertions, 160 deletions
diff --git a/chrome/common/extensions/docs/api_index.html b/chrome/common/extensions/docs/api_index.html index 3444593..78dc513 100644 --- a/chrome/common/extensions/docs/api_index.html +++ b/chrome/common/extensions/docs/api_index.html @@ -374,7 +374,7 @@ Here are the supported chrome.* APIs: </p> <ul> - <li><a href="bookmarks.html" js="">bookmarks</a></li><li><a href="browserAction.html" js="">browserAction</a></li><li><a href="contentSettings.html" js="">contentSettings</a></li><li><a href="contextMenus.html" js="">contextMenus</a></li><li><a href="cookies.html" js="">cookies</a></li><li><a href="debugger.html" js="">debugger</a></li><li><a href="extension.html" js="">extension</a></li><li><a href="fileBrowserHandler.html" js="">fileBrowserHandler</a></li><li><a href="history.html" js="">history</a></li><li><a href="i18n.html" js="">i18n</a></li><li><a href="idle.html" js="">idle</a></li><li><a href="management.html" js="">management</a></li><li><a href="omnibox.html" js="">omnibox</a></li><li><a href="pageAction.html" js="">pageAction</a></li><li><a href="pageCapture.html" js="">pageCapture</a></li><li><a href="permissions.html" js="">permissions</a></li><li><a href="proxy.html" js="">proxy</a></li><li><a href="storage.html" js="">storage</a></li><li><a href="tabs.html" js="">tabs</a></li><li><a href="tts.html" js="">tts</a></li><li><a href="ttsEngine.html" js="">ttsEngine</a></li><li><a href="types.html" js="">types</a></li><li><a href="webNavigation.html" js="">webNavigation</a></li><li><a href="webRequest.html" js="">webRequest</a></li><li><a href="windows.html" js="">windows</a></li> + <li><a href="bookmarks.html" js="">bookmarks</a></li><li><a href="browserAction.html" js="">browserAction</a></li><li><a href="contentSettings.html" js="">contentSettings</a></li><li><a href="contextMenus.html" js="">contextMenus</a></li><li><a href="cookies.html" js="">cookies</a></li><li><a href="debugger.html" js="">debugger</a></li><li><a href="extension.html" js="">extension</a></li><li><a href="fileBrowserHandler.html" js="">fileBrowserHandler</a></li><li><a href="history.html" js="">history</a></li><li><a href="i18n.html" js="">i18n</a></li><li><a href="idle.html" js="">idle</a></li><li><a href="management.html" js="">management</a></li><li><a href="omnibox.html" js="">omnibox</a></li><li><a href="pageAction.html" js="">pageAction</a></li><li><a href="pageCapture.html" js="">pageCapture</a></li><li><a href="permissions.html" js="">permissions</a></li><li><a href="privacy.html" js="">privacy</a></li><li><a href="proxy.html" js="">proxy</a></li><li><a href="storage.html" js="">storage</a></li><li><a href="tabs.html" js="">tabs</a></li><li><a href="tts.html" js="">tts</a></li><li><a href="ttsEngine.html" js="">ttsEngine</a></li><li><a href="types.html" js="">types</a></li><li><a href="webNavigation.html" js="">webNavigation</a></li><li><a href="webRequest.html" js="">webRequest</a></li><li><a href="windows.html" js="">windows</a></li> </ul> <h2 id="experimental">Experimental APIs</h2> diff --git a/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/chrome_stubs.js b/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/chrome_stubs.js new file mode 100644 index 0000000..38b7514 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/chrome_stubs.js @@ -0,0 +1,68 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview Stubs for Chrome extension APIs that aren't available to + * regular web pages, to allow tests to run. + */ + +chrome = chrome || {}; +chrome.extension = chrome.extension || {}; +chrome.contentSettings = chrome.contentSettings || {}; + +var _rules = {}; +chrome.contentSettings.plugins = { + 'set': function(details, callback) { + assertObjectEquals({'id': 'myplugin'}, details.resourceIdentifier); + var pattern = details.primaryPattern; + var setting = details.setting; + if (pattern == '__invalid_pattern') { + chrome.extension.lastError = {'message': 'Invalid pattern'}; + } else if (setting == '__invalid_setting') { + throw Error('Invalid setting'); + } else { + chrome.extension.lastError = undefined; + _rules[pattern] = setting; + } + callback(); + }, + + 'clear': function(details, callback) { + assertObjectEquals({}, details); + _rules = {}; + callback(); + } +}; + +chrome.i18n = chrome.i18n || {}; +chrome.i18n.getMessage = function(id) { + var messages = { + 'patternColumnHeader': 'Hostname Pattern', + 'settingColumnHeader': 'Behavior', + 'allowRule': 'Allow', + 'blockRule': 'Block', + 'addNewPattern': 'Add a new hostname pattern', + }; + return messages[id]; +} + +/** + * Creates a new Settings object with a set of rules for a dummy plug-in. + * Because we provide stub implementations for the Chrome contentSettings + * extension API, we know that the methods will execute immediately instead of + * asynchronously. + * @param {!Object} rules A map from content settings pattern to setting. + * @return {!pluginSettings.Settings} A newly created Settings object with the + * passed in set of rules. + */ +function createSettings(rules) { + var settings = new pluginSettings.Settings('myplugin'); + if (rules) { + for (var pattern in rules) { + settings.set(pattern, rules[pattern], function() {}); + } + } + return settings; +} + diff --git a/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/main.js b/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/main.js index 1b61566..89aeca6 100644 --- a/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/main.js +++ b/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/main.js @@ -1,12 +1,17 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +/** + * @fileoverview Main entry point that creates a new plug-in list on document + * load. + */ + document.addEventListener('DOMContentLoaded', function() { chrome.contentSettings.plugins.getResourceIdentifiers(function(r) { if (chrome.extension.lastError) { $('error').textContent = - "Error: " + chrome.extension.lastError.message; + 'Error: ' + chrome.extension.lastError.message; return; } var pluginList = $('plugin-list'); @@ -14,3 +19,4 @@ document.addEventListener('DOMContentLoaded', function() { pluginList.dataModel = new cr.ui.ArrayDataModel(r); }); }); + diff --git a/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/plugin_list.js b/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/plugin_list.js index 53d9da8..f1d3032 100644 --- a/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/plugin_list.js +++ b/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/plugin_list.js @@ -1,64 +1,122 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +/** + * @fileoverview Defines a list of plug-ins that shows for each plug-in a list + * of content setting rules. + */ + cr.define('pluginSettings.ui', function() { const List = cr.ui.List; const ListItem = cr.ui.ListItem; const ListSingleSelectionModel = cr.ui.ListSingleSelectionModel; /** + * CSS classes used by this class. + * @enum {string} + */ + const CSSClass = { + + /** + * Hides an element. + */ + HIDDEN: 'hidden', + + /** + * A plug-in list. + */ + PLUGIN_LIST: 'plugin-list', + + /** + * Set on a plug-in list entry to show details about the plug-in. + */ + PLUGIN_SHOW_DETAILS: 'plugin-show-details', + + /** + * The plug-in name. + */ + PLUGIN_NAME: 'plugin-name', + + /** + * The number of rules set for a plug-in. + */ + NUM_RULES: 'num-rules', + + /** + * The element containing details about a plug-in. + */ + PLUGIN_DETAILS: 'plugin-details', + + /** + * The element containing the column headers for the list of rules. + */ + COLUMN_HEADERS: 'column-headers', + + /** + * The header for the pattern column. + */ + PATTERN_COLUMN_HEADER: 'pattern-column-header', + + /** + * The header for the setting column. + */ + SETTING_COLUMN_HEADER: 'setting-column-header', + }; + + /** * Returns the item's height, like offsetHeight but such that it works better * when the page is zoomed. See the similar calculation in @{code cr.ui.List}. * This version also accounts for the animation done in this file. - * @param {Element} item The item to get the height of. + * @param {!Element} item The item to get the height of. * @return {number} The height of the item, calculated with zooming in mind. */ function getItemHeight(item) { var height = item.style.height; // Use the fixed animation target height if set, in case the element is // currently being animated and we'd get an intermediate height below. - if (height && height.substr(-2) == 'px') + if (height && height.substr(-2) == 'px') { return parseInt(height.substr(0, height.length - 2)); + } return item.getBoundingClientRect().height; } /** * Creates a new plug-in list item element. - * @param {PluginList} list The plug-in list containing this item. - * @param {Object} info Information about the plug-in. + * @param {!PluginList} list The plug-in list containing this item. + * @param {!Object} info Information about the plug-in. * @constructor * @extends {cr.ui.ListItem} */ function PluginListItem(list, info) { var el = cr.doc.createElement('li'); - el.list_ = list; - el.info_ = info; - el.__proto__ = PluginListItem.prototype; - el.decorate(); - return el; - } - - PluginListItem.prototype = { - __proto__: ListItem.prototype, /** * The plug-in list containing this item. - * @type {PluginList} + * @type {!PluginList} * @private */ - list_: null, + el.list_ = list; /** * Information about the plug-in. - * @type {Object} + * @type {!Object} * @private */ - info_: null, + el.info_ = info; + + el.__proto__ = PluginListItem.prototype; + el.decorate(); + return el; + } + + PluginListItem.prototype = { + __proto__: ListItem.prototype, /** - * The element containing details about the plug-in. - * @type {HTMLDivElemebt} + * The element containing details about the plug-in. This is only null in + * the prototype. + * @type {?HTMLDivElement} * @private */ detailsElement_: null, @@ -75,28 +133,29 @@ cr.define('pluginSettings.ui', function() { var titleEl = this.ownerDocument.createElement('div'); var nameEl = this.ownerDocument.createElement('span'); - nameEl.className = 'plugin-name'; + nameEl.className = CSSClass.PLUGIN_NAME; nameEl.textContent = info.description; nameEl.title = info.description; titleEl.appendChild(nameEl); this.numRulesEl_ = this.ownerDocument.createElement('span'); - this.numRulesEl_.className = 'num-rules'; + this.numRulesEl_.className = CSSClass.NUM_RULES; titleEl.appendChild(this.numRulesEl_); contentElement.appendChild(titleEl); this.detailsElement_ = this.ownerDocument.createElement('div'); - this.detailsElement_.className = 'plugin-details hidden'; + this.detailsElement_.classList.add(CSSClass.PLUGIN_DETAILS); + this.detailsElement_.classList.add(CSSClass.HIDDEN); var columnHeadersEl = this.ownerDocument.createElement('div'); - columnHeadersEl.className = 'column-headers'; + columnHeadersEl.className = CSSClass.COLUMN_HEADERS; var patternColumnEl = this.ownerDocument.createElement('div'); patternColumnEl.textContent = - chrome.i18n.getMessage("patternColumnHeader"); - patternColumnEl.className = 'pattern-column-header'; + chrome.i18n.getMessage('patternColumnHeader'); + patternColumnEl.className = CSSClass.PATTERN_COLUMN_HEADER; var settingColumnEl = this.ownerDocument.createElement('div'); settingColumnEl.textContent = - chrome.i18n.getMessage("settingColumnHeader"); - settingColumnEl.className = 'setting-column-header'; + chrome.i18n.getMessage('settingColumnHeader'); + settingColumnEl.className = CSSClass.SETTING_COLUMN_HEADER; columnHeadersEl.appendChild(patternColumnEl); columnHeadersEl.appendChild(settingColumnEl); this.detailsElement_.appendChild(columnHeadersEl); @@ -106,8 +165,9 @@ cr.define('pluginSettings.ui', function() { var settings = new pluginSettings.Settings(this.info_.id); this.updateRulesCount_(settings); - settings.addEventListener('change', - this.updateRulesCount_.bind(this, settings)); + settings.addEventListener( + 'change', + this.updateRulesCount_.bind(this, settings)); // Create the rule list asynchronously, to make sure that it is already // fully integrated in the DOM tree. @@ -116,6 +176,8 @@ cr.define('pluginSettings.ui', function() { /** * Create the list of content setting rules applying to this plug-in. + * @param {!pluginSettings.Settings} The settings object storing the content + * setting rules. * @private */ loadRules_: function(settings) { @@ -126,6 +188,13 @@ cr.define('pluginSettings.ui', function() { rulesEl.setPluginSettings(settings); }, + /** + * Called when the list of rules changes to update the rule count shown when + * the list is not expanded. + * @param {!pluginSettings.Settings} The settings object storing the content + * setting rules. + * @private + */ updateRulesCount_: function(settings) { this.numRulesEl_.textContent = '(' + settings.getAll().length + ' rules)'; }, @@ -135,28 +204,34 @@ cr.define('pluginSettings.ui', function() { * @type {boolean} */ expanded_: false, + /** + * Whether this item is expanded or not. + * @type {boolean} + */ get expanded() { return this.expanded_; }, set expanded(expanded) { - if (this.expanded_ == expanded) + if (this.expanded_ == expanded) { return; + } this.expanded_ = expanded; if (expanded) { var oldExpanded = this.list_.expandItem; this.list_.expandItem = this; - this.detailsElement_.classList.remove('hidden'); - if (oldExpanded) + this.detailsElement_.classList.remove(CSSClass.HIDDEN); + if (oldExpanded) { oldExpanded.expanded = false; - this.classList.add('plugin-show-details'); + } + this.classList.add(CSSClass.PLUGIN_SHOW_DETAILS); } else { if (this.list_.expandItem == this) { this.list_.leadItemHeight = 0; this.list_.expandItem = null; } this.style.height = ''; - this.detailsElement_.classList.add('hidden'); - this.classList.remove('plugin-show-details'); + this.detailsElement_.classList.add(CSSClass.HIDDEN); + this.classList.remove(CSSClass.PLUGIN_SHOW_DETAILS); } }, }; @@ -176,7 +251,7 @@ cr.define('pluginSettings.ui', function() { */ decorate: function() { List.prototype.decorate.call(this); - this.classList.add('plugin-list'); + this.classList.add(CSSClass.PLUGIN_LIST); var sm = new ListSingleSelectionModel(); sm.addEventListener('change', this.handleSelectionChange_.bind(this)); this.selectionModel = sm; @@ -185,7 +260,7 @@ cr.define('pluginSettings.ui', function() { /** * Creates a new plug-in list item. - * @param {Object} info Information about the plug-in. + * @param {!Object} info Information about the plug-in. */ createItem: function(info) { return new PluginListItem(this, info); @@ -193,6 +268,7 @@ cr.define('pluginSettings.ui', function() { /** * Called when the selection changes. + * @param {!cr.Event} ce The change event. * @private */ handleSelectionChange_: function(ce) { @@ -202,8 +278,9 @@ cr.define('pluginSettings.ui', function() { if (!change.selected) { // TODO(bsmith) explain window timeout (from cookies_list.js) window.setTimeout(function() { - if (!listItem.selected || !listItem.lead) + if (!listItem.selected || !listItem.lead) { listItem.expanded = false; + } }, 0); } else if (listItem.lead) { listItem.expanded = true; diff --git a/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/plugin_list_test.html b/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/plugin_list_test.html new file mode 100644 index 0000000..9fd852a --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/plugin_list_test.html @@ -0,0 +1,68 @@ +<!DOCTYPE html> +<html> +<head> +<style> +#error { + display: none; +} +</style> +<link rel="stylesheet" href="../domui/css/button.css"> +<link rel="stylesheet" href="../domui/css/chrome_shared.css"> +<link rel="stylesheet" href="../domui/css/list.css"> +<link rel="stylesheet" href="../domui/css/select.css"> + +<link rel="stylesheet" href="../options/css/list.css"> + +<link rel="stylesheet" href="../css/plugin_list.css"> +<link rel="stylesheet" href="../css/rule_list.css"> + +<script src="http://closure-library.googlecode.com/svn/trunk/closure/goog/base.js"></script> +<script src="../domui/js/cr.js"></script> +<script src="../domui/js/cr/event_target.js"></script> +<script src="../domui/js/cr/ui.js"></script> +<script src="../domui/js/cr/ui/array_data_model.js"></script> +<script src="../domui/js/cr/ui/list_item.js"></script> +<script src="../domui/js/cr/ui/list_selection_controller.js"></script> +<script src="../domui/js/cr/ui/list_selection_model.js"></script> +<script src="../domui/js/cr/ui/list_single_selection_model.js"></script> +<script src="../domui/js/cr/ui/list.js"></script> +<script src="../domui/js/util.js"></script> + +<script src="../options/js/deletable_item_list.js"></script> +<script src="../options/js/inline_editable_list.js"></script> + +<script src="plugin_list.js" type="text/javascript"></script> +<script src="plugin_settings.js" type="text/javascript"></script> +<script src="rule_list.js" type="text/javascript"></script> + +<script> +goog.require('goog.testing.jsunit'); +</script> +<script src="chrome_stubs.js" type="text/javascript"></script> +</head> +<body> +<div id="error"></div> +<script> +function testConstruction() { + var pluginList = document.createElement('list'); + document.body.appendChild(pluginList); + pluginSettings.ui.PluginList.decorate(pluginList); + var plugins = [ + { + 'id': 'myplugin', + 'description': 'My Plugin' + } + ]; + var rules = { + 'http://example.com/*': 'block', + 'http://moose.org/*': 'allow', + }; + createSettings(rules); + pluginList.dataModel = new cr.ui.ArrayDataModel(plugins); + assertEquals('My Plugin', + pluginList.querySelector('.plugin-name').textContent); + assertEquals('(2 rules)', pluginList.querySelector('.num-rules').textContent); +} +</script> +</body> +</html> diff --git a/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/plugin_settings.js b/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/plugin_settings.js index 9929e89..253240d 100644 --- a/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/plugin_settings.js +++ b/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/plugin_settings.js @@ -1,7 +1,12 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +/** + * @fileoverview Defines a class that provides some convenient wrapper methods + * around the Chrome contentSettings extension API. + */ + cr.define('pluginSettings', function() { const EventTarget = cr.EventTarget; const Event = cr.Event; @@ -14,18 +19,16 @@ cr.define('pluginSettings', function() { * @extends {cr.EventTarget} */ function Settings(plugin) { - this.plugin_ = plugin; - } - - Settings.prototype = { - __proto__: cr.EventTarget.prototype, - /** * Identifies the plug-in for which this object stores settings. * @type {string} * @private */ - plugin_: null, + this.plugin_ = plugin; + } + + Settings.prototype = { + __proto__: cr.EventTarget.prototype, /** * Clears all content settings, and recreates them from local storage. If a @@ -37,64 +40,96 @@ cr.define('pluginSettings', function() { * @private */ recreateRules_: function(callback) { - chrome.contentSettings.plugins.clear({}, function() { - if (chrome.extension.lastError) { - console.error("Error clearing rules"); - callback(); - return; - } - var length = window.localStorage.length; - if (length == 0) { - callback(); - return; - } - var count = length; - var errors = []; - for (var i = 0; i < length; i++) { - var key = window.localStorage.key(i); - var keyArray = JSON.parse(key); - var plugin = keyArray[0]; - var pattern = keyArray[1]; - var setting = window.localStorage.getItem(key); - chrome.contentSettings.plugins.set({ + chrome.contentSettings.plugins.clear( + {}, this.didClearRules_.bind(this, callback)); + }, + + /** + * Recreates all content settings from local storage. + * @param {function()} callback Called when the content settings have been + * recreated, or on error. + * @private + */ + didClearRules_: function(callback) { + if (chrome.extension.lastError) { + console.error('Error clearing rules'); + callback(); + return; + } + var length = window.localStorage.length; + if (length == 0) { + cr.dispatchSimpleEvent(settings, 'change'); + callback(); + return; + } + var counter = { + 'value': length + }; + for (var i = 0; i < length; i++) { + var key = window.localStorage.key(i); + var keyArray = JSON.parse(key); + var plugin = keyArray[0]; + var pattern = keyArray[1]; + var setting = window.localStorage.getItem(key) + chrome.contentSettings.plugins.set( + { 'primaryPattern': pattern, - 'resourceIdentifier': { 'id': plugin }, + 'resourceIdentifier': {'id': plugin}, 'setting': setting, - }, function() { - if (chrome.extension.lastError) { - console.error('Error restoring [' + plugin_ + ', ' + - pattern + setting + ']: ' + - chrome.extension.lastError.message); - window.localStorage.removeItem(key); - } - count--; - if (count == 0) - callback(); - }); - } - }); + }, + this.didSetContentSetting_.bind( + this, key, setting, counter, callback)); + } + }, + + /** + * Checks if we're finished with recreating content settings and calls the + * passed in callback if so. + * @param {string} key The local storage key under which the content + * setting was stored. + * @param {string} value The content setting value in local storage. + * @param {{value:number}} counter Contains the number of callbacks still + * outstanding. + * @param {function()} callback Called when the content settings have been + * recreated, or on error. + * @private + */ + didSetContentSetting_: function(plugin, pattern, key, counter, callback) { + if (chrome.extension.lastError) { + console.error( + 'Error restoring [' + key + ': ' + value + ']: ' + + chrome.extension.lastError.message); + window.localStorage.removeItem(key); + } + counter.value--; + if (counter.value == 0) { + cr.dispatchSimpleEvent(this, 'change'); + callback(); + } }, /** * Creates a content setting rule and calls the passed in callback with the - * result. + * result. * @param {string} pattern The content setting pattern for the rule. * @param {string} setting The setting for the rule. - * @param {function(?string)} callback Called when the content settings have - * been updated, or on error. + * @param {function(?string)} callback Called when the content settings + * have been updated, or on error. */ set: function(pattern, setting, callback) { var plugin = this.plugin_; + var settings = this; chrome.contentSettings.plugins.set({ - 'primaryPattern': pattern, - 'resourceIdentifier': { 'id': plugin }, - 'setting': setting, + 'primaryPattern': pattern, + 'resourceIdentifier': { 'id': plugin }, + 'setting': setting, }, function() { if (chrome.extension.lastError) { callback(chrome.extension.lastError.message); } else { window.localStorage.setItem(JSON.stringify([plugin, pattern]), setting); + cr.dispatchSimpleEvent(settings, 'change'); callback(); } }); @@ -102,10 +137,10 @@ cr.define('pluginSettings', function() { /** * Removes the content setting rule with a given pattern, and calls the - * passed in callback afterwards. + * passed in callback afterwards. * @param {string} pattern The content setting pattern for the rule. - * @param {function()?} callback Called when the content settings have - * been updated. + * @param {function()} callback Called when the content settings have + * been updated. */ clear: function(pattern, callback) { window.localStorage.removeItem( @@ -115,12 +150,12 @@ cr.define('pluginSettings', function() { /** * Updates the content setting rule with a given pattern to a new pattern - * and setting and calls the passed in callback with the result. + * and setting and calls the passed in callback with the result. * @param {string} oldPattern The old content setting pattern for the rule. * @param {string} newPattern The new content setting pattern for the rule. * @param {string} setting The setting for the rule. - * @param {function(?string)} callback Called when the content settings have - * been updated, or on error. + * @param {function(?string)} callback Called when the content settings + * have been updated, or on error. */ update: function(oldPattern, newPattern, setting, callback) { if (oldPattern == newPattern) { @@ -136,8 +171,7 @@ cr.define('pluginSettings', function() { settings.set(newPattern, setting, function(error) { if (error) { // If setting the new rule failed, restore the old rule. - settings.setInternal_(oldPattern, oldSetting, - function(restoreError) { + settings.set(oldPattern, oldSetting, function(restoreError) { if (restoreError) { console.error('Error restoring [' + settings.plugin_ + ', ' + oldPattern + oldSetting + ']: ' + restoreError); @@ -162,12 +196,11 @@ cr.define('pluginSettings', function() { }, /** - * @return {array} A list of all content setting rules for this plug-in. + * @return {!Array} A list of all content setting rules for this plug-in. */ getAll: function() { - var length = window.localStorage.length; var rules = []; - for (var i = 0; i < length; i++) { + for (var i = 0; i < window.localStorage.length; i++) { var key = window.localStorage.key(i); var keyArray = JSON.parse(key); if (keyArray[0] == this.plugin_) { @@ -185,4 +218,3 @@ cr.define('pluginSettings', function() { Settings: Settings, } }); - diff --git a/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/plugin_settings_test.html b/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/plugin_settings_test.html new file mode 100644 index 0000000..7bcad36 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/plugin_settings_test.html @@ -0,0 +1,143 @@ +<!DOCTYPE html> +<html> +<head> +<script src="http://closure-library.googlecode.com/svn/trunk/closure/goog/base.js"></script> +<script src="../domui/js/cr.js"></script> +<script src="../domui/js/cr/event_target.js"></script> +<script src="plugin_settings.js" type="text/javascript"></script> +<script> +goog.require('goog.testing.jsunit'); +</script> +<script src="chrome_stubs.js" type="text/javascript"></script> +</head> +<body> +<script> +function testConstruction() { + var settings = createSettings(); +} + +function testSet() { + var settings = createSettings(); + var rules = { + 'http://example.com/*': 'block', + 'http://google.com/*': 'allow', + 'http://moose.org/*': 'allow', + }; + var numCallbacks = 0; + for (var pattern in rules) { + settings.set(pattern, rules[pattern], function(error) { + numCallbacks++; + assertUndefined(error); + }); + } + assertEquals(Object.keys(rules).length, numCallbacks); + assertObjectEquals(rules, _rules); +} + +function testSetInvalid() { + var settings = createSettings(); + // Attempting to set an invalid pattern should return an error in the + // callback. + var callbackCalled = false; + settings.set('__invalid_pattern', 'block', function(error) { + callbackCalled = true; + assertEquals('Invalid pattern', error); + }); + assertTrue(callbackCalled); + assertObjectEquals({}, _rules); + + // Attempting to set an invalid setting should immediately throw an exception. + callbackCalled = false; + assertThrows(function() { + settings.set('http://example.com/*', '__invalid_setting', function() { + callbackCalled = true; + }); + }); + assertFalse(callbackCalled); + assertObjectEquals({}, _rules); +} + +function testGet() { + var rules = { + 'http://example.com/*': 'block', + 'http://google.com/*': 'allow', + 'http://moose.org/*': 'allow', + }; + var settings = createSettings(rules); + for (var pattern in rules) + assertEquals(rules[pattern], settings.get(pattern)); +} + +function testGetAll() { + var settings = createSettings({ + 'http://example.com/*': 'block', + 'http://google.com/*': 'allow', + 'http://moose.org/*': 'allow', + }); + var rules = settings.getAll(); + // Sort the rules lexicographically by their pattern. + rules.sort(function(a, b) { + if (a.primaryPattern == b.primaryPattern) { + return 0; + } + if (a.primaryPattern > b.primaryPattern) { + return 1; + } + return -1; + }); + assertEquals(3, rules.length); + assertObjectEquals({'primaryPattern': 'http://example.com/*', + 'setting': 'block'}, + rules[0]); + assertObjectEquals({'primaryPattern': 'http://google.com/*', + 'setting': 'allow'}, + rules[1]); + assertObjectEquals({'primaryPattern': 'http://moose.org/*', + 'setting': 'allow'}, + rules[2]); +} + +function testUpdate() { + var settings = createSettings({ + 'http://example.com/*': 'block', + 'http://google.com/*': 'allow', + 'http://moose.org/*': 'allow', + }); + var numCallbacks = 0; + settings.update('http://google.com/*', 'http://google.com/*', 'ask', + function(error) { + numCallbacks++; + assertUndefined(error); + }); + assertEquals('ask', _rules['http://google.com/*']); + + settings.update('http://google.com/*', 'http://blurp.net/*', 'ask', + function(error) { + numCallbacks++; + assertUndefined(error); + }); + assertUndefined(_rules['http://google.com/*']); + assertEquals('ask', _rules['http://blurp.net/*']); + + // Attempting to update a rule to an invalid pattern should return an error + // and leave the rules unchanged. + settings.update('http://example.com/*', '__invalid_pattern', 'ask', + function(error) { + numCallbacks++; + assertEquals('Invalid pattern', error); + }); + assertUndefined(_rules['__invalid_pattern']); + assertEquals('block', _rules['http://example.com/*']); + + assertEquals(3, numCallbacks); +} + +function tearDown() { + // Clear local storage and |rules_| to make sure no state leaks into the next + // test. + window.localStorage.clear(); + _rules = {}; +} +</script> +</body> +</html> diff --git a/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/rule_list.js b/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/rule_list.js index db83cee..9919958 100644 --- a/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/rule_list.js +++ b/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/rule_list.js @@ -1,23 +1,63 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +/** + * @fileoverview Defines a list of content setting rules. + */ + cr.define('pluginSettings.ui', function() { const InlineEditableItemList = options.InlineEditableItemList; const InlineEditableItem = options.InlineEditableItem; const ArrayDataModel = cr.ui.ArrayDataModel; /** - * Creates a new rule list item. - * @param {RuleList} list The rule list containing this item. - * @param {Object} rule The content setting rule. + * CSS classes used by this class. + * @enum {string} + */ + const CSSClass = { + /** + * A list of content setting rules. + */ + RULE_LIST: 'rule-list', + + /** + * The element containing the content setting pattern for a rule. + */ + RULE_PATTERN: 'rule-pattern', + + /** + * The element containing the behavior (allow or block) for a rule. + */ + RULE_BEHAVIOR: 'rule-behavior', + + /** + * Static text (as opposed to an editable text field). + */ + STATIC_TEXT: 'static-text', + }; + /** + * A single item in a list of rules. + * @param {!RuleList} list The rule list containing this item. + * @param {!Object} rule The content setting rule. * @constructor * @extends {options.InlineEditableItem} */ function RuleListItem(list, rule) { var el = cr.doc.createElement('li'); + /** + * The content setting rule. + * @type {!Object} + * @private + */ el.dataItem_ = rule; + + /** + * The rule list containing this item. + * @type {!RuleList} + * @private + */ el.list_ = list; el.__proto__ = RuleListItem.prototype; el.decorate(); @@ -29,63 +69,51 @@ cr.define('pluginSettings.ui', function() { __proto__: InlineEditableItem.prototype, /** - * The content setting rule. - * @type {Object} - * @private - */ - dataItem_: null, - - /** - * The rule list containing this item. - * @type {RuleList} - * @private - */ - list_: null, - - /** - * The text input element for the pattern. - * @type {HTMLInputElement} + * The text input element for the pattern. This is only null in the + * prototype. + * @type {?HTMLInputElement} * @private */ input_: null, /** - * The popup button for the setting. - * @type {HTMLSelectElement} + * The popup button for the setting. This is only null in the prototype. + * @type {?HTMLSelectElement} * @private */ select_: null, /** * The static text field containing the pattern. - * @type {HTMLDivElement} + * @type {?HTMLDivElement} * @private */ patternLabel_: null, /** * The static text field containing the setting. - * @type {HTMLDivElement} + * @type {?HTMLDivElement} * @private */ settingLabel_: null, /** - * Called when an element is decorated as a list item. + * Decorates an elements as a list item. */ decorate: function() { InlineEditableItem.prototype.decorate.call(this); this.isPlaceholder = !this.pattern; var patternCell = this.createEditableTextCell(this.pattern); - patternCell.className = 'rule-pattern'; - patternCell.classList.add('weakrtl'); + patternCell.className = CSSClass.RULE_PATTERN; this.contentElement.appendChild(patternCell); var input = patternCell.querySelector('input'); - if (this.pattern) - this.patternLabel_ = patternCell.querySelector('.static-text'); - else - input.placeholder = chrome.i18n.getMessage("addNewPattern"); + if (this.pattern) { + this.patternLabel_ = + patternCell.querySelector('.' + CSSClass.STATIC_TEXT); + } else { + input.placeholder = chrome.i18n.getMessage('addNewPattern'); + } // TODO(stuartmorgan): Create an createEditableSelectCell abstracting // this code. @@ -94,7 +122,7 @@ cr.define('pluginSettings.ui', function() { if (this.pattern) { var settingLabel = cr.doc.createElement('span'); settingLabel.textContent = this.settingForDisplay(); - settingLabel.className = 'rule-behavior'; + settingLabel.className = CSSClass.RULE_BEHAVIOR; settingLabel.setAttribute('displaymode', 'static'); this.contentElement.appendChild(settingLabel); this.settingLabel_ = settingLabel; @@ -103,19 +131,20 @@ cr.define('pluginSettings.ui', function() { // Setting select element for edit mode. var select = cr.doc.createElement('select'); var optionAllow = cr.doc.createElement('option'); - optionAllow.textContent = chrome.i18n.getMessage("allowRule"); + optionAllow.textContent = chrome.i18n.getMessage('allowRule'); optionAllow.value = 'allow'; select.appendChild(optionAllow); var optionBlock = cr.doc.createElement('option'); - optionBlock.textContent = chrome.i18n.getMessage("blockRule"); + optionBlock.textContent = chrome.i18n.getMessage('blockRule'); optionBlock.value = 'block'; select.appendChild(optionBlock); this.contentElement.appendChild(select); - select.className = 'rule-behavior'; - if (this.pattern) + select.className = CSSClass.RULE_BEHAVIOR; + if (this.pattern) { select.setAttribute('displaymode', 'edit'); + } this.input_ = input; this.select_ = select; @@ -155,10 +184,12 @@ cr.define('pluginSettings.ui', function() { */ settingForDisplay: function() { var setting = this.setting; - if (setting == 'allow') - return chrome.i18n.getMessage("allowRule"); - else if (setting == 'block') - return chrome.i18n.getMessage("blockRule"); + if (setting == 'allow') { + return chrome.i18n.getMessage('allowRule'); + } + if (setting == 'block') { + return chrome.i18n.getMessage('blockRule'); + } }, /** @@ -177,8 +208,9 @@ cr.define('pluginSettings.ui', function() { var settingOption = this.select_.querySelector('[value=\'' + this.setting + '\']'); - if (settingOption) + if (settingOption) { settingOption.selected = true; + } }, /** @inheritDoc */ @@ -190,7 +222,7 @@ cr.define('pluginSettings.ui', function() { /** * Called when committing an edit. - * @param {Event} e The end event. + * @param {!Event} e The end event. * @private */ onEditCommitted_: function(e) { @@ -202,7 +234,7 @@ cr.define('pluginSettings.ui', function() { /** * Called when cancelling an edit; resets the control states. - * @param {Event} e The cancel event. + * @param {!Event} e The cancel event. * @private */ onEditCancelled_: function() { @@ -228,7 +260,7 @@ cr.define('pluginSettings.ui', function() { /** * Create a new list item to add a rule. - * @param {RuleList} list The rule list containing this item. + * @param {!RuleList} list The rule list containing this item. * @constructor * @extends {AddRuleListItem} */ @@ -280,7 +312,7 @@ cr.define('pluginSettings.ui', function() { }; /** - * Creates a new rule list. + * A list of content setting rules. * @constructor * @extends {cr.ui.List} */ @@ -291,7 +323,7 @@ cr.define('pluginSettings.ui', function() { /** * The content settings model for this list. - * @type {Settings} + * @type {?Settings} */ settings: null, @@ -301,7 +333,7 @@ cr.define('pluginSettings.ui', function() { decorate: function() { InlineEditableItemList.prototype.decorate.call(this); - this.classList.add('rule-list'); + this.classList.add(CSSClass.RULE_LIST); this.autoExpands = true; this.reset(); @@ -309,7 +341,7 @@ cr.define('pluginSettings.ui', function() { /** * Creates an item to go in the list. - * @param {Object} entry The element from the data model for this row. + * @param {?Object} entry The element from the data model for this row. */ createItem: function(entry) { if (entry) { @@ -323,7 +355,7 @@ cr.define('pluginSettings.ui', function() { /** * Sets the rules in the js model. - * @param {Object} entries A list of dictionaries of values, each dictionary + * @param {!Array} entries A list of dictionaries of values, each dictionary * represents a rule. */ setRules_: function(entries) { @@ -341,16 +373,17 @@ cr.define('pluginSettings.ui', function() { * @private */ settingsChanged_: function(error) { - if (error) + if (error) { $('error').textContent = 'Error: ' + error; - else + } else { $('error').textContent = ''; + } this.setRules_(this.settings.getAll()); }, /** - * @return {function()} A bound callback to update the UI after the settings - * have been changed. + * @return {function()} A bound callback to update the UI after the + * settings have been changed. */ settingsChangedCallback: function() { return this.settingsChanged_.bind(this); @@ -358,7 +391,7 @@ cr.define('pluginSettings.ui', function() { /** * Binds this list to the content settings model. - * @param {Settings} settings The content settings model. + * @param {!Settings} settings The content settings model. */ setPluginSettings: function(settings) { this.settings = settings; @@ -376,8 +409,9 @@ cr.define('pluginSettings.ui', function() { /** @inheritDoc */ deleteItemAtIndex: function(index) { var listItem = this.getListItemByIndex(index); - if (listItem.undeletable) + if (listItem.undeletable) { return; + } this.settings.clear(listItem.pattern, this.settingsChangedCallback()); }, @@ -389,4 +423,3 @@ cr.define('pluginSettings.ui', function() { RuleList: RuleList, } }); - diff --git a/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/rule_list_test.html b/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/rule_list_test.html new file mode 100644 index 0000000..fee89f5 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/plugin_settings/js/rule_list_test.html @@ -0,0 +1,64 @@ +<!DOCTYPE html> +<html> +<head> +<link rel="stylesheet" href="../domui/css/button.css"> +<link rel="stylesheet" href="../domui/css/chrome_shared.css"> +<link rel="stylesheet" href="../domui/css/list.css"> +<link rel="stylesheet" href="../domui/css/select.css"> + +<link rel="stylesheet" href="../options/css/list.css"> + +<link rel="stylesheet" href="../css/rule_list.css"> + +<script src="http://closure-library.googlecode.com/svn/trunk/closure/goog/base.js"></script> +<script src="../domui/js/cr.js"></script> +<script src="../domui/js/cr/event_target.js"></script> +<script src="../domui/js/cr/ui.js"></script> +<script src="../domui/js/cr/ui/array_data_model.js"></script> +<script src="../domui/js/cr/ui/list_item.js"></script> +<script src="../domui/js/cr/ui/list_selection_controller.js"></script> +<script src="../domui/js/cr/ui/list_selection_model.js"></script> +<script src="../domui/js/cr/ui/list_single_selection_model.js"></script> +<script src="../domui/js/cr/ui/list.js"></script> +<script src="../domui/js/util.js"></script> + +<script src="../options/js/deletable_item_list.js"></script> +<script src="../options/js/inline_editable_list.js"></script> + +<script src="plugin_settings.js" type="text/javascript"></script> +<script src="rule_list.js" type="text/javascript"></script> + +<script> +goog.require('goog.testing.jsunit'); +</script> +<script src="chrome_stubs.js" type="text/javascript"></script> +</head> +<body> +<list id="rule-list"></list> +<div id="error"></div> +<script> +function testConstruction() { + var rulesEl = document.createElement('list'); + document.body.appendChild(rulesEl); + pluginSettings.ui.RuleList.decorate(rulesEl); + var rules = { + 'http://example.com/*': 'block', + 'http://moose.org/*': 'allow', + }; + rulesEl.setPluginSettings(createSettings(rules)); + var ruleElements = rulesEl.querySelectorAll('[role=listitem]'); + assertEquals(3, ruleElements.length); + assertEquals('http://example.com/*', + ruleElements[0].querySelector('.rule-pattern').textContent); + assertEquals('http://moose.org/*', + ruleElements[1].querySelector('.rule-pattern').textContent); + assertEquals('', ruleElements[2].querySelector('.rule-pattern').textContent); + assertEquals('Block', + ruleElements[0].querySelector('.rule-behavior').textContent); + assertEquals('Allow', + ruleElements[1].querySelector('.rule-behavior').textContent); + assertEquals('allow', ruleElements[2].querySelector('.rule-behavior').value); +} +</script> +</body> +</html> diff --git a/chrome/common/extensions/docs/experimental.html b/chrome/common/extensions/docs/experimental.html index c757619..0f64673 100644 --- a/chrome/common/extensions/docs/experimental.html +++ b/chrome/common/extensions/docs/experimental.html @@ -397,7 +397,6 @@ on the following experimental APIs: <a href="experimental.devtools.audits.html">experimental.devtools.audits</a></li><li> <a href="experimental.devtools.console.html">experimental.devtools.console</a></li><li> <a href="experimental.infobars.html">experimental.infobars</a></li><li> - <a href="experimental.privacy.html">experimental.privacy</a></li><li> <a href="experimental.speechInput.html">experimental.speechInput</a></li> </ul> diff --git a/chrome/common/extensions/docs/samples.html b/chrome/common/extensions/docs/samples.html index 325077c..7227b75 100644 --- a/chrome/common/extensions/docs/samples.html +++ b/chrome/common/extensions/docs/samples.html @@ -3309,14 +3309,22 @@ </li><li> <code><a target="_blank" href="examples/extensions/plugin_settings/domui/js/util.js">domui/js/util.js</a></code> </li><li> + <code><a target="_blank" href="examples/extensions/plugin_settings/js/chrome_stubs.js">js/chrome_stubs.js</a></code> + </li><li> <code><a target="_blank" href="examples/extensions/plugin_settings/js/main.js">js/main.js</a></code> </li><li> <code><a target="_blank" href="examples/extensions/plugin_settings/js/plugin_list.js">js/plugin_list.js</a></code> </li><li> + <code><a target="_blank" href="examples/extensions/plugin_settings/js/plugin_list_test.html">js/plugin_list_test.html</a></code> + </li><li> <code><a target="_blank" href="examples/extensions/plugin_settings/js/plugin_settings.js">js/plugin_settings.js</a></code> </li><li> + <code><a target="_blank" href="examples/extensions/plugin_settings/js/plugin_settings_test.html">js/plugin_settings_test.html</a></code> + </li><li> <code><a target="_blank" href="examples/extensions/plugin_settings/js/rule_list.js">js/rule_list.js</a></code> </li><li> + <code><a target="_blank" href="examples/extensions/plugin_settings/js/rule_list_test.html">js/rule_list_test.html</a></code> + </li><li> <code><a target="_blank" href="examples/extensions/plugin_settings/manifest.json">manifest.json</a></code> </li><li> <code><a target="_blank" href="examples/extensions/plugin_settings/options.html">options.html</a></code> diff --git a/chrome/common/extensions/docs/samples.json b/chrome/common/extensions/docs/samples.json index 9191245..4f3c1c6 100644 --- a/chrome/common/extensions/docs/samples.json +++ b/chrome/common/extensions/docs/samples.json @@ -1845,17 +1845,21 @@ "domui\/js\/cr\/ui\/list_selection_model.js", "domui\/js\/cr\/ui\/list_single_selection_model.js", "domui\/js\/util.js", + "js\/chrome_stubs.js", "js\/main.js", "js\/plugin_list.js", + "js\/plugin_list_test.html", "js\/plugin_settings.js", + "js\/plugin_settings_test.html", "js\/rule_list.js", + "js\/rule_list_test.html", "manifest.json", "options.html", "options\/css\/list.css", "options\/js\/deletable_item_list.js", "options\/js\/inline_editable_list.js" ], - "source_hash": "914933e586fe84d71ba8b01c88309288b9d4e515", + "source_hash": "5091cd6db218262813b6d98264cf580c6469ce23", "zip_path": "examples\/extensions\/plugin_settings.zip" }, { |