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