# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is the Firefox Preferences System. # # The Initial Developer of the Original Code is # Ben Goodger. # Portions created by the Initial Developer are Copyright (C) 2005 # the Initial Developer. All Rights Reserved. # # Contributor(s): # Ben Goodger # Ehsan Akhgari # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** const nsICookie = Components.interfaces.nsICookie; var gCookiesWindow = { _cm : Components.classes["@mozilla.org/cookiemanager;1"] .getService(Components.interfaces.nsICookieManager), _ds : Components.classes["@mozilla.org/intl/scriptabledateformat;1"] .getService(Components.interfaces.nsIScriptableDateFormat), _hosts : {}, _hostOrder : [], _tree : null, _bundle : null, init: function () { var os = Components.classes["@mozilla.org/observer-service;1"] .getService(Components.interfaces.nsIObserverService); os.addObserver(this, "cookie-changed", false); os.addObserver(this, "perm-changed", false); this._bundle = document.getElementById("bundlePreferences"); this._tree = document.getElementById("cookiesList"); this._loadCookies(); this._tree.treeBoxObject.view = this._view; this.sort("rawHost"); if (this._view.rowCount > 0) this._tree.view.selection.select(0); if ("arguments" in window && window.arguments[0] && window.arguments[0].filterString) this.setFilter(window.arguments[0].filterString); this._saveState(); document.getElementById("filter").focus(); }, uninit: function () { var os = Components.classes["@mozilla.org/observer-service;1"] .getService(Components.interfaces.nsIObserverService); os.removeObserver(this, "cookie-changed"); os.removeObserver(this, "perm-changed"); }, _cookieEquals: function (aCookieA, aCookieB, aStrippedHost) { return aCookieA.rawHost == aStrippedHost && aCookieA.name == aCookieB.name && aCookieA.path == aCookieB.path; }, observe: function (aCookie, aTopic, aData) { if (aTopic != "cookie-changed") return; if (aCookie instanceof Components.interfaces.nsICookie) { var strippedHost = this._makeStrippedHost(aCookie.host); if (aData == "changed") this._handleCookieChanged(aCookie, strippedHost); else if (aData == "added") this._handleCookieAdded(aCookie, strippedHost); } else if (aData == "cleared") { this._hosts = {}; this._hostOrder = []; var oldRowCount = this._view._rowCount; this._view._rowCount = 0; this._tree.treeBoxObject.rowCountChanged(0, -oldRowCount); this._view.selection.clearSelection(); } // We don't yet handle aData == "deleted" - it's a less common case // and is rather complicated as selection tracking is difficult }, _handleCookieChanged: function (changedCookie, strippedHost) { var rowIndex = 0; var cookieItem = null; if (!this._view._filtered) { for (var i = 0; i < this._hostOrder.length; ++i) { // (var host in this._hosts) { ++rowIndex; var hostItem = this._hosts[this._hostOrder[i]]; // var hostItem = this._hosts[host]; if (this._hostOrder[i] == strippedHost) { // host == strippedHost) { // Host matches, look for the cookie within this Host collection // and update its data for (var j = 0; j < hostItem.cookies.length; ++j) { ++rowIndex; var currCookie = hostItem.cookies[j]; if (this._cookieEquals(currCookie, changedCookie, strippedHost)) { currCookie.value = changedCookie.value; currCookie.isSecure = changedCookie.isSecure; currCookie.isDomain = changedCookie.isDomain; currCookie.expires = changedCookie.expires; cookieItem = currCookie; break; } } } else if (hostItem.open) rowIndex += hostItem.cookies.length; } } else { // Just walk the filter list to find the item. It doesn't matter that // we don't update the main Host collection when we do this, because // when the filter is reset the Host collection is rebuilt anyway. for (rowIndex = 0; rowIndex < this._view._filterSet.length; ++rowIndex) { currCookie = this._view._filterSet[rowIndex]; if (this._cookieEquals(currCookie, changedCookie, strippedHost)) { currCookie.value = changedCookie.value; currCookie.isSecure = changedCookie.isSecure; currCookie.isDomain = changedCookie.isDomain; currCookie.expires = changedCookie.expires; cookieItem = currCookie; break; } } } // Make sure the tree display is up to date... this._tree.treeBoxObject.invalidateRow(rowIndex); // ... and if the cookie is selected, update the displayed metadata too if (cookieItem != null && this._view.selection.currentIndex == rowIndex) this._updateCookieData(cookieItem); }, _handleCookieAdded: function (changedCookie, strippedHost) { var rowCountImpact = 0; var addedHost = { value: 0 }; this._addCookie(strippedHost, changedCookie, addedHost); if (!this._view._filtered) { // The Host collection for this cookie already exists, and it's not open, // so don't increment the rowCountImpact becaues the user is not going to // see the additional rows as they're hidden. if (addedHost.value || this._hosts[strippedHost].open) ++rowCountImpact; } else { // We're in search mode, and the cookie being added matches // the search condition, so add it to the list. var c = this._makeCookieObject(strippedHost, changedCookie); if (this._cookieMatchesFilter(c)) { this._view._filterSet.push(this._makeCookieObject(strippedHost, changedCookie)); ++rowCountImpact; } } // Now update the tree display at the end (we could/should re run the sort // if any to get the position correct.) var oldRowCount = this._rowCount; this._view._rowCount += rowCountImpact; this._tree.treeBoxObject.rowCountChanged(oldRowCount - 1, rowCountImpact); document.getElementById("removeAllCookies").disabled = this._view._filtered; }, _view: { _filtered : false, _filterSet : [], _filterValue: "", _rowCount : 0, _cacheValid : 0, _cacheItems : [], get rowCount() { return this._rowCount; }, _getItemAtIndex: function (aIndex) { if (this._filtered) return this._filterSet[aIndex]; var start = 0; var count = 0, hostIndex = 0; var cacheIndex = Math.min(this._cacheValid, aIndex); if (cacheIndex > 0) { var cacheItem = this._cacheItems[cacheIndex]; start = cacheItem['start']; count = hostIndex = cacheItem['count']; } for (var i = start; i < gCookiesWindow._hostOrder.length; ++i) { // var host in gCookiesWindow._hosts) { var currHost = gCookiesWindow._hosts[gCookiesWindow._hostOrder[i]]; // gCookiesWindow._hosts[host]; if (!currHost) continue; if (count == aIndex) return currHost; hostIndex = count; var cacheEntry = { 'start' : i, 'count' : count }; var cacheStart = count; if (currHost.open) { if (count < aIndex && aIndex <= (count + currHost.cookies.length)) { // We are looking for an entry within this host's children, // enumerate them looking for the index. ++count; for (var i = 0; i < currHost.cookies.length; ++i) { if (count == aIndex) { var cookie = currHost.cookies[i]; cookie.parentIndex = hostIndex; return cookie; } ++count; } } else { // A host entry was open, but we weren't looking for an index // within that host entry's children, so skip forward over the // entry's children. We need to add one to increment for the // host value too. count += currHost.cookies.length + 1; } } else ++count; for (var j = cacheStart; j < count; j++) this._cacheItems[j] = cacheEntry; this._cacheValid = count - 1; } return null; }, _removeItemAtIndex: function (aIndex, aCount) { var removeCount = aCount === undefined ? 1 : aCount; if (this._filtered) { // remove the cookies from the unfiltered set so that they // don't reappear when the filter is changed. See bug 410863. for (var i = aIndex; i < aIndex + removeCount; ++i) { var item = this._filterSet[i]; var parent = gCookiesWindow._hosts[item.rawHost]; for (var j = 0; j < parent.cookies.length; ++j) { if (item == parent.cookies[j]) { parent.cookies.splice(j, 1); break; } } } this._filterSet.splice(aIndex, removeCount); return; } var item = this._getItemAtIndex(aIndex); if (!item) return; this._invalidateCache(aIndex - 1); if (item.container) gCookiesWindow._hosts[item.rawHost] = null; else { var parent = this._getItemAtIndex(item.parentIndex); for (var i = 0; i < parent.cookies.length; ++i) { var cookie = parent.cookies[i]; if (item.rawHost == cookie.rawHost && item.name == cookie.name && item.path == cookie.path) parent.cookies.splice(i, removeCount); } } }, _invalidateCache: function (aIndex) { this._cacheValid = Math.min(this._cacheValid, aIndex); }, getCellText: function (aIndex, aColumn) { if (!this._filtered) { var item = this._getItemAtIndex(aIndex); if (!item) return ""; if (aColumn.id == "domainCol") return item.rawHost; else if (aColumn.id == "nameCol") return item.name; } else { if (aColumn.id == "domainCol") return this._filterSet[aIndex].rawHost; else if (aColumn.id == "nameCol") return this._filterSet[aIndex].name; } return ""; }, _selection: null, get selection () { return this._selection; }, set selection (val) { this._selection = val; return val; }, getRowProperties: function (aIndex, aProperties) {}, getCellProperties: function (aIndex, aColumn, aProperties) {}, getColumnProperties: function (aColumn, aProperties) {}, isContainer: function (aIndex) { if (!this._filtered) { var item = this._getItemAtIndex(aIndex); if (!item) return false; return item.container; } return false; }, isContainerOpen: function (aIndex) { if (!this._filtered) { var item = this._getItemAtIndex(aIndex); if (!item) return false; return item.open; } return false; }, isContainerEmpty: function (aIndex) { if (!this._filtered) { var item = this._getItemAtIndex(aIndex); if (!item) return false; return item.cookies.length == 0; } return false; }, isSeparator: function (aIndex) { return false; }, isSorted: function (aIndex) { return false; }, canDrop: function (aIndex, aOrientation) { return false; }, drop: function (aIndex, aOrientation) {}, getParentIndex: function (aIndex) { if (!this._filtered) { var item = this._getItemAtIndex(aIndex); // If an item has no parent index (i.e. it is at the top level) this // function MUST return -1 otherwise we will go into an infinite loop. // Containers are always top level items in the cookies tree, so make // sure to return the appropriate value here. if (!item || item.container) return -1; return item.parentIndex; } return -1; }, hasNextSibling: function (aParentIndex, aIndex) { if (!this._filtered) { // |aParentIndex| appears to be bogus, but we can get the real // parent index by getting the entry for |aIndex| and reading the // parentIndex field. // The index of the last item in this host collection is the // index of the parent + the size of the host collection, and // aIndex has a next sibling if it is less than this value. var item = this._getItemAtIndex(aIndex); if (item) { if (item.container) { for (var i = aIndex + 1; i < this.rowCount; ++i) { var subsequent = this._getItemAtIndex(i); if (subsequent.container) return true; } return false; } else { var parent = this._getItemAtIndex(item.parentIndex); if (parent && parent.container) return aIndex < item.parentIndex + parent.cookies.length; } } } return aIndex < this.rowCount - 1; }, hasPreviousSibling: function (aIndex) { if (!this._filtered) { var item = this._getItemAtIndex(aIndex); if (!item) return false; var parent = this._getItemAtIndex(item.parentIndex); if (parent && parent.container) return aIndex > item.parentIndex + 1; } return aIndex > 0; }, getLevel: function (aIndex) { if (!this._filtered) { var item = this._getItemAtIndex(aIndex); if (!item) return 0; return item.level; } return 0; }, getImageSrc: function (aIndex, aColumn) {}, getProgressMode: function (aIndex, aColumn) {}, getCellValue: function (aIndex, aColumn) {}, setTree: function (aTree) {}, toggleOpenState: function (aIndex) { if (!this._filtered) { var item = this._getItemAtIndex(aIndex); if (!item) return; this._invalidateCache(aIndex); var multiplier = item.open ? -1 : 1; var delta = multiplier * item.cookies.length; this._rowCount += delta; item.open = !item.open; gCookiesWindow._tree.treeBoxObject.rowCountChanged(aIndex + 1, delta); gCookiesWindow._tree.treeBoxObject.invalidateRow(aIndex); } }, cycleHeader: function (aColumn) {}, selectionChanged: function () {}, cycleCell: function (aIndex, aColumn) {}, isEditable: function (aIndex, aColumn) { return false; }, isSelectable: function (aIndex, aColumn) { return false; }, setCellValue: function (aIndex, aColumn, aValue) {}, setCellText: function (aIndex, aColumn, aValue) {}, performAction: function (aAction) {}, performActionOnRow: function (aAction, aIndex) {}, performActionOnCell: function (aAction, aindex, aColumn) {} }, _makeStrippedHost: function (aHost) { var formattedHost = aHost.charAt(0) == "." ? aHost.substring(1, aHost.length) : aHost; return formattedHost.substring(0, 4) == "www." ? formattedHost.substring(4, formattedHost.length) : formattedHost; }, _addCookie: function (aStrippedHost, aCookie, aHostCount) { if (!(aStrippedHost in this._hosts) || !this._hosts[aStrippedHost]) { this._hosts[aStrippedHost] = { cookies : [], rawHost : aStrippedHost, level : 0, open : false, container : true }; this._hostOrder.push(aStrippedHost); ++aHostCount.value; } var c = this._makeCookieObject(aStrippedHost, aCookie); this._hosts[aStrippedHost].cookies.push(c); }, _makeCookieObject: function (aStrippedHost, aCookie) { var host = aCookie.host; var formattedHost = host.charAt(0) == "." ? host.substring(1, host.length) : host; var c = { name : aCookie.name, value : aCookie.value, isDomain : aCookie.isDomain, host : aCookie.host, rawHost : aStrippedHost, path : aCookie.path, isSecure : aCookie.isSecure, expires : aCookie.expires, level : 1, container : false }; return c; }, _loadCookies: function () { var e = this._cm.enumerator; var hostCount = { value: 0 }; this._hosts = {}; this._hostOrder = []; while (e.hasMoreElements()) { var cookie = e.getNext(); if (cookie && cookie instanceof Components.interfaces.nsICookie) { var strippedHost = this._makeStrippedHost(cookie.host); this._addCookie(strippedHost, cookie, hostCount); } else break; } this._view._rowCount = hostCount.value; }, formatExpiresString: function (aExpires) { if (aExpires) { var date = new Date(1000 * aExpires); return this._ds.FormatDateTime("", this._ds.dateFormatLong, this._ds.timeFormatSeconds, date.getFullYear(), date.getMonth() + 1, date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds()); } return this._bundle.getString("AtEndOfSession"); }, _updateCookieData: function (aItem) { var seln = this._view.selection; var ids = ["name", "value", "host", "path", "isSecure", "expires"]; var properties; if (aItem && !aItem.container && seln.count > 0) { properties = { name: aItem.name, value: aItem.value, host: aItem.host, path: aItem.path, expires: this.formatExpiresString(aItem.expires), isDomain: aItem.isDomain ? this._bundle.getString("domainColon") : this._bundle.getString("hostColon"), isSecure: aItem.isSecure ? this._bundle.getString("forSecureOnly") : this._bundle.getString("forAnyConnection") }; for (var i = 0; i < ids.length; ++i) document.getElementById(ids[i]).disabled = false; } else { var noneSelected = this._bundle.getString("noCookieSelected"); properties = { name: noneSelected, value: noneSelected, host: noneSelected, path: noneSelected, expires: noneSelected, isSecure: noneSelected }; for (i = 0; i < ids.length; ++i) document.getElementById(ids[i]).disabled = true; } for (var property in properties) document.getElementById(property).value = properties[property]; }, onCookieSelected: function () { var properties, item; var seln = this._tree.view.selection; if (!this._view._filtered) item = this._view._getItemAtIndex(seln.currentIndex); else item = this._view._filterSet[seln.currentIndex]; this._updateCookieData(item); var rangeCount = seln.getRangeCount(); var selectedCookieCount = 0; for (var i = 0; i < rangeCount; ++i) { var min = {}; var max = {}; seln.getRangeAt(i, min, max); for (var j = min.value; j <= max.value; ++j) { item = this._view._getItemAtIndex(j); if (!item) continue; if (item.container && !item.open) selectedCookieCount += item.cookies.length; else if (!item.container) ++selectedCookieCount; } } var item = this._view._getItemAtIndex(seln.currentIndex); if (item && seln.count == 1 && item.container && item.open) selectedCookieCount += 2; var stringKey = selectedCookieCount == 1 ? "removeCookie" : "removeCookies"; document.getElementById("removeCookie").label = this._bundle.getString(stringKey); document.getElementById("removeAllCookies").disabled = this._view._filtered; document.getElementById("removeCookie").disabled = !(seln.count > 0); }, deleteCookie: function () { # // Selection Notes # // - Selection always moves to *NEXT* adjacent item unless item # // is last child at a given level in which case it moves to *PREVIOUS* # // item # // # // Selection Cases (Somewhat Complicated) # // # // 1) Single cookie selected, host has single child # // v cnn.com # // //// cnn.com ///////////// goksdjf@ //// # // > atwola.com # // # // Before SelectedIndex: 1 Before RowCount: 3 # // After SelectedIndex: 0 After RowCount: 1 # // # // 2) Host selected, host open # // v goats.com //////////////////////////// # // goats.com sldkkfjl # // goat.scom flksj133 # // > atwola.com # // # // Before SelectedIndex: 0 Before RowCount: 4 # // After SelectedIndex: 0 After RowCount: 1 # // # // 3) Host selected, host closed # // > goats.com //////////////////////////// # // > atwola.com # // # // Before SelectedIndex: 0 Before RowCount: 2 # // After SelectedIndex: 0 After RowCount: 1 # // # // 4) Single cookie selected, host has many children # // v goats.com # // goats.com sldkkfjl # // //// goats.com /////////// flksjl33 //// # // > atwola.com # // # // Before SelectedIndex: 2 Before RowCount: 4 # // After SelectedIndex: 1 After RowCount: 3 # // # // 5) Single cookie selected, host has many children # // v goats.com # // //// goats.com /////////// flksjl33 //// # // goats.com sldkkfjl # // > atwola.com # // # // Before SelectedIndex: 1 Before RowCount: 4 # // After SelectedIndex: 1 After RowCount: 3 var seln = this._view.selection; var tbo = this._tree.treeBoxObject; if (seln.count < 1) return; var nextSelected = 0; var rowCountImpact = 0; var deleteItems = []; if (!this._view._filtered) { var ci = seln.currentIndex; nextSelected = ci; var invalidateRow = -1; var item = this._view._getItemAtIndex(ci); if (item.container) { rowCountImpact -= (item.open ? item.cookies.length : 0) + 1; deleteItems = deleteItems.concat(item.cookies); if (!this._view.hasNextSibling(-1, ci)) --nextSelected; this._view._removeItemAtIndex(ci); } else { var parent = this._view._getItemAtIndex(item.parentIndex); --rowCountImpact; if (parent.cookies.length == 1) { --rowCountImpact; deleteItems.push(item); if (!this._view.hasNextSibling(-1, ci)) --nextSelected; if (!this._view.hasNextSibling(-1, item.parentIndex)) --nextSelected; this._view._removeItemAtIndex(item.parentIndex); invalidateRow = item.parentIndex; } else { deleteItems.push(item); if (!this._view.hasNextSibling(-1, ci)) --nextSelected; this._view._removeItemAtIndex(ci); } } this._view._rowCount += rowCountImpact; tbo.rowCountChanged(ci, rowCountImpact); if (invalidateRow != -1) tbo.invalidateRow(invalidateRow); } else { var rangeCount = seln.getRangeCount(); for (var i = 0; i < rangeCount; ++i) { var min = {}; var max = {}; seln.getRangeAt(i, min, max); nextSelected = min.value; for (var j = min.value; j <= max.value; ++j) { deleteItems.push(this._view._getItemAtIndex(j)); if (!this._view.hasNextSibling(-1, max.value)) --nextSelected; } var delta = max.value - min.value + 1; this._view._removeItemAtIndex(min.value, delta); rowCountImpact = -1 * delta; this._view._rowCount += rowCountImpact; tbo.rowCountChanged(min.value, rowCountImpact); } } var psvc = Components.classes["@mozilla.org/preferences-service;1"] .getService(Components.interfaces.nsIPrefBranch); var blockFutureCookies = false; if (psvc.prefHasUserValue("network.cookie.blockFutureCookies")) blockFutureCookies = psvc.getBoolPref("network.cookie.blockFutureCookies"); for (i = 0; i < deleteItems.length; ++i) { var item = deleteItems[i]; this._cm.remove(item.host, item.name, item.path, blockFutureCookies); } if (nextSelected < 0) seln.clearSelection(); else { seln.select(nextSelected); this._tree.focus(); } }, deleteAllCookies: function () { this._cm.removeAll(); this._tree.focus(); }, onCookieKeyPress: function (aEvent) { if (aEvent.keyCode == 46) this.deleteCookie(); }, _lastSortProperty : "", _lastSortAscending: false, sort: function (aProperty) { var ascending = (aProperty == this._lastSortProperty) ? !this._lastSortAscending : true; // Sort the Non-Filtered Host Collections if (aProperty == "rawHost") { function sortByHost(a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); } this._hostOrder.sort(sortByHost); if (!ascending) this._hostOrder.reverse(); } function sortByProperty(a, b) { return a[aProperty].toLowerCase().localeCompare(b[aProperty].toLowerCase()); } for (var host in this._hosts) { var cookies = this._hosts[host].cookies; cookies.sort(sortByProperty); if (!ascending) cookies.reverse(); } // Sort the Filtered List, if in Filtered mode if (this._view._filtered) { this._view._filterSet.sort(sortByProperty); if (!ascending) this._view._filterSet.reverse(); } this._view._invalidateCache(0); this._view.selection.clearSelection(); this._view.selection.select(0); this._tree.treeBoxObject.invalidate(); this._tree.treeBoxObject.ensureRowIsVisible(0); this._lastSortAscending = ascending; this._lastSortProperty = aProperty; }, clearFilter: function () { // Revert to single-select in the tree this._tree.setAttribute("seltype", "single"); // Clear the Filter and the Tree Display document.getElementById("filter").value = ""; this._view._filtered = false; this._view._rowCount = 0; this._tree.treeBoxObject.rowCountChanged(0, -this._view._filterSet.length); this._view._filterSet = []; // Just reload the list to make sure deletions are respected this._loadCookies(); this._tree.treeBoxObject.view = this._view; // Restore sort order var sortby = this._lastSortProperty; if (sortby == "") { this._lastSortAscending = false; this.sort("rawHost"); } else { this._lastSortAscending = !this._lastSortAscending; this.sort(sortby); } // Restore open state for (var i = 0; i < this._openIndices.length; ++i) this._view.toggleOpenState(this._openIndices[i]); this._openIndices = []; // Restore selection this._view.selection.clearSelection(); for (i = 0; i < this._lastSelectedRanges.length; ++i) { var range = this._lastSelectedRanges[i]; this._view.selection.rangedSelect(range.min, range.max, true); } this._lastSelectedRanges = []; document.getElementById("cookiesIntro").value = this._bundle.getString("cookiesAll"); document.getElementById("clearFilter").disabled = true; document.getElementById("filter").focus(); }, _cookieMatchesFilter: function (aCookie) { return aCookie.rawHost.indexOf(this._view._filterValue) != -1 || aCookie.name.indexOf(this._view._filterValue) != -1 || aCookie.value.indexOf(this._view._filterValue) != -1; }, _filterCookies: function (aFilterValue) { this._view._filterValue = aFilterValue; var cookies = []; for (var i = 0; i < gCookiesWindow._hostOrder.length; ++i) { //var host in gCookiesWindow._hosts) { var currHost = gCookiesWindow._hosts[gCookiesWindow._hostOrder[i]]; // gCookiesWindow._hosts[host]; if (!currHost) continue; for (var j = 0; j < currHost.cookies.length; ++j) { var cookie = currHost.cookies[j]; if (this._cookieMatchesFilter(cookie)) cookies.push(cookie); } } return cookies; }, _lastSelectedRanges: [], _openIndices: [], _saveState: function () { // Save selection var seln = this._view.selection; this._lastSelectedRanges = []; var rangeCount = seln.getRangeCount(); for (var i = 0; i < rangeCount; ++i) { var min = {}; var max = {}; seln.getRangeAt(i, min, max); this._lastSelectedRanges.push({ min: min.value, max: max.value }); } // Save open states this._openIndices = []; for (i = 0; i < this._view.rowCount; ++i) { var item = this._view._getItemAtIndex(i); if (item && item.container && item.open) this._openIndices.push(i); } }, _filterTimeout: -1, onFilterInput: function () { if (this._filterTimeout != -1) clearTimeout(this._filterTimeout); function filterCookies() { var filter = document.getElementById("filter").value; if (filter == "") { gCookiesWindow.clearFilter(); return; } var view = gCookiesWindow._view; view._filterSet = gCookiesWindow._filterCookies(filter); if (!view._filtered) { // Save Display Info for the Non-Filtered mode when we first // enter Filtered mode. gCookiesWindow._saveState(); view._filtered = true; } // Move to multi-select in the tree gCookiesWindow._tree.setAttribute("seltype", "multiple"); // Clear the display var oldCount = view._rowCount; view._rowCount = 0; gCookiesWindow._tree.treeBoxObject.rowCountChanged(0, -oldCount); // Set up the filtered display view._rowCount = view._filterSet.length; gCookiesWindow._tree.treeBoxObject.rowCountChanged(0, view.rowCount); // if the view is not empty then select the first item if (view.rowCount > 0) view.selection.select(0); document.getElementById("cookiesIntro").value = gCookiesWindow._bundle.getString("cookiesFiltered"); document.getElementById("clearFilter").disabled = false; } window.filterCookies = filterCookies; this._filterTimeout = setTimeout("filterCookies();", 500); }, onFilterKeyPress: function (aEvent) { var filter = document.getElementById("filter").value; if (aEvent.keyCode == 27 && filter != "") // ESC key this.clearFilter(); }, focusFilterBox: function () { var filter = document.getElementById("filter"); filter.focus(); filter.select(); }, setFilter: function (aFilterString) { document.getElementById("filter").value = aFilterString; this.onFilterInput(); } };