summaryrefslogtreecommitdiffstats
path: root/comm/mail/components/preferences/cookies.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mail/components/preferences/cookies.js')
-rw-r--r--comm/mail/components/preferences/cookies.js993
1 files changed, 993 insertions, 0 deletions
diff --git a/comm/mail/components/preferences/cookies.js b/comm/mail/components/preferences/cookies.js
new file mode 100644
index 0000000000..da06eb7e5a
--- /dev/null
+++ b/comm/mail/components/preferences/cookies.js
@@ -0,0 +1,993 @@
+/* -*- Mode: Javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var { PluralForm } = ChromeUtils.importESModule(
+ "resource://gre/modules/PluralForm.sys.mjs"
+);
+ChromeUtils.defineESModuleGetters(this, {
+ ContextualIdentityService:
+ "resource://gre/modules/ContextualIdentityService.sys.mjs",
+});
+
+var gCookiesWindow = {
+ _hosts: {},
+ _hostOrder: [],
+ _tree: null,
+ _bundle: null,
+
+ init() {
+ Services.obs.addObserver(this, "cookie-changed");
+ Services.obs.addObserver(this, "perm-changed");
+
+ this._bundle = document.getElementById("bundlePreferences");
+ this._tree = document.getElementById("cookiesList");
+
+ this._populateList(true);
+
+ document.getElementById("filter").focus();
+
+ if (!Services.prefs.getBoolPref("privacy.userContext.enabled")) {
+ document.getElementById("userContext").hidden = true;
+ document.getElementById("userContextLabel").hidden = true;
+ }
+ },
+
+ uninit() {
+ Services.obs.removeObserver(this, "cookie-changed");
+ Services.obs.removeObserver(this, "perm-changed");
+ },
+
+ _populateList(aInitialLoad) {
+ this._loadCookies();
+ this._tree.view = this._view;
+ if (aInitialLoad) {
+ this.sort("rawHost");
+ }
+ if (this._view.rowCount > 0) {
+ this._tree.view.selection.select(0);
+ }
+
+ if (aInitialLoad) {
+ if (
+ "arguments" in window &&
+ window.arguments[2] &&
+ window.arguments[2].filterString
+ ) {
+ this.setFilter(window.arguments[2].filterString);
+ }
+ } else if (document.getElementById("filter").value != "") {
+ this.filter();
+ }
+
+ this._saveState();
+ },
+
+ _cookieEquals(aCookieA, aCookieB, aStrippedHost) {
+ return (
+ aCookieA.rawHost == aStrippedHost &&
+ aCookieA.name == aCookieB.name &&
+ aCookieA.path == aCookieB.path &&
+ ChromeUtils.isOriginAttributesEqual(
+ aCookieA.originAttributes,
+ aCookieB.originAttributes
+ )
+ );
+ },
+
+ observe(aCookie, aTopic, aData) {
+ if (aTopic != "cookie-changed") {
+ return;
+ }
+
+ if (aCookie instanceof Ci.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.rowCountChanged(0, -oldRowCount);
+ this._view.selection.clearSelection();
+ } else if (aData == "reload") {
+ // first, clear any existing entries
+ this.observe(aCookie, aTopic, "cleared");
+
+ // then, reload the list
+ this._populateList(false);
+ }
+
+ // We don't yet handle aData == "deleted" - it's a less common case
+ // and is rather complicated as selection tracking is difficult
+ },
+
+ _handleCookieChanged(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.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(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 because 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.rowCountChanged(oldRowCount - 1, rowCountImpact);
+
+ document.getElementById("removeAllCookies").disabled = this._view._filtered;
+ },
+
+ _view: {
+ QueryInterface: ChromeUtils.generateQI(["nsITreeView"]),
+ _filtered: false,
+ _filterSet: [],
+ _filterValue: "",
+ _rowCount: 0,
+ _cacheValid: 0,
+ _cacheItems: [],
+ get rowCount() {
+ return this._rowCount;
+ },
+
+ _getItemAtIndex(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 (let i = start; i < gCookiesWindow._hostOrder.length; ++i) {
+ let currHost = gCookiesWindow._hosts[gCookiesWindow._hostOrder[i]];
+ if (!currHost) {
+ continue;
+ }
+ if (count == aIndex) {
+ return currHost;
+ }
+ hostIndex = count;
+
+ let cacheEntry = { start: i, 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 (let j = 0; j < currHost.cookies.length; ++j) {
+ if (count == aIndex) {
+ let cookie = currHost.cookies[j];
+ 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 (let j = cacheStart; j < count; j++) {
+ this._cacheItems[j] = cacheEntry;
+ }
+ this._cacheValid = count - 1;
+ }
+ return null;
+ },
+
+ _removeItemAtIndex(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 (let i = aIndex; i < aIndex + removeCount; ++i) {
+ let item = this._filterSet[i];
+ let 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;
+ }
+
+ let item = this._getItemAtIndex(aIndex);
+ if (!item) {
+ return;
+ }
+ this._invalidateCache(aIndex - 1);
+ if (item.container) {
+ gCookiesWindow._hosts[item.rawHost] = null;
+ } else {
+ let parent = this._getItemAtIndex(item.parentIndex);
+ for (let 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 &&
+ ChromeUtils.isOriginAttributesEqual(
+ item.originAttributes,
+ cookie.originAttributes
+ )
+ ) {
+ parent.cookies.splice(i, removeCount);
+ }
+ }
+ }
+ },
+
+ _invalidateCache(aIndex) {
+ this._cacheValid = Math.min(this._cacheValid, aIndex);
+ },
+
+ getCellText(aIndex, aColumn) {
+ if (!this._filtered) {
+ var item = this._getItemAtIndex(aIndex);
+ if (!item) {
+ return "";
+ }
+ if (aColumn.id == "domainCol") {
+ return item.rawHost;
+ }
+ if (aColumn.id == "nameCol") {
+ return "name" in item ? item.name : "";
+ }
+ } else if (aColumn.id == "domainCol") {
+ return this._filterSet[aIndex].rawHost;
+ } else if (aColumn.id == "nameCol") {
+ return "name" in this._filterSet[aIndex]
+ ? this._filterSet[aIndex].name
+ : "";
+ }
+ return "";
+ },
+
+ _selection: null,
+ get selection() {
+ return this._selection;
+ },
+ set selection(val) {
+ this._selection = val;
+ },
+ getRowProperties(aRow) {
+ return "";
+ },
+ getCellProperties(aRow, aColumn) {
+ return "";
+ },
+ getColumnProperties(aColumn) {
+ return "";
+ },
+ isContainer(aIndex) {
+ if (!this._filtered) {
+ var item = this._getItemAtIndex(aIndex);
+ if (!item) {
+ return false;
+ }
+ return item.container;
+ }
+ return false;
+ },
+ isContainerOpen(aIndex) {
+ if (!this._filtered) {
+ var item = this._getItemAtIndex(aIndex);
+ if (!item) {
+ return false;
+ }
+ return item.open;
+ }
+ return false;
+ },
+ isContainerEmpty(aIndex) {
+ if (!this._filtered) {
+ var item = this._getItemAtIndex(aIndex);
+ if (!item) {
+ return false;
+ }
+ return item.cookies.length == 0;
+ }
+ return false;
+ },
+ isSeparator(aIndex) {
+ return false;
+ },
+ isSorted(aIndex) {
+ return false;
+ },
+ canDrop(aIndex, aOrientation) {
+ return false;
+ },
+ drop(aIndex, aOrientation) {},
+ getParentIndex(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(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;
+ }
+ let parent = this._getItemAtIndex(item.parentIndex);
+ if (parent && parent.container) {
+ return aIndex < item.parentIndex + parent.cookies.length;
+ }
+ }
+ }
+ return aIndex < this.rowCount - 1;
+ },
+ hasPreviousSibling(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(aIndex) {
+ if (!this._filtered) {
+ var item = this._getItemAtIndex(aIndex);
+ if (!item) {
+ return 0;
+ }
+ return item.level;
+ }
+ return 0;
+ },
+ getImageSrc(aIndex, aColumn) {},
+ getProgressMode(aIndex, aColumn) {},
+ getCellValue(aIndex, aColumn) {},
+ setTree(aTree) {},
+ toggleOpenState(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.rowCountChanged(aIndex + 1, delta);
+ gCookiesWindow._tree.invalidateRow(aIndex);
+ }
+ },
+ cycleHeader(aColumn) {},
+ selectionChanged() {},
+ cycleCell(aIndex, aColumn) {},
+ isEditable(aIndex, aColumn) {
+ return false;
+ },
+ setCellValue(aIndex, aColumn, aValue) {},
+ setCellText(aIndex, aColumn, aValue) {},
+ },
+
+ _makeStrippedHost(aHost) {
+ let formattedHost = aHost.startsWith(".")
+ ? aHost.substring(1, aHost.length)
+ : aHost;
+ return formattedHost.startsWith("www.")
+ ? formattedHost.substring(4, formattedHost.length)
+ : formattedHost;
+ },
+
+ _addCookie(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(aStrippedHost, aCookie) {
+ let 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,
+ originAttributes: aCookie.originAttributes,
+ };
+ return c;
+ },
+
+ _loadCookies() {
+ var hostCount = { value: 0 };
+ this._hosts = {};
+ this._hostOrder = [];
+ for (let cookie of Services.cookies.cookies) {
+ var strippedHost = this._makeStrippedHost(cookie.host);
+ this._addCookie(strippedHost, cookie, hostCount);
+ }
+ this._view._rowCount = hostCount.value;
+ },
+
+ formatExpiresString(aExpires) {
+ if (aExpires) {
+ var date = new Date(1000 * aExpires);
+ const dateTimeFormatter = new Services.intl.DateTimeFormat(undefined, {
+ dateStyle: "long",
+ timeStyle: "long",
+ });
+ return dateTimeFormatter.format(date);
+ }
+ return this._bundle.getString("expireAtEndOfSession");
+ },
+
+ _getUserContextString(aUserContextId) {
+ if (parseInt(aUserContextId, 10) == 0) {
+ return this._bundle.getString("defaultUserContextLabel");
+ }
+
+ return ContextualIdentityService.getUserContextLabel(aUserContextId);
+ },
+
+ _updateCookieData(aItem) {
+ var seln = this._view.selection;
+ var ids = [
+ "name",
+ "value",
+ "host",
+ "path",
+ "isSecure",
+ "expires",
+ "userContext",
+ ];
+ 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"),
+ userContext: this._getUserContextString(
+ aItem.originAttributes.userContextId
+ ),
+ };
+ 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,
+ userContext: 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() {
+ var 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;
+ }
+ }
+ }
+ item = this._view._getItemAtIndex(seln.currentIndex);
+ if (item && seln.count == 1 && item.container && item.open) {
+ selectedCookieCount += 2;
+ }
+
+ let buttonLabel = this._bundle.getString("removeSelectedCookies");
+ let removeSelectedCookies = document.getElementById(
+ "removeSelectedCookies"
+ );
+ removeSelectedCookies.label = PluralForm.get(
+ selectedCookieCount,
+ buttonLabel
+ ).replace("#1", selectedCookieCount);
+
+ removeSelectedCookies.disabled = !(seln.count > 0);
+ document.getElementById("removeAllCookies").disabled = this._view._filtered;
+ },
+
+ deleteCookie() {
+ // 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;
+
+ 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;
+ let 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);
+ }
+ }
+
+ for (let item of deleteItems) {
+ Services.cookies.remove(
+ item.host,
+ item.name,
+ item.path,
+ item.originAttributes
+ );
+ }
+
+ if (nextSelected < 0) {
+ seln.clearSelection();
+ } else {
+ seln.select(nextSelected);
+ this._tree.focus();
+ }
+ },
+
+ deleteAllCookies() {
+ Services.cookies.removeAll();
+ this._tree.focus();
+ },
+
+ onCookieKeyPress(aEvent) {
+ if (aEvent.keyCode == 46) {
+ this.deleteCookie();
+ }
+ },
+
+ _lastSortProperty: "",
+ _lastSortAscending: false,
+ sort(aProperty) {
+ var ascending =
+ aProperty == this._lastSortProperty ? !this._lastSortAscending : true;
+
+ function sortByHost(a, b) {
+ return a.toLowerCase().localeCompare(b.toLowerCase());
+ }
+
+ // Sort the Non-Filtered Host Collections
+ if (aProperty == "rawHost") {
+ 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.invalidate();
+ this._tree.ensureRowIsVisible(0);
+
+ this._lastSortAscending = ascending;
+ this._lastSortProperty = aProperty;
+ },
+
+ clearFilter() {
+ // Revert to single-select in the tree
+ this._tree.setAttribute("seltype", "single");
+
+ // Clear the Tree Display
+ this._view._filtered = false;
+ this._view._rowCount = 0;
+ this._tree.rowCountChanged(0, -this._view._filterSet.length);
+ this._view._filterSet = [];
+
+ // Just reload the list to make sure deletions are respected
+ this._loadCookies();
+ this._tree.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");
+ },
+
+ _cookieMatchesFilter(aCookie) {
+ return (
+ aCookie.rawHost.includes(this._view._filterValue) ||
+ aCookie.name.includes(this._view._filterValue) ||
+ aCookie.value.includes(this._view._filterValue)
+ );
+ },
+
+ _filterCookies(aFilterValue) {
+ this._view._filterValue = aFilterValue;
+ var cookies = [];
+ for (let i = 0; i < gCookiesWindow._hostOrder.length; ++i) {
+ let currHost = gCookiesWindow._hosts[gCookiesWindow._hostOrder[i]];
+ 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() {
+ // 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);
+ }
+ }
+ },
+
+ filter() {
+ 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.rowCountChanged(0, -oldCount);
+ // Set up the filtered display
+ view._rowCount = view._filterSet.length;
+ gCookiesWindow._tree.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");
+ },
+
+ setFilter(aFilterString) {
+ document.getElementById("filter").value = aFilterString;
+ this.filter();
+ },
+
+ focusFilterBox() {
+ var filter = document.getElementById("filter");
+ filter.focus();
+ filter.select();
+ },
+};