Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4292)

Unified Diff: chrome/browser/resources/options2/cookies_list.js

Issue 8895023: Options2: Pull the trigger. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: DIAF. Created 9 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/resources/options2/cookies_list.js
diff --git a/chrome/browser/resources/options2/cookies_list.js b/chrome/browser/resources/options2/cookies_list.js
new file mode 100644
index 0000000000000000000000000000000000000000..4e70d1d0708a8462070d96bc86c335ecad59ffc4
--- /dev/null
+++ b/chrome/browser/resources/options2/cookies_list.js
@@ -0,0 +1,853 @@
+// Copyright (c) 2011 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.
+
+cr.define('options', function() {
+ const DeletableItemList = options.DeletableItemList;
+ const DeletableItem = options.DeletableItem;
+ const ArrayDataModel = cr.ui.ArrayDataModel;
+ const ListSingleSelectionModel = cr.ui.ListSingleSelectionModel;
+
+ // This structure maps the various cookie type names from C++ (hence the
+ // underscores) to arrays of the different types of data each has, along with
+ // the i18n name for the description of that data type.
+ const cookieInfo = {
+ 'cookie': [ ['name', 'label_cookie_name'],
+ ['content', 'label_cookie_content'],
+ ['domain', 'label_cookie_domain'],
+ ['path', 'label_cookie_path'],
+ ['sendfor', 'label_cookie_send_for'],
+ ['accessibleToScript', 'label_cookie_accessible_to_script'],
+ ['created', 'label_cookie_created'],
+ ['expires', 'label_cookie_expires'] ],
+ 'app_cache': [ ['manifest', 'label_app_cache_manifest'],
+ ['size', 'label_local_storage_size'],
+ ['created', 'label_cookie_created'],
+ ['accessed', 'label_cookie_last_accessed'] ],
+ 'database': [ ['name', 'label_cookie_name'],
+ ['desc', 'label_webdb_desc'],
+ ['size', 'label_local_storage_size'],
+ ['modified', 'label_local_storage_last_modified'] ],
+ 'local_storage': [ ['origin', 'label_local_storage_origin'],
+ ['size', 'label_local_storage_size'],
+ ['modified', 'label_local_storage_last_modified'] ],
+ 'indexed_db': [ ['origin', 'label_indexed_db_origin'],
+ ['size', 'label_indexed_db_size'],
+ ['modified', 'label_indexed_db_last_modified'] ],
+ 'file_system': [ ['origin', 'label_file_system_origin'],
+ ['persistent', 'label_file_system_persistent_usage' ],
+ ['temporary', 'label_file_system_temporary_usage' ] ],
+ };
+
+ const localStrings = new LocalStrings();
+
+ /**
+ * 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.
+ * @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')
+ return parseInt(height.substr(0, height.length - 2));
+ return item.getBoundingClientRect().height;
+ }
+
+ /**
+ * Create tree nodes for the objects in the data array, and insert them all
+ * into the given list using its @{code splice} method at the given index.
+ * @param {Array.<Object>} data The data objects for the nodes to add.
+ * @param {number} start The index at which to start inserting the nodes.
+ * @return {Array.<CookieTreeNode>} An array of CookieTreeNodes added.
+ */
+ function spliceTreeNodes(data, start, list) {
+ var nodes = data.map(function(x) { return new CookieTreeNode(x); });
+ // Insert [start, 0] at the beginning of the array of nodes, making it
+ // into the arguments we want to pass to @{code list.splice} below.
+ nodes.splice(0, 0, start, 0);
+ list.splice.apply(list, nodes);
+ // Remove the [start, 0] prefix and return the array of nodes.
+ nodes.splice(0, 2);
+ return nodes;
+ }
+
+ var parentLookup = {};
+ var lookupRequests = {};
+
+ /**
+ * Creates a new list item for sites data. Note that these are created and
+ * destroyed lazily as they scroll into and out of view, so they must be
+ * stateless. We cache the expanded item in @{code CookiesList} though, so it
+ * can keep state. (Mostly just which item is selected.)
+ * @param {Object} origin Data used to create a cookie list item.
+ * @param {CookiesList} list The list that will contain this item.
+ * @constructor
+ * @extends {DeletableItem}
+ */
+ function CookieListItem(origin, list) {
+ var listItem = new DeletableItem(null);
+ listItem.__proto__ = CookieListItem.prototype;
+
+ listItem.origin = origin;
+ listItem.list = list;
+ listItem.decorate();
+
+ // This hooks up updateOrigin() to the list item, makes the top-level
+ // tree nodes (i.e., origins) register their IDs in parentLookup, and
+ // causes them to request their children if they have none. Note that we
+ // have special logic in the setter for the parent property to make sure
+ // that we can still garbage collect list items when they scroll out of
+ // view, even though it appears that we keep a direct reference.
+ if (origin) {
+ origin.parent = listItem;
+ origin.updateOrigin();
+ }
+
+ return listItem;
+ }
+
+ CookieListItem.prototype = {
+ __proto__: DeletableItem.prototype,
+
+ /** @inheritDoc */
+ decorate: function() {
+ this.siteChild = this.ownerDocument.createElement('div');
+ this.siteChild.className = 'cookie-site';
+ this.dataChild = this.ownerDocument.createElement('div');
+ this.dataChild.className = 'cookie-data';
+ this.sizeChild = this.ownerDocument.createElement('div');
+ this.sizeChild.className = 'cookie-size';
+ this.itemsChild = this.ownerDocument.createElement('div');
+ this.itemsChild.className = 'cookie-items';
+ this.infoChild = this.ownerDocument.createElement('div');
+ this.infoChild.className = 'cookie-details';
+ this.infoChild.hidden = true;
+ var remove = this.ownerDocument.createElement('button');
+ remove.textContent = localStrings.getString('remove_cookie');
+ remove.onclick = this.removeCookie_.bind(this);
+ this.infoChild.appendChild(remove);
+ var content = this.contentElement;
+ content.appendChild(this.siteChild);
+ content.appendChild(this.dataChild);
+ content.appendChild(this.sizeChild);
+ content.appendChild(this.itemsChild);
+ this.itemsChild.appendChild(this.infoChild);
+ if (this.origin && this.origin.data) {
+ this.siteChild.textContent = this.origin.data.title;
+ this.siteChild.setAttribute('title', this.origin.data.title);
+ }
+ this.itemList_ = [];
+ },
+
+ /** @type {boolean} */
+ get expanded() {
+ return this.expanded_;
+ },
+ set expanded(expanded) {
+ if (this.expanded_ == expanded)
+ return;
+ this.expanded_ = expanded;
+ if (expanded) {
+ var oldExpanded = this.list.expandedItem;
+ this.list.expandedItem = this;
+ this.updateItems_();
+ if (oldExpanded)
+ oldExpanded.expanded = false;
+ this.classList.add('show-items');
+ } else {
+ if (this.list.expandedItem == this) {
+ this.list.expandedItem = null;
+ }
+ this.style.height = '';
+ this.itemsChild.style.height = '';
+ this.classList.remove('show-items');
+ }
+ },
+
+ /**
+ * The callback for the "remove" button shown when an item is selected.
+ * Requests that the currently selected cookie be removed.
+ * @private
+ */
+ removeCookie_: function() {
+ if (this.selectedIndex_ >= 0) {
+ var item = this.itemList_[this.selectedIndex_];
+ if (item && item.node)
+ chrome.send('removeCookie', [item.node.pathId]);
+ }
+ },
+
+ /**
+ * Disable animation within this cookie list item, in preparation for making
+ * changes that will need to be animated. Makes it possible to measure the
+ * contents without displaying them, to set animation targets.
+ * @private
+ */
+ disableAnimation_: function() {
+ this.itemsHeight_ = getItemHeight(this.itemsChild);
+ this.classList.add('measure-items');
+ },
+
+ /**
+ * Enable animation after changing the contents of this cookie list item.
+ * See @{code disableAnimation_}.
+ * @private
+ */
+ enableAnimation_: function() {
+ if (!this.classList.contains('measure-items'))
+ this.disableAnimation_();
+ this.itemsChild.style.height = '';
+ // This will force relayout in order to calculate the new heights.
+ var itemsHeight = getItemHeight(this.itemsChild);
+ var fixedHeight = getItemHeight(this) + itemsHeight - this.itemsHeight_;
+ this.itemsChild.style.height = this.itemsHeight_ + 'px';
+ // Force relayout before enabling animation, so that if we have
+ // changed things since the last layout, they will not be animated
+ // during subsequent layouts.
+ this.itemsChild.offsetHeight;
+ this.classList.remove('measure-items');
+ this.itemsChild.style.height = itemsHeight + 'px';
+ this.style.height = fixedHeight + 'px';
+ },
+
+ /**
+ * Updates the origin summary to reflect changes in its items.
+ * Both CookieListItem and CookieTreeNode implement this API.
+ * This implementation scans the descendants to update the text.
+ */
+ updateOrigin: function() {
+ var info = {
+ cookies: 0,
+ database: false,
+ localStorage: false,
+ appCache: false,
+ indexedDb: false,
+ fileSystem: false,
+ };
+ if (this.origin)
+ this.origin.collectSummaryInfo(info);
+ var list = [];
+ if (info.cookies > 1)
+ list.push(localStrings.getStringF('cookie_plural', info.cookies));
+ else if (info.cookies > 0)
+ list.push(localStrings.getString('cookie_singular'));
+ if (info.database || info.indexedDb)
+ list.push(localStrings.getString('cookie_database_storage'));
+ if (info.localStorage)
+ list.push(localStrings.getString('cookie_local_storage'));
+ if (info.appCache)
+ list.push(localStrings.getString('cookie_app_cache'));
+ if (info.fileSystem)
+ list.push(localStrings.getString('cookie_file_system'));
+ var text = '';
+ for (var i = 0; i < list.length; ++i)
+ if (text.length > 0)
+ text += ', ' + list[i];
+ else
+ text = list[i];
+ this.dataChild.textContent = text;
+ if (info.quota && info.quota.totalUsage) {
+ this.sizeChild.textContent = info.quota.totalUsage;
+ }
+
+ if (this.expanded)
+ this.updateItems_();
+ },
+
+ /**
+ * Updates the items section to reflect changes, animating to the new state.
+ * Removes existing contents and calls @{code CookieTreeNode.createItems}.
+ * @private
+ */
+ updateItems_: function() {
+ this.disableAnimation_();
+ this.itemsChild.textContent = '';
+ this.infoChild.hidden = true;
+ this.selectedIndex_ = -1;
+ this.itemList_ = [];
+ if (this.origin)
+ this.origin.createItems(this);
+ this.itemsChild.appendChild(this.infoChild);
+ this.enableAnimation_();
+ },
+
+ /**
+ * Append a new cookie node "bubble" to this list item.
+ * @param {CookieTreeNode} node The cookie node to add a bubble for.
+ * @param {Element} div The DOM element for the bubble itself.
+ * @return {number} The index the bubble was added at.
+ */
+ appendItem: function(node, div) {
+ this.itemList_.push({node: node, div: div});
+ this.itemsChild.appendChild(div);
+ return this.itemList_.length - 1;
+ },
+
+ /**
+ * The currently selected cookie node ("cookie bubble") index.
+ * @type {number}
+ * @private
+ */
+ selectedIndex_: -1,
+
+ /**
+ * Get the currently selected cookie node ("cookie bubble") index.
+ * @type {number}
+ */
+ get selectedIndex() {
+ return this.selectedIndex_;
+ },
+
+ /**
+ * Set the currently selected cookie node ("cookie bubble") index to
+ * @{code itemIndex}, unselecting any previously selected node first.
+ * @param {number} itemIndex The index to set as the selected index.
+ */
+ set selectedIndex(itemIndex) {
+ // Get the list index up front before we change anything.
+ var index = this.list.getIndexOfListItem(this);
+ // Unselect any previously selected item.
+ if (this.selectedIndex_ >= 0) {
+ var item = this.itemList_[this.selectedIndex_];
+ if (item && item.div)
+ item.div.removeAttribute('selected');
+ }
+ // Special case: decrementing -1 wraps around to the end of the list.
+ if (itemIndex == -2)
+ itemIndex = this.itemList_.length - 1;
+ // Check if we're going out of bounds and hide the item details.
+ if (itemIndex < 0 || itemIndex >= this.itemList_.length) {
+ this.selectedIndex_ = -1;
+ this.disableAnimation_();
+ this.infoChild.hidden = true;
+ this.enableAnimation_();
+ return;
+ }
+ // Set the new selected item and show the item details for it.
+ this.selectedIndex_ = itemIndex;
+ this.itemList_[itemIndex].div.setAttribute('selected', '');
+ this.disableAnimation_();
+ this.itemList_[itemIndex].node.setDetailText(this.infoChild,
+ this.list.infoNodes);
+ this.infoChild.hidden = false;
+ this.enableAnimation_();
+ // If we're near the bottom of the list this may cause the list item to go
+ // beyond the end of the visible area. Fix it after the animation is done.
+ var list = this.list;
+ window.setTimeout(function() { list.scrollIndexIntoView(index); }, 150);
+ },
+ };
+
+ /**
+ * {@code CookieTreeNode}s mirror the structure of the cookie tree lazily, and
+ * contain all the actual data used to generate the {@code CookieListItem}s.
+ * @param {Object} data The data object for this node.
+ * @constructor
+ */
+ function CookieTreeNode(data) {
+ this.data = data;
+ this.children = [];
+ }
+
+ CookieTreeNode.prototype = {
+ /**
+ * Insert the given list of cookie tree nodes at the given index.
+ * Both CookiesList and CookieTreeNode implement this API.
+ * @param {Array.<Object>} data The data objects for the nodes to add.
+ * @param {number} start The index at which to start inserting the nodes.
+ */
+ insertAt: function(data, start) {
+ var nodes = spliceTreeNodes(data, start, this.children);
+ for (var i = 0; i < nodes.length; i++)
+ nodes[i].parent = this;
+ this.updateOrigin();
+ },
+
+ /**
+ * Remove a cookie tree node from the given index.
+ * Both CookiesList and CookieTreeNode implement this API.
+ * @param {number} index The index of the tree node to remove.
+ */
+ remove: function(index) {
+ if (index < this.children.length) {
+ this.children.splice(index, 1);
+ this.updateOrigin();
+ }
+ },
+
+ /**
+ * Clears all children.
+ * Both CookiesList and CookieTreeNode implement this API.
+ * It is used by CookiesList.loadChildren().
+ */
+ clear: function() {
+ // We might leave some garbage in parentLookup for removed children.
+ // But that should be OK because parentLookup is cleared when we
+ // reload the tree.
+ this.children = [];
+ this.updateOrigin();
+ },
+
+ /**
+ * The counter used by startBatchUpdates() and endBatchUpdates().
+ * @type {number}
+ */
+ batchCount_: 0,
+
+ /**
+ * See cr.ui.List.startBatchUpdates().
+ * Both CookiesList (via List) and CookieTreeNode implement this API.
+ */
+ startBatchUpdates: function() {
+ this.batchCount_++;
+ },
+
+ /**
+ * See cr.ui.List.endBatchUpdates().
+ * Both CookiesList (via List) and CookieTreeNode implement this API.
+ */
+ endBatchUpdates: function() {
+ if (!--this.batchCount_)
+ this.updateOrigin();
+ },
+
+ /**
+ * Requests updating the origin summary to reflect changes in this item.
+ * Both CookieListItem and CookieTreeNode implement this API.
+ */
+ updateOrigin: function() {
+ if (!this.batchCount_ && this.parent)
+ this.parent.updateOrigin();
+ },
+
+ /**
+ * Summarize the information in this node and update @{code info}.
+ * This will recurse into child nodes to summarize all descendants.
+ * @param {Object} info The info object from @{code updateOrigin}.
+ */
+ collectSummaryInfo: function(info) {
+ if (this.children.length > 0) {
+ for (var i = 0; i < this.children.length; ++i)
+ this.children[i].collectSummaryInfo(info);
+ } else if (this.data && !this.data.hasChildren) {
+ if (this.data.type == 'cookie') {
+ info.cookies++;
+ } else if (this.data.type == 'database') {
+ info.database = true;
+ } else if (this.data.type == 'local_storage') {
+ info.localStorage = true;
+ } else if (this.data.type == 'app_cache') {
+ info.appCache = true;
+ } else if (this.data.type == 'indexed_db') {
+ info.indexedDb = true;
+ } else if (this.data.type == 'file_system') {
+ info.fileSystem = true;
+ } else if (this.data.type == 'quota') {
+ info.quota = this.data;
+ }
+ }
+ },
+
+ /**
+ * Create the cookie "bubbles" for this node, recursing into children
+ * if there are any. Append the cookie bubbles to @{code item}.
+ * @param {CookieListItem} item The cookie list item to create items in.
+ */
+ createItems: function(item) {
+ if (this.children.length > 0) {
+ for (var i = 0; i < this.children.length; ++i)
+ this.children[i].createItems(item);
+ } else if (this.data && !this.data.hasChildren) {
+ var text = '';
+ switch (this.data.type) {
+ case 'cookie':
+ case 'database':
+ text = this.data.name;
+ break;
+ case 'local_storage':
+ text = localStrings.getString('cookie_local_storage');
+ break;
+ case 'app_cache':
+ text = localStrings.getString('cookie_app_cache');
+ break;
+ case 'indexed_db':
+ text = localStrings.getString('cookie_indexed_db');
+ break;
+ case 'file_system':
+ text = localStrings.getString('cookie_file_system');
+ break;
+ }
+ if (!text)
+ return;
+ var div = item.ownerDocument.createElement('div');
+ div.className = 'cookie-item';
+ // Help out screen readers and such: this is a clickable thing.
+ div.setAttribute('role', 'button');
+ div.textContent = text;
+ var index = item.appendItem(this, div);
+ div.onclick = function() {
+ if (item.selectedIndex == index)
+ item.selectedIndex = -1;
+ else
+ item.selectedIndex = index;
+ };
+ }
+ },
+
+ /**
+ * Set the detail text to be displayed to that of this cookie tree node.
+ * Uses preallocated DOM elements for each cookie node type from @{code
+ * infoNodes}, and inserts the appropriate elements to @{code element}.
+ * @param {Element} element The DOM element to insert elements to.
+ * @param {Object.<string, {table: Element, info: Object.<string,
+ * Element>}>} infoNodes The map from cookie node types to maps from
+ * cookie attribute names to DOM elements to display cookie attribute
+ * values, created by @{code CookiesList.decorate}.
+ */
+ setDetailText: function(element, infoNodes) {
+ var table;
+ if (this.data && !this.data.hasChildren) {
+ if (cookieInfo[this.data.type]) {
+ var info = cookieInfo[this.data.type];
+ var nodes = infoNodes[this.data.type].info;
+ for (var i = 0; i < info.length; ++i) {
+ var name = info[i][0];
+ if (name != 'id' && this.data[name])
+ nodes[name].textContent = this.data[name];
+ }
+ table = infoNodes[this.data.type].table;
+ }
+ }
+ while (element.childNodes.length > 1)
+ element.removeChild(element.firstChild);
+ if (table)
+ element.insertBefore(table, element.firstChild);
+ },
+
+ /**
+ * The parent of this cookie tree node.
+ * @type {?CookieTreeNode|CookieListItem}
+ */
+ get parent(parent) {
+ // See below for an explanation of this special case.
+ if (typeof this.parent_ == 'number')
+ return this.list_.getListItemByIndex(this.parent_);
+ return this.parent_;
+ },
+ set parent(parent) {
+ if (parent == this.parent)
+ return;
+ if (parent instanceof CookieListItem) {
+ // If the parent is to be a CookieListItem, then we keep the reference
+ // to it by its containing list and list index, rather than directly.
+ // This allows the list items to be garbage collected when they scroll
+ // out of view (except the expanded item, which we cache). This is
+ // transparent except in the setter and getter, where we handle it.
+ this.parent_ = parent.listIndex;
+ this.list_ = parent.list;
+ parent.addEventListener('listIndexChange',
+ this.parentIndexChanged_.bind(this));
+ } else {
+ this.parent_ = parent;
+ }
+ if (this.data && this.data.id) {
+ if (parent)
+ parentLookup[this.data.id] = this;
+ else
+ delete parentLookup[this.data.id];
+ }
+ if (this.data && this.data.hasChildren &&
+ !this.children.length && !lookupRequests[this.data.id]) {
+ lookupRequests[this.data.id] = true;
+ chrome.send('loadCookie', [this.pathId]);
+ }
+ },
+
+ /**
+ * Called when the parent is a CookieListItem whose index has changed.
+ * See the code above that avoids keeping a direct reference to
+ * CookieListItem parents, to allow them to be garbage collected.
+ * @private
+ */
+ parentIndexChanged_: function(event) {
+ if (typeof this.parent_ == 'number') {
+ this.parent_ = event.newValue;
+ // We set a timeout to update the origin, rather than doing it right
+ // away, because this callback may occur while the list items are
+ // being repopulated following a scroll event. Calling updateOrigin()
+ // immediately could trigger relayout that would reset the scroll
+ // position within the list, among other things.
+ window.setTimeout(this.updateOrigin.bind(this), 0);
+ }
+ },
+
+ /**
+ * The cookie tree path id.
+ * @type {string}
+ */
+ get pathId() {
+ var parent = this.parent;
+ if (parent && parent instanceof CookieTreeNode)
+ return parent.pathId + ',' + this.data.id;
+ return this.data.id;
+ },
+ };
+
+ /**
+ * Creates a new cookies list.
+ * @param {Object=} opt_propertyBag Optional properties.
+ * @constructor
+ * @extends {DeletableItemList}
+ */
+ var CookiesList = cr.ui.define('list');
+
+ CookiesList.prototype = {
+ __proto__: DeletableItemList.prototype,
+
+ /** @inheritDoc */
+ decorate: function() {
+ DeletableItemList.prototype.decorate.call(this);
+ this.classList.add('cookie-list');
+ this.data_ = [];
+ this.dataModel = new ArrayDataModel(this.data_);
+ this.addEventListener('keydown', this.handleKeyLeftRight_.bind(this));
+ var sm = new ListSingleSelectionModel();
+ sm.addEventListener('change', this.cookieSelectionChange_.bind(this));
+ sm.addEventListener('leadIndexChange', this.cookieLeadChange_.bind(this));
+ this.selectionModel = sm;
+ this.infoNodes = {};
+ this.fixedHeight = false;
+ var doc = this.ownerDocument;
+ // Create a table for each type of site data (e.g. cookies, databases,
+ // etc.) and save it so that we can reuse it for all origins.
+ for (var type in cookieInfo) {
+ var table = doc.createElement('table');
+ table.className = 'cookie-details-table';
+ var tbody = doc.createElement('tbody');
+ table.appendChild(tbody);
+ var info = {};
+ for (var i = 0; i < cookieInfo[type].length; i++) {
+ var tr = doc.createElement('tr');
+ var name = doc.createElement('td');
+ var data = doc.createElement('td');
+ var pair = cookieInfo[type][i];
+ name.className = 'cookie-details-label';
+ name.textContent = localStrings.getString(pair[1]);
+ data.className = 'cookie-details-value';
+ data.textContent = '';
+ tr.appendChild(name);
+ tr.appendChild(data);
+ tbody.appendChild(tr);
+ info[pair[0]] = data;
+ }
+ this.infoNodes[type] = {table: table, info: info};
+ }
+ },
+
+ /**
+ * Handles key down events and looks for left and right arrows, then
+ * dispatches to the currently expanded item, if any.
+ * @param {Event} e The keydown event.
+ * @private
+ */
+ handleKeyLeftRight_: function(e) {
+ var id = e.keyIdentifier;
+ if ((id == 'Left' || id == 'Right') && this.expandedItem) {
+ var cs = this.ownerDocument.defaultView.getComputedStyle(this);
+ var rtl = cs.direction == 'rtl';
+ if ((!rtl && id == 'Left') || (rtl && id == 'Right'))
+ this.expandedItem.selectedIndex--;
+ else
+ this.expandedItem.selectedIndex++;
+ this.scrollIndexIntoView(this.expandedItem.listIndex);
+ // Prevent the page itself from scrolling.
+ e.preventDefault();
+ }
+ },
+
+ /**
+ * Called on selection model selection changes.
+ * @param {Event} ce The selection change event.
+ * @private
+ */
+ cookieSelectionChange_: function(ce) {
+ ce.changes.forEach(function(change) {
+ var listItem = this.getListItemByIndex(change.index);
+ if (listItem) {
+ if (!change.selected) {
+ // We set a timeout here, rather than setting the item unexpanded
+ // immediately, so that if another item gets set expanded right
+ // away, it will be expanded before this item is unexpanded. It
+ // will notice that, and unexpand this item in sync with its own
+ // expansion. Later, this callback will end up having no effect.
+ window.setTimeout(function() {
+ if (!listItem.selected || !listItem.lead)
+ listItem.expanded = false;
+ }, 0);
+ } else if (listItem.lead) {
+ listItem.expanded = true;
+ }
+ }
+ }, this);
+ },
+
+ /**
+ * Called on selection model lead changes.
+ * @param {Event} pe The lead change event.
+ * @private
+ */
+ cookieLeadChange_: function(pe) {
+ if (pe.oldValue != -1) {
+ var listItem = this.getListItemByIndex(pe.oldValue);
+ if (listItem) {
+ // See cookieSelectionChange_ above for why we use a timeout here.
+ window.setTimeout(function() {
+ if (!listItem.lead || !listItem.selected)
+ listItem.expanded = false;
+ }, 0);
+ }
+ }
+ if (pe.newValue != -1) {
+ var listItem = this.getListItemByIndex(pe.newValue);
+ if (listItem && listItem.selected)
+ listItem.expanded = true;
+ }
+ },
+
+ /**
+ * The currently expanded item. Used by CookieListItem above.
+ * @type {?CookieListItem}
+ */
+ expandedItem: null,
+
+ // from cr.ui.List
+ /** @inheritDoc */
+ createItem: function(data) {
+ // We use the cached expanded item in order to allow it to maintain some
+ // state (like its fixed height, and which bubble is selected).
+ if (this.expandedItem && this.expandedItem.origin == data)
+ return this.expandedItem;
+ return new CookieListItem(data, this);
+ },
+
+ // from options.DeletableItemList
+ /** @inheritDoc */
+ deleteItemAtIndex: function(index) {
+ var item = this.data_[index];
+ if (item) {
+ var pathId = item.pathId;
+ if (pathId)
+ chrome.send('removeCookie', [pathId]);
+ }
+ },
+
+ /**
+ * Insert the given list of cookie tree nodes at the given index.
+ * Both CookiesList and CookieTreeNode implement this API.
+ * @param {Array.<Object>} data The data objects for the nodes to add.
+ * @param {number} start The index at which to start inserting the nodes.
+ */
+ insertAt: function(data, start) {
+ spliceTreeNodes(data, start, this.dataModel);
+ },
+
+ /**
+ * Remove a cookie tree node from the given index.
+ * Both CookiesList and CookieTreeNode implement this API.
+ * @param {number} index The index of the tree node to remove.
+ */
+ remove: function(index) {
+ if (index < this.data_.length)
+ this.dataModel.splice(index, 1);
+ },
+
+ /**
+ * Clears the list.
+ * Both CookiesList and CookieTreeNode implement this API.
+ * It is used by CookiesList.loadChildren().
+ */
+ clear: function() {
+ parentLookup = {};
+ this.data_ = [];
+ this.dataModel = new ArrayDataModel(this.data_);
+ this.redraw();
+ },
+
+ /**
+ * Add tree nodes by given parent.
+ * @param {Object} parent The parent node.
+ * @param {number} start The index at which to start inserting the nodes.
+ * @param {Array} nodesData Nodes data array.
+ * @private
+ */
+ addByParent_: function(parent, start, nodesData) {
+ if (!parent)
+ return;
+
+ parent.startBatchUpdates();
+ parent.insertAt(nodesData, start);
+ parent.endBatchUpdates();
+
+ cr.dispatchSimpleEvent(this, 'change');
+ },
+
+ /**
+ * Add tree nodes by parent id.
+ * This is used by cookies_view.js.
+ * @param {string} parentId Id of the parent node.
+ * @param {number} start The index at which to start inserting the nodes.
+ * @param {Array} nodesData Nodes data array.
+ */
+ addByParentId: function(parentId, start, nodesData) {
+ var parent = parentId ? parentLookup[parentId] : this;
+ this.addByParent_(parent, start, nodesData);
+ },
+
+ /**
+ * Removes tree nodes by parent id.
+ * This is used by cookies_view.js.
+ * @param {string} parentId Id of the parent node.
+ * @param {number} start The index at which to start removing the nodes.
+ * @param {number} count Number of nodes to remove.
+ */
+ removeByParentId: function(parentId, start, count) {
+ var parent = parentId ? parentLookup[parentId] : this;
+ if (!parent)
+ return;
+
+ parent.startBatchUpdates();
+ while (count-- > 0)
+ parent.remove(start);
+ parent.endBatchUpdates();
+
+ cr.dispatchSimpleEvent(this, 'change');
+ },
+
+ /**
+ * Loads the immediate children of given parent node.
+ * This is used by cookies_view.js.
+ * @param {string} parentId Id of the parent node.
+ * @param {Array} children The immediate children of parent node.
+ */
+ loadChildren: function(parentId, children) {
+ if (parentId)
+ delete lookupRequests[parentId];
+ var parent = parentId ? parentLookup[parentId] : this;
+ if (!parent)
+ return;
+
+ parent.startBatchUpdates();
+ parent.clear();
+ this.addByParent_(parent, 0, children);
+ parent.endBatchUpdates();
+ },
+ };
+
+ return {
+ CookiesList: CookiesList
+ };
+});
« no previous file with comments | « chrome/browser/resources/options2/controlled_setting.js ('k') | chrome/browser/resources/options2/cookies_view.css » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698