Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / browser / components / preferences / cookies.js
blob4e025a60f806d9401a756828d5763cbc9bc62283
1 # -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 # ***** BEGIN LICENSE BLOCK *****
3 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 # The contents of this file are subject to the Mozilla Public License Version
6 # 1.1 (the "License"); you may not use this file except in compliance with
7 # the License. You may obtain a copy of the License at
8 # http://www.mozilla.org/MPL/
10 # Software distributed under the License is distributed on an "AS IS" basis,
11 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 # for the specific language governing rights and limitations under the
13 # License.
15 # The Original Code is the Firefox Preferences System.
17 # The Initial Developer of the Original Code is
18 # Ben Goodger.
19 # Portions created by the Initial Developer are Copyright (C) 2005
20 # the Initial Developer. All Rights Reserved.
22 # Contributor(s):
23 # Ben Goodger <ben@mozilla.org>
24 # Ehsan Akhgari <ehsan.akhgari@gmail.com>
26 # Alternatively, the contents of this file may be used under the terms of
27 # either the GNU General Public License Version 2 or later (the "GPL"), or
28 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 # in which case the provisions of the GPL or the LGPL are applicable instead
30 # of those above. If you wish to allow use of your version of this file only
31 # under the terms of either the GPL or the LGPL, and not to allow others to
32 # use your version of this file under the terms of the MPL, indicate your
33 # decision by deleting the provisions above and replace them with the notice
34 # and other provisions required by the GPL or the LGPL. If you do not delete
35 # the provisions above, a recipient may use your version of this file under
36 # the terms of any one of the MPL, the GPL or the LGPL.
38 # ***** END LICENSE BLOCK *****
40 const nsICookie = Components.interfaces.nsICookie;
42 var gCookiesWindow = {
43 _cm : Components.classes["@mozilla.org/cookiemanager;1"]
44 .getService(Components.interfaces.nsICookieManager),
45 _ds : Components.classes["@mozilla.org/intl/scriptabledateformat;1"]
46 .getService(Components.interfaces.nsIScriptableDateFormat),
47 _hosts : {},
48 _hostOrder : [],
49 _tree : null,
50 _bundle : null,
52 init: function ()
54 var os = Components.classes["@mozilla.org/observer-service;1"]
55 .getService(Components.interfaces.nsIObserverService);
56 os.addObserver(this, "cookie-changed", false);
57 os.addObserver(this, "perm-changed", false);
59 this._bundle = document.getElementById("bundlePreferences");
60 this._tree = document.getElementById("cookiesList");
62 this._populateList(true);
64 document.getElementById("filter").focus();
67 uninit: function ()
69 var os = Components.classes["@mozilla.org/observer-service;1"]
70 .getService(Components.interfaces.nsIObserverService);
71 os.removeObserver(this, "cookie-changed");
72 os.removeObserver(this, "perm-changed");
75 _populateList: function (aInitialLoad)
77 this._loadCookies();
78 this._tree.treeBoxObject.view = this._view;
79 if (aInitialLoad)
80 this.sort("rawHost");
81 if (this._view.rowCount > 0)
82 this._tree.view.selection.select(0);
84 if (aInitialLoad) {
85 if ("arguments" in window && window.arguments[0] &&
86 window.arguments[0].filterString)
88 document.getElementById("filter").value = window.arguments[0].filterString;
89 this.filter();
92 else {
93 if (document.getElementById("filter").value != "")
94 this.filter();
97 this._saveState();
100 _cookieEquals: function (aCookieA, aCookieB, aStrippedHost)
102 return aCookieA.rawHost == aStrippedHost &&
103 aCookieA.name == aCookieB.name &&
104 aCookieA.path == aCookieB.path;
107 observe: function (aCookie, aTopic, aData)
109 if (aTopic != "cookie-changed")
110 return;
112 if (aCookie instanceof Components.interfaces.nsICookie) {
113 var strippedHost = this._makeStrippedHost(aCookie.host);
114 if (aData == "changed")
115 this._handleCookieChanged(aCookie, strippedHost);
116 else if (aData == "added")
117 this._handleCookieAdded(aCookie, strippedHost);
119 else if (aData == "cleared") {
120 this._hosts = {};
121 this._hostOrder = [];
123 var oldRowCount = this._view._rowCount;
124 this._view._rowCount = 0;
125 this._tree.treeBoxObject.rowCountChanged(0, -oldRowCount);
126 this._view.selection.clearSelection();
128 else if (aData == "reload") {
129 // first, clear any existing entries
130 this.observe(aCookie, aTopic, "cleared");
132 // then, reload the list
133 this._populateList(false);
136 // We don't yet handle aData == "deleted" - it's a less common case
137 // and is rather complicated as selection tracking is difficult
140 _handleCookieChanged: function (changedCookie, strippedHost)
142 var rowIndex = 0;
143 var cookieItem = null;
144 if (!this._view._filtered) {
145 for (var i = 0; i < this._hostOrder.length; ++i) { // (var host in this._hosts) {
146 ++rowIndex;
147 var hostItem = this._hosts[this._hostOrder[i]]; // var hostItem = this._hosts[host];
148 if (this._hostOrder[i] == strippedHost) { // host == strippedHost) {
149 // Host matches, look for the cookie within this Host collection
150 // and update its data
151 for (var j = 0; j < hostItem.cookies.length; ++j) {
152 ++rowIndex;
153 var currCookie = hostItem.cookies[j];
154 if (this._cookieEquals(currCookie, changedCookie, strippedHost)) {
155 currCookie.value = changedCookie.value;
156 currCookie.isSecure = changedCookie.isSecure;
157 currCookie.isDomain = changedCookie.isDomain;
158 currCookie.expires = changedCookie.expires;
159 cookieItem = currCookie;
160 break;
164 else if (hostItem.open)
165 rowIndex += hostItem.cookies.length;
168 else {
169 // Just walk the filter list to find the item. It doesn't matter that
170 // we don't update the main Host collection when we do this, because
171 // when the filter is reset the Host collection is rebuilt anyway.
172 for (rowIndex = 0; rowIndex < this._view._filterSet.length; ++rowIndex) {
173 currCookie = this._view._filterSet[rowIndex];
174 if (this._cookieEquals(currCookie, changedCookie, strippedHost)) {
175 currCookie.value = changedCookie.value;
176 currCookie.isSecure = changedCookie.isSecure;
177 currCookie.isDomain = changedCookie.isDomain;
178 currCookie.expires = changedCookie.expires;
179 cookieItem = currCookie;
180 break;
185 // Make sure the tree display is up to date...
186 this._tree.treeBoxObject.invalidateRow(rowIndex);
187 // ... and if the cookie is selected, update the displayed metadata too
188 if (cookieItem != null && this._view.selection.currentIndex == rowIndex)
189 this._updateCookieData(cookieItem);
192 _handleCookieAdded: function (changedCookie, strippedHost)
194 var rowCountImpact = 0;
195 var addedHost = { value: 0 };
196 this._addCookie(strippedHost, changedCookie, addedHost);
197 if (!this._view._filtered) {
198 // The Host collection for this cookie already exists, and it's not open,
199 // so don't increment the rowCountImpact becaues the user is not going to
200 // see the additional rows as they're hidden.
201 if (addedHost.value || this._hosts[strippedHost].open)
202 ++rowCountImpact;
204 else {
205 // We're in search mode, and the cookie being added matches
206 // the search condition, so add it to the list.
207 var c = this._makeCookieObject(strippedHost, changedCookie);
208 if (this._cookieMatchesFilter(c)) {
209 this._view._filterSet.push(this._makeCookieObject(strippedHost, changedCookie));
210 ++rowCountImpact;
213 // Now update the tree display at the end (we could/should re run the sort
214 // if any to get the position correct.)
215 var oldRowCount = this._rowCount;
216 this._view._rowCount += rowCountImpact;
217 this._tree.treeBoxObject.rowCountChanged(oldRowCount - 1, rowCountImpact);
219 document.getElementById("removeAllCookies").disabled = this._view._filtered;
222 _view: {
223 _filtered : false,
224 _filterSet : [],
225 _filterValue: "",
226 _rowCount : 0,
227 _cacheValid : 0,
228 _cacheItems : [],
229 get rowCount()
231 return this._rowCount;
234 _getItemAtIndex: function (aIndex)
236 if (this._filtered)
237 return this._filterSet[aIndex];
239 var start = 0;
240 var count = 0, hostIndex = 0;
242 var cacheIndex = Math.min(this._cacheValid, aIndex);
243 if (cacheIndex > 0) {
244 var cacheItem = this._cacheItems[cacheIndex];
245 start = cacheItem['start'];
246 count = hostIndex = cacheItem['count'];
249 for (var i = start; i < gCookiesWindow._hostOrder.length; ++i) { // var host in gCookiesWindow._hosts) {
250 var currHost = gCookiesWindow._hosts[gCookiesWindow._hostOrder[i]]; // gCookiesWindow._hosts[host];
251 if (!currHost) continue;
252 if (count == aIndex)
253 return currHost;
254 hostIndex = count;
256 var cacheEntry = { 'start' : i, 'count' : count };
257 var cacheStart = count;
259 if (currHost.open) {
260 if (count < aIndex && aIndex <= (count + currHost.cookies.length)) {
261 // We are looking for an entry within this host's children,
262 // enumerate them looking for the index.
263 ++count;
264 for (var i = 0; i < currHost.cookies.length; ++i) {
265 if (count == aIndex) {
266 var cookie = currHost.cookies[i];
267 cookie.parentIndex = hostIndex;
268 return cookie;
270 ++count;
273 else {
274 // A host entry was open, but we weren't looking for an index
275 // within that host entry's children, so skip forward over the
276 // entry's children. We need to add one to increment for the
277 // host value too.
278 count += currHost.cookies.length + 1;
281 else
282 ++count;
284 for (var j = cacheStart; j < count; j++)
285 this._cacheItems[j] = cacheEntry;
286 this._cacheValid = count - 1;
288 return null;
291 _removeItemAtIndex: function (aIndex, aCount)
293 var removeCount = aCount === undefined ? 1 : aCount;
294 if (this._filtered) {
295 // remove the cookies from the unfiltered set so that they
296 // don't reappear when the filter is changed. See bug 410863.
297 for (var i = aIndex; i < aIndex + removeCount; ++i) {
298 var item = this._filterSet[i];
299 var parent = gCookiesWindow._hosts[item.rawHost];
300 for (var j = 0; j < parent.cookies.length; ++j) {
301 if (item == parent.cookies[j]) {
302 parent.cookies.splice(j, 1);
303 break;
307 this._filterSet.splice(aIndex, removeCount);
308 return;
311 var item = this._getItemAtIndex(aIndex);
312 if (!item) return;
313 this._invalidateCache(aIndex - 1);
314 if (item.container)
315 gCookiesWindow._hosts[item.rawHost] = null;
316 else {
317 var parent = this._getItemAtIndex(item.parentIndex);
318 for (var i = 0; i < parent.cookies.length; ++i) {
319 var cookie = parent.cookies[i];
320 if (item.rawHost == cookie.rawHost &&
321 item.name == cookie.name && item.path == cookie.path)
322 parent.cookies.splice(i, removeCount);
327 _invalidateCache: function (aIndex)
329 this._cacheValid = Math.min(this._cacheValid, aIndex);
332 getCellText: function (aIndex, aColumn)
334 if (!this._filtered) {
335 var item = this._getItemAtIndex(aIndex);
336 if (!item)
337 return "";
338 if (aColumn.id == "domainCol")
339 return item.rawHost;
340 else if (aColumn.id == "nameCol")
341 return item.name;
343 else {
344 if (aColumn.id == "domainCol")
345 return this._filterSet[aIndex].rawHost;
346 else if (aColumn.id == "nameCol")
347 return this._filterSet[aIndex].name;
349 return "";
352 _selection: null,
353 get selection () { return this._selection; },
354 set selection (val) { this._selection = val; return val; },
355 getRowProperties: function (aIndex, aProperties) {},
356 getCellProperties: function (aIndex, aColumn, aProperties) {},
357 getColumnProperties: function (aColumn, aProperties) {},
358 isContainer: function (aIndex)
360 if (!this._filtered) {
361 var item = this._getItemAtIndex(aIndex);
362 if (!item) return false;
363 return item.container;
365 return false;
367 isContainerOpen: function (aIndex)
369 if (!this._filtered) {
370 var item = this._getItemAtIndex(aIndex);
371 if (!item) return false;
372 return item.open;
374 return false;
376 isContainerEmpty: function (aIndex)
378 if (!this._filtered) {
379 var item = this._getItemAtIndex(aIndex);
380 if (!item) return false;
381 return item.cookies.length == 0;
383 return false;
385 isSeparator: function (aIndex) { return false; },
386 isSorted: function (aIndex) { return false; },
387 canDrop: function (aIndex, aOrientation) { return false; },
388 drop: function (aIndex, aOrientation) {},
389 getParentIndex: function (aIndex)
391 if (!this._filtered) {
392 var item = this._getItemAtIndex(aIndex);
393 // If an item has no parent index (i.e. it is at the top level) this
394 // function MUST return -1 otherwise we will go into an infinite loop.
395 // Containers are always top level items in the cookies tree, so make
396 // sure to return the appropriate value here.
397 if (!item || item.container) return -1;
398 return item.parentIndex;
400 return -1;
402 hasNextSibling: function (aParentIndex, aIndex)
404 if (!this._filtered) {
405 // |aParentIndex| appears to be bogus, but we can get the real
406 // parent index by getting the entry for |aIndex| and reading the
407 // parentIndex field.
408 // The index of the last item in this host collection is the
409 // index of the parent + the size of the host collection, and
410 // aIndex has a next sibling if it is less than this value.
411 var item = this._getItemAtIndex(aIndex);
412 if (item) {
413 if (item.container) {
414 for (var i = aIndex + 1; i < this.rowCount; ++i) {
415 var subsequent = this._getItemAtIndex(i);
416 if (subsequent.container)
417 return true;
419 return false;
421 else {
422 var parent = this._getItemAtIndex(item.parentIndex);
423 if (parent && parent.container)
424 return aIndex < item.parentIndex + parent.cookies.length;
428 return aIndex < this.rowCount - 1;
430 hasPreviousSibling: function (aIndex)
432 if (!this._filtered) {
433 var item = this._getItemAtIndex(aIndex);
434 if (!item) return false;
435 var parent = this._getItemAtIndex(item.parentIndex);
436 if (parent && parent.container)
437 return aIndex > item.parentIndex + 1;
439 return aIndex > 0;
441 getLevel: function (aIndex)
443 if (!this._filtered) {
444 var item = this._getItemAtIndex(aIndex);
445 if (!item) return 0;
446 return item.level;
448 return 0;
450 getImageSrc: function (aIndex, aColumn) {},
451 getProgressMode: function (aIndex, aColumn) {},
452 getCellValue: function (aIndex, aColumn) {},
453 setTree: function (aTree) {},
454 toggleOpenState: function (aIndex)
456 if (!this._filtered) {
457 var item = this._getItemAtIndex(aIndex);
458 if (!item) return;
459 this._invalidateCache(aIndex);
460 var multiplier = item.open ? -1 : 1;
461 var delta = multiplier * item.cookies.length;
462 this._rowCount += delta;
463 item.open = !item.open;
464 gCookiesWindow._tree.treeBoxObject.rowCountChanged(aIndex + 1, delta);
465 gCookiesWindow._tree.treeBoxObject.invalidateRow(aIndex);
468 cycleHeader: function (aColumn) {},
469 selectionChanged: function () {},
470 cycleCell: function (aIndex, aColumn) {},
471 isEditable: function (aIndex, aColumn)
473 return false;
475 isSelectable: function (aIndex, aColumn)
477 return false;
479 setCellValue: function (aIndex, aColumn, aValue) {},
480 setCellText: function (aIndex, aColumn, aValue) {},
481 performAction: function (aAction) {},
482 performActionOnRow: function (aAction, aIndex) {},
483 performActionOnCell: function (aAction, aindex, aColumn) {}
486 _makeStrippedHost: function (aHost)
488 var formattedHost = aHost.charAt(0) == "." ? aHost.substring(1, aHost.length) : aHost;
489 return formattedHost.substring(0, 4) == "www." ? formattedHost.substring(4, formattedHost.length) : formattedHost;
492 _addCookie: function (aStrippedHost, aCookie, aHostCount)
494 if (!(aStrippedHost in this._hosts) || !this._hosts[aStrippedHost]) {
495 this._hosts[aStrippedHost] = { cookies : [],
496 rawHost : aStrippedHost,
497 level : 0,
498 open : false,
499 container : true };
500 this._hostOrder.push(aStrippedHost);
501 ++aHostCount.value;
504 var c = this._makeCookieObject(aStrippedHost, aCookie);
505 this._hosts[aStrippedHost].cookies.push(c);
508 _makeCookieObject: function (aStrippedHost, aCookie)
510 var host = aCookie.host;
511 var formattedHost = host.charAt(0) == "." ? host.substring(1, host.length) : host;
512 var c = { name : aCookie.name,
513 value : aCookie.value,
514 isDomain : aCookie.isDomain,
515 host : aCookie.host,
516 rawHost : aStrippedHost,
517 path : aCookie.path,
518 isSecure : aCookie.isSecure,
519 expires : aCookie.expires,
520 level : 1,
521 container : false };
522 return c;
525 _loadCookies: function ()
527 var e = this._cm.enumerator;
528 var hostCount = { value: 0 };
529 this._hosts = {};
530 this._hostOrder = [];
531 while (e.hasMoreElements()) {
532 var cookie = e.getNext();
533 if (cookie && cookie instanceof Components.interfaces.nsICookie) {
534 var strippedHost = this._makeStrippedHost(cookie.host);
535 this._addCookie(strippedHost, cookie, hostCount);
537 else
538 break;
540 this._view._rowCount = hostCount.value;
543 formatExpiresString: function (aExpires)
545 if (aExpires) {
546 var date = new Date(1000 * aExpires);
547 return this._ds.FormatDateTime("", this._ds.dateFormatLong,
548 this._ds.timeFormatSeconds,
549 date.getFullYear(),
550 date.getMonth() + 1,
551 date.getDate(),
552 date.getHours(),
553 date.getMinutes(),
554 date.getSeconds());
556 return this._bundle.getString("AtEndOfSession");
559 _updateCookieData: function (aItem)
561 var seln = this._view.selection;
562 var ids = ["name", "value", "host", "path", "isSecure", "expires"];
563 var properties;
565 if (aItem && !aItem.container && seln.count > 0) {
566 properties = { name: aItem.name, value: aItem.value, host: aItem.host,
567 path: aItem.path, expires: this.formatExpiresString(aItem.expires),
568 isDomain: aItem.isDomain ? this._bundle.getString("domainColon")
569 : this._bundle.getString("hostColon"),
570 isSecure: aItem.isSecure ? this._bundle.getString("forSecureOnly")
571 : this._bundle.getString("forAnyConnection") };
572 for (var i = 0; i < ids.length; ++i)
573 document.getElementById(ids[i]).disabled = false;
575 else {
576 var noneSelected = this._bundle.getString("noCookieSelected");
577 properties = { name: noneSelected, value: noneSelected, host: noneSelected,
578 path: noneSelected, expires: noneSelected,
579 isSecure: noneSelected };
580 for (i = 0; i < ids.length; ++i)
581 document.getElementById(ids[i]).disabled = true;
583 for (var property in properties)
584 document.getElementById(property).value = properties[property];
587 onCookieSelected: function ()
589 var properties, item;
590 var seln = this._tree.view.selection;
591 if (!this._view._filtered)
592 item = this._view._getItemAtIndex(seln.currentIndex);
593 else
594 item = this._view._filterSet[seln.currentIndex];
596 this._updateCookieData(item);
598 var rangeCount = seln.getRangeCount();
599 var selectedCookieCount = 0;
600 for (var i = 0; i < rangeCount; ++i) {
601 var min = {}; var max = {};
602 seln.getRangeAt(i, min, max);
603 for (var j = min.value; j <= max.value; ++j) {
604 item = this._view._getItemAtIndex(j);
605 if (!item) continue;
606 if (item.container && !item.open)
607 selectedCookieCount += item.cookies.length;
608 else if (!item.container)
609 ++selectedCookieCount;
612 var item = this._view._getItemAtIndex(seln.currentIndex);
613 if (item && seln.count == 1 && item.container && item.open)
614 selectedCookieCount += 2;
616 var stringKey = selectedCookieCount == 1 ? "removeCookie" : "removeCookies";
617 document.getElementById("removeCookie").label = this._bundle.getString(stringKey);
619 document.getElementById("removeAllCookies").disabled = this._view._filtered;
620 document.getElementById("removeCookie").disabled = !(seln.count > 0);
623 deleteCookie: function ()
625 # // Selection Notes
626 # // - Selection always moves to *NEXT* adjacent item unless item
627 # // is last child at a given level in which case it moves to *PREVIOUS*
628 # // item
629 # //
630 # // Selection Cases (Somewhat Complicated)
631 # //
632 # // 1) Single cookie selected, host has single child
633 # // v cnn.com
634 # // //// cnn.com ///////////// goksdjf@ ////
635 # // > atwola.com
636 # //
637 # // Before SelectedIndex: 1 Before RowCount: 3
638 # // After SelectedIndex: 0 After RowCount: 1
639 # //
640 # // 2) Host selected, host open
641 # // v goats.com ////////////////////////////
642 # // goats.com sldkkfjl
643 # // goat.scom flksj133
644 # // > atwola.com
645 # //
646 # // Before SelectedIndex: 0 Before RowCount: 4
647 # // After SelectedIndex: 0 After RowCount: 1
648 # //
649 # // 3) Host selected, host closed
650 # // > goats.com ////////////////////////////
651 # // > atwola.com
652 # //
653 # // Before SelectedIndex: 0 Before RowCount: 2
654 # // After SelectedIndex: 0 After RowCount: 1
655 # //
656 # // 4) Single cookie selected, host has many children
657 # // v goats.com
658 # // goats.com sldkkfjl
659 # // //// goats.com /////////// flksjl33 ////
660 # // > atwola.com
661 # //
662 # // Before SelectedIndex: 2 Before RowCount: 4
663 # // After SelectedIndex: 1 After RowCount: 3
664 # //
665 # // 5) Single cookie selected, host has many children
666 # // v goats.com
667 # // //// goats.com /////////// flksjl33 ////
668 # // goats.com sldkkfjl
669 # // > atwola.com
670 # //
671 # // Before SelectedIndex: 1 Before RowCount: 4
672 # // After SelectedIndex: 1 After RowCount: 3
673 var seln = this._view.selection;
674 var tbo = this._tree.treeBoxObject;
676 if (seln.count < 1) return;
678 var nextSelected = 0;
679 var rowCountImpact = 0;
680 var deleteItems = [];
681 if (!this._view._filtered) {
682 var ci = seln.currentIndex;
683 nextSelected = ci;
684 var invalidateRow = -1;
685 var item = this._view._getItemAtIndex(ci);
686 if (item.container) {
687 rowCountImpact -= (item.open ? item.cookies.length : 0) + 1;
688 deleteItems = deleteItems.concat(item.cookies);
689 if (!this._view.hasNextSibling(-1, ci))
690 --nextSelected;
691 this._view._removeItemAtIndex(ci);
693 else {
694 var parent = this._view._getItemAtIndex(item.parentIndex);
695 --rowCountImpact;
696 if (parent.cookies.length == 1) {
697 --rowCountImpact;
698 deleteItems.push(item);
699 if (!this._view.hasNextSibling(-1, ci))
700 --nextSelected;
701 if (!this._view.hasNextSibling(-1, item.parentIndex))
702 --nextSelected;
703 this._view._removeItemAtIndex(item.parentIndex);
704 invalidateRow = item.parentIndex;
706 else {
707 deleteItems.push(item);
708 if (!this._view.hasNextSibling(-1, ci))
709 --nextSelected;
710 this._view._removeItemAtIndex(ci);
713 this._view._rowCount += rowCountImpact;
714 tbo.rowCountChanged(ci, rowCountImpact);
715 if (invalidateRow != -1)
716 tbo.invalidateRow(invalidateRow);
718 else {
719 var rangeCount = seln.getRangeCount();
720 for (var i = 0; i < rangeCount; ++i) {
721 var min = {}; var max = {};
722 seln.getRangeAt(i, min, max);
723 nextSelected = min.value;
724 for (var j = min.value; j <= max.value; ++j) {
725 deleteItems.push(this._view._getItemAtIndex(j));
726 if (!this._view.hasNextSibling(-1, max.value))
727 --nextSelected;
729 var delta = max.value - min.value + 1;
730 this._view._removeItemAtIndex(min.value, delta);
731 rowCountImpact = -1 * delta;
732 this._view._rowCount += rowCountImpact;
733 tbo.rowCountChanged(min.value, rowCountImpact);
737 var psvc = Components.classes["@mozilla.org/preferences-service;1"]
738 .getService(Components.interfaces.nsIPrefBranch);
739 var blockFutureCookies = false;
740 if (psvc.prefHasUserValue("network.cookie.blockFutureCookies"))
741 blockFutureCookies = psvc.getBoolPref("network.cookie.blockFutureCookies");
742 for (i = 0; i < deleteItems.length; ++i) {
743 var item = deleteItems[i];
744 this._cm.remove(item.host, item.name, item.path, blockFutureCookies);
747 if (nextSelected < 0)
748 seln.clearSelection();
749 else {
750 seln.select(nextSelected);
751 this._tree.focus();
755 deleteAllCookies: function ()
757 this._cm.removeAll();
758 this._tree.focus();
761 onCookieKeyPress: function (aEvent)
763 if (aEvent.keyCode == 46)
764 this.deleteCookie();
767 _lastSortProperty : "",
768 _lastSortAscending: false,
769 sort: function (aProperty)
771 var ascending = (aProperty == this._lastSortProperty) ? !this._lastSortAscending : true;
772 // Sort the Non-Filtered Host Collections
773 if (aProperty == "rawHost") {
774 function sortByHost(a, b)
776 return a.toLowerCase().localeCompare(b.toLowerCase());
778 this._hostOrder.sort(sortByHost);
779 if (!ascending)
780 this._hostOrder.reverse();
783 function sortByProperty(a, b)
785 return a[aProperty].toLowerCase().localeCompare(b[aProperty].toLowerCase());
787 for (var host in this._hosts) {
788 var cookies = this._hosts[host].cookies;
789 cookies.sort(sortByProperty);
790 if (!ascending)
791 cookies.reverse();
793 // Sort the Filtered List, if in Filtered mode
794 if (this._view._filtered) {
795 this._view._filterSet.sort(sortByProperty);
796 if (!ascending)
797 this._view._filterSet.reverse();
800 this._view._invalidateCache(0);
801 this._view.selection.clearSelection();
802 this._view.selection.select(0);
803 this._tree.treeBoxObject.invalidate();
804 this._tree.treeBoxObject.ensureRowIsVisible(0);
806 this._lastSortAscending = ascending;
807 this._lastSortProperty = aProperty;
810 clearFilter: function ()
812 // Revert to single-select in the tree
813 this._tree.setAttribute("seltype", "single");
815 // Clear the Tree Display
816 this._view._filtered = false;
817 this._view._rowCount = 0;
818 this._tree.treeBoxObject.rowCountChanged(0, -this._view._filterSet.length);
819 this._view._filterSet = [];
821 // Just reload the list to make sure deletions are respected
822 this._loadCookies();
823 this._tree.treeBoxObject.view = this._view;
825 // Restore sort order
826 var sortby = this._lastSortProperty;
827 if (sortby == "") {
828 this._lastSortAscending = false;
829 this.sort("rawHost");
831 else {
832 this._lastSortAscending = !this._lastSortAscending;
833 this.sort(sortby);
836 // Restore open state
837 for (var i = 0; i < this._openIndices.length; ++i)
838 this._view.toggleOpenState(this._openIndices[i]);
839 this._openIndices = [];
841 // Restore selection
842 this._view.selection.clearSelection();
843 for (i = 0; i < this._lastSelectedRanges.length; ++i) {
844 var range = this._lastSelectedRanges[i];
845 this._view.selection.rangedSelect(range.min, range.max, true);
847 this._lastSelectedRanges = [];
849 document.getElementById("cookiesIntro").value = this._bundle.getString("cookiesAll");
852 _cookieMatchesFilter: function (aCookie)
854 return aCookie.rawHost.indexOf(this._view._filterValue) != -1 ||
855 aCookie.name.indexOf(this._view._filterValue) != -1 ||
856 aCookie.value.indexOf(this._view._filterValue) != -1;
859 _filterCookies: function (aFilterValue)
861 this._view._filterValue = aFilterValue;
862 var cookies = [];
863 for (var i = 0; i < gCookiesWindow._hostOrder.length; ++i) { //var host in gCookiesWindow._hosts) {
864 var currHost = gCookiesWindow._hosts[gCookiesWindow._hostOrder[i]]; // gCookiesWindow._hosts[host];
865 if (!currHost) continue;
866 for (var j = 0; j < currHost.cookies.length; ++j) {
867 var cookie = currHost.cookies[j];
868 if (this._cookieMatchesFilter(cookie))
869 cookies.push(cookie);
872 return cookies;
875 _lastSelectedRanges: [],
876 _openIndices: [],
877 _saveState: function ()
879 // Save selection
880 var seln = this._view.selection;
881 this._lastSelectedRanges = [];
882 var rangeCount = seln.getRangeCount();
883 for (var i = 0; i < rangeCount; ++i) {
884 var min = {}; var max = {};
885 seln.getRangeAt(i, min, max);
886 this._lastSelectedRanges.push({ min: min.value, max: max.value });
889 // Save open states
890 this._openIndices = [];
891 for (i = 0; i < this._view.rowCount; ++i) {
892 var item = this._view._getItemAtIndex(i);
893 if (item && item.container && item.open)
894 this._openIndices.push(i);
898 filter: function ()
900 var filter = document.getElementById("filter").value;
901 if (filter == "") {
902 gCookiesWindow.clearFilter();
903 return;
905 var view = gCookiesWindow._view;
906 view._filterSet = gCookiesWindow._filterCookies(filter);
907 if (!view._filtered) {
908 // Save Display Info for the Non-Filtered mode when we first
909 // enter Filtered mode.
910 gCookiesWindow._saveState();
911 view._filtered = true;
913 // Move to multi-select in the tree
914 gCookiesWindow._tree.setAttribute("seltype", "multiple");
916 // Clear the display
917 var oldCount = view._rowCount;
918 view._rowCount = 0;
919 gCookiesWindow._tree.treeBoxObject.rowCountChanged(0, -oldCount);
920 // Set up the filtered display
921 view._rowCount = view._filterSet.length;
922 gCookiesWindow._tree.treeBoxObject.rowCountChanged(0, view.rowCount);
924 // if the view is not empty then select the first item
925 if (view.rowCount > 0)
926 view.selection.select(0);
928 document.getElementById("cookiesIntro").value = gCookiesWindow._bundle.getString("cookiesFiltered");
931 focusFilterBox: function ()
933 var filter = document.getElementById("filter");
934 filter.focus();
935 filter.select();