Wed, 31 Dec 2014 07:53:36 +0100
Correct small whitespace inconsistency, lost while renaming variables.
1 /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 const nsICookie = Components.interfaces.nsICookie;
8 var gCookiesWindow = {
9 _cm : Components.classes["@mozilla.org/cookiemanager;1"]
10 .getService(Components.interfaces.nsICookieManager),
11 _ds : Components.classes["@mozilla.org/intl/scriptabledateformat;1"]
12 .getService(Components.interfaces.nsIScriptableDateFormat),
13 _hosts : {},
14 _hostOrder : [],
15 _tree : null,
16 _bundle : null,
18 init: function () {
19 var os = Components.classes["@mozilla.org/observer-service;1"]
20 .getService(Components.interfaces.nsIObserverService);
21 os.addObserver(this, "cookie-changed", false);
22 os.addObserver(this, "perm-changed", false);
24 this._bundle = document.getElementById("bundlePreferences");
25 this._tree = document.getElementById("cookiesList");
27 this._populateList(true);
29 document.getElementById("filter").focus();
30 },
32 uninit: function () {
33 var os = Components.classes["@mozilla.org/observer-service;1"]
34 .getService(Components.interfaces.nsIObserverService);
35 os.removeObserver(this, "cookie-changed");
36 os.removeObserver(this, "perm-changed");
37 },
39 _populateList: function (aInitialLoad) {
40 this._loadCookies();
41 this._tree.treeBoxObject.view = this._view;
42 if (aInitialLoad)
43 this.sort("rawHost");
44 if (this._view.rowCount > 0)
45 this._tree.view.selection.select(0);
47 if (aInitialLoad) {
48 if ("arguments" in window &&
49 window.arguments[0] &&
50 window.arguments[0].filterString)
51 this.setFilter(window.arguments[0].filterString);
52 }
53 else {
54 if (document.getElementById("filter").value != "")
55 this.filter();
56 }
58 this._updateRemoveAllButton();
60 this._saveState();
61 },
63 _cookieEquals: function (aCookieA, aCookieB, aStrippedHost) {
64 return aCookieA.rawHost == aStrippedHost &&
65 aCookieA.name == aCookieB.name &&
66 aCookieA.path == aCookieB.path;
67 },
69 observe: function (aCookie, aTopic, aData) {
70 if (aTopic != "cookie-changed")
71 return;
73 if (aCookie instanceof Components.interfaces.nsICookie) {
74 var strippedHost = this._makeStrippedHost(aCookie.host);
75 if (aData == "changed")
76 this._handleCookieChanged(aCookie, strippedHost);
77 else if (aData == "added")
78 this._handleCookieAdded(aCookie, strippedHost);
79 }
80 else if (aData == "cleared") {
81 this._hosts = {};
82 this._hostOrder = [];
84 var oldRowCount = this._view._rowCount;
85 this._view._rowCount = 0;
86 this._tree.treeBoxObject.rowCountChanged(0, -oldRowCount);
87 this._view.selection.clearSelection();
88 this._updateRemoveAllButton();
89 }
90 else if (aData == "reload") {
91 // first, clear any existing entries
92 this.observe(aCookie, aTopic, "cleared");
94 // then, reload the list
95 this._populateList(false);
96 }
98 // We don't yet handle aData == "deleted" - it's a less common case
99 // and is rather complicated as selection tracking is difficult
100 },
102 _handleCookieChanged: function (changedCookie, strippedHost) {
103 var rowIndex = 0;
104 var cookieItem = null;
105 if (!this._view._filtered) {
106 for (var i = 0; i < this._hostOrder.length; ++i) { // (var host in this._hosts) {
107 ++rowIndex;
108 var hostItem = this._hosts[this._hostOrder[i]]; // var hostItem = this._hosts[host];
109 if (this._hostOrder[i] == strippedHost) { // host == strippedHost) {
110 // Host matches, look for the cookie within this Host collection
111 // and update its data
112 for (var j = 0; j < hostItem.cookies.length; ++j) {
113 ++rowIndex;
114 var currCookie = hostItem.cookies[j];
115 if (this._cookieEquals(currCookie, changedCookie, strippedHost)) {
116 currCookie.value = changedCookie.value;
117 currCookie.isSecure = changedCookie.isSecure;
118 currCookie.isDomain = changedCookie.isDomain;
119 currCookie.expires = changedCookie.expires;
120 cookieItem = currCookie;
121 break;
122 }
123 }
124 }
125 else if (hostItem.open)
126 rowIndex += hostItem.cookies.length;
127 }
128 }
129 else {
130 // Just walk the filter list to find the item. It doesn't matter that
131 // we don't update the main Host collection when we do this, because
132 // when the filter is reset the Host collection is rebuilt anyway.
133 for (rowIndex = 0; rowIndex < this._view._filterSet.length; ++rowIndex) {
134 currCookie = this._view._filterSet[rowIndex];
135 if (this._cookieEquals(currCookie, changedCookie, strippedHost)) {
136 currCookie.value = changedCookie.value;
137 currCookie.isSecure = changedCookie.isSecure;
138 currCookie.isDomain = changedCookie.isDomain;
139 currCookie.expires = changedCookie.expires;
140 cookieItem = currCookie;
141 break;
142 }
143 }
144 }
146 // Make sure the tree display is up to date...
147 this._tree.treeBoxObject.invalidateRow(rowIndex);
148 // ... and if the cookie is selected, update the displayed metadata too
149 if (cookieItem != null && this._view.selection.currentIndex == rowIndex)
150 this._updateCookieData(cookieItem);
151 },
153 _handleCookieAdded: function (changedCookie, strippedHost) {
154 var rowCountImpact = 0;
155 var addedHost = { value: 0 };
156 this._addCookie(strippedHost, changedCookie, addedHost);
157 if (!this._view._filtered) {
158 // The Host collection for this cookie already exists, and it's not open,
159 // so don't increment the rowCountImpact becaues the user is not going to
160 // see the additional rows as they're hidden.
161 if (addedHost.value || this._hosts[strippedHost].open)
162 ++rowCountImpact;
163 }
164 else {
165 // We're in search mode, and the cookie being added matches
166 // the search condition, so add it to the list.
167 var c = this._makeCookieObject(strippedHost, changedCookie);
168 if (this._cookieMatchesFilter(c)) {
169 this._view._filterSet.push(this._makeCookieObject(strippedHost, changedCookie));
170 ++rowCountImpact;
171 }
172 }
173 // Now update the tree display at the end (we could/should re run the sort
174 // if any to get the position correct.)
175 var oldRowCount = this._rowCount;
176 this._view._rowCount += rowCountImpact;
177 this._tree.treeBoxObject.rowCountChanged(oldRowCount - 1, rowCountImpact);
179 this._updateRemoveAllButton();
180 },
182 _view: {
183 _filtered : false,
184 _filterSet : [],
185 _filterValue: "",
186 _rowCount : 0,
187 _cacheValid : 0,
188 _cacheItems : [],
189 get rowCount() {
190 return this._rowCount;
191 },
193 _getItemAtIndex: function (aIndex) {
194 if (this._filtered)
195 return this._filterSet[aIndex];
197 var start = 0;
198 var count = 0, hostIndex = 0;
200 var cacheIndex = Math.min(this._cacheValid, aIndex);
201 if (cacheIndex > 0) {
202 var cacheItem = this._cacheItems[cacheIndex];
203 start = cacheItem['start'];
204 count = hostIndex = cacheItem['count'];
205 }
207 for (var i = start; i < gCookiesWindow._hostOrder.length; ++i) { // var host in gCookiesWindow._hosts) {
208 var currHost = gCookiesWindow._hosts[gCookiesWindow._hostOrder[i]]; // gCookiesWindow._hosts[host];
209 if (!currHost) continue;
210 if (count == aIndex)
211 return currHost;
212 hostIndex = count;
214 var cacheEntry = { 'start' : i, 'count' : count };
215 var cacheStart = count;
217 if (currHost.open) {
218 if (count < aIndex && aIndex <= (count + currHost.cookies.length)) {
219 // We are looking for an entry within this host's children,
220 // enumerate them looking for the index.
221 ++count;
222 for (var i = 0; i < currHost.cookies.length; ++i) {
223 if (count == aIndex) {
224 var cookie = currHost.cookies[i];
225 cookie.parentIndex = hostIndex;
226 return cookie;
227 }
228 ++count;
229 }
230 }
231 else {
232 // A host entry was open, but we weren't looking for an index
233 // within that host entry's children, so skip forward over the
234 // entry's children. We need to add one to increment for the
235 // host value too.
236 count += currHost.cookies.length + 1;
237 }
238 }
239 else
240 ++count;
242 for (var j = cacheStart; j < count; j++)
243 this._cacheItems[j] = cacheEntry;
244 this._cacheValid = count - 1;
245 }
246 return null;
247 },
249 _removeItemAtIndex: function (aIndex, aCount) {
250 var removeCount = aCount === undefined ? 1 : aCount;
251 if (this._filtered) {
252 // remove the cookies from the unfiltered set so that they
253 // don't reappear when the filter is changed. See bug 410863.
254 for (var i = aIndex; i < aIndex + removeCount; ++i) {
255 var item = this._filterSet[i];
256 var parent = gCookiesWindow._hosts[item.rawHost];
257 for (var j = 0; j < parent.cookies.length; ++j) {
258 if (item == parent.cookies[j]) {
259 parent.cookies.splice(j, 1);
260 break;
261 }
262 }
263 }
264 this._filterSet.splice(aIndex, removeCount);
265 return;
266 }
268 var item = this._getItemAtIndex(aIndex);
269 if (!item) return;
270 this._invalidateCache(aIndex - 1);
271 if (item.container)
272 gCookiesWindow._hosts[item.rawHost] = null;
273 else {
274 var parent = this._getItemAtIndex(item.parentIndex);
275 for (var i = 0; i < parent.cookies.length; ++i) {
276 var cookie = parent.cookies[i];
277 if (item.rawHost == cookie.rawHost &&
278 item.name == cookie.name && item.path == cookie.path)
279 parent.cookies.splice(i, removeCount);
280 }
281 }
282 },
284 _invalidateCache: function (aIndex) {
285 this._cacheValid = Math.min(this._cacheValid, aIndex);
286 },
288 getCellText: function (aIndex, aColumn) {
289 if (!this._filtered) {
290 var item = this._getItemAtIndex(aIndex);
291 if (!item)
292 return "";
293 if (aColumn.id == "domainCol")
294 return item.rawHost;
295 else if (aColumn.id == "nameCol")
296 return item.name;
297 }
298 else {
299 if (aColumn.id == "domainCol")
300 return this._filterSet[aIndex].rawHost;
301 else if (aColumn.id == "nameCol")
302 return this._filterSet[aIndex].name;
303 }
304 return "";
305 },
307 _selection: null,
308 get selection () { return this._selection; },
309 set selection (val) { this._selection = val; return val; },
310 getRowProperties: function (aIndex) { return ""; },
311 getCellProperties: function (aIndex, aColumn) { return ""; },
312 getColumnProperties: function (aColumn) { return ""; },
313 isContainer: function (aIndex) {
314 if (!this._filtered) {
315 var item = this._getItemAtIndex(aIndex);
316 if (!item) return false;
317 return item.container;
318 }
319 return false;
320 },
321 isContainerOpen: function (aIndex) {
322 if (!this._filtered) {
323 var item = this._getItemAtIndex(aIndex);
324 if (!item) return false;
325 return item.open;
326 }
327 return false;
328 },
329 isContainerEmpty: function (aIndex) {
330 if (!this._filtered) {
331 var item = this._getItemAtIndex(aIndex);
332 if (!item) return false;
333 return item.cookies.length == 0;
334 }
335 return false;
336 },
337 isSeparator: function (aIndex) { return false; },
338 isSorted: function (aIndex) { return false; },
339 canDrop: function (aIndex, aOrientation) { return false; },
340 drop: function (aIndex, aOrientation) {},
341 getParentIndex: function (aIndex) {
342 if (!this._filtered) {
343 var item = this._getItemAtIndex(aIndex);
344 // If an item has no parent index (i.e. it is at the top level) this
345 // function MUST return -1 otherwise we will go into an infinite loop.
346 // Containers are always top level items in the cookies tree, so make
347 // sure to return the appropriate value here.
348 if (!item || item.container) return -1;
349 return item.parentIndex;
350 }
351 return -1;
352 },
353 hasNextSibling: function (aParentIndex, aIndex) {
354 if (!this._filtered) {
355 // |aParentIndex| appears to be bogus, but we can get the real
356 // parent index by getting the entry for |aIndex| and reading the
357 // parentIndex field.
358 // The index of the last item in this host collection is the
359 // index of the parent + the size of the host collection, and
360 // aIndex has a next sibling if it is less than this value.
361 var item = this._getItemAtIndex(aIndex);
362 if (item) {
363 if (item.container) {
364 for (var i = aIndex + 1; i < this.rowCount; ++i) {
365 var subsequent = this._getItemAtIndex(i);
366 if (subsequent.container)
367 return true;
368 }
369 return false;
370 }
371 else {
372 var parent = this._getItemAtIndex(item.parentIndex);
373 if (parent && parent.container)
374 return aIndex < item.parentIndex + parent.cookies.length;
375 }
376 }
377 }
378 return aIndex < this.rowCount - 1;
379 },
380 hasPreviousSibling: function (aIndex) {
381 if (!this._filtered) {
382 var item = this._getItemAtIndex(aIndex);
383 if (!item) return false;
384 var parent = this._getItemAtIndex(item.parentIndex);
385 if (parent && parent.container)
386 return aIndex > item.parentIndex + 1;
387 }
388 return aIndex > 0;
389 },
390 getLevel: function (aIndex) {
391 if (!this._filtered) {
392 var item = this._getItemAtIndex(aIndex);
393 if (!item) return 0;
394 return item.level;
395 }
396 return 0;
397 },
398 getImageSrc: function (aIndex, aColumn) {},
399 getProgressMode: function (aIndex, aColumn) {},
400 getCellValue: function (aIndex, aColumn) {},
401 setTree: function (aTree) {},
402 toggleOpenState: function (aIndex) {
403 if (!this._filtered) {
404 var item = this._getItemAtIndex(aIndex);
405 if (!item) return;
406 this._invalidateCache(aIndex);
407 var multiplier = item.open ? -1 : 1;
408 var delta = multiplier * item.cookies.length;
409 this._rowCount += delta;
410 item.open = !item.open;
411 gCookiesWindow._tree.treeBoxObject.rowCountChanged(aIndex + 1, delta);
412 gCookiesWindow._tree.treeBoxObject.invalidateRow(aIndex);
413 }
414 },
415 cycleHeader: function (aColumn) {},
416 selectionChanged: function () {},
417 cycleCell: function (aIndex, aColumn) {},
418 isEditable: function (aIndex, aColumn) {
419 return false;
420 },
421 isSelectable: function (aIndex, aColumn) {
422 return false;
423 },
424 setCellValue: function (aIndex, aColumn, aValue) {},
425 setCellText: function (aIndex, aColumn, aValue) {},
426 performAction: function (aAction) {},
427 performActionOnRow: function (aAction, aIndex) {},
428 performActionOnCell: function (aAction, aindex, aColumn) {}
429 },
431 _makeStrippedHost: function (aHost) {
432 var formattedHost = aHost.charAt(0) == "." ? aHost.substring(1, aHost.length) : aHost;
433 return formattedHost.substring(0, 4) == "www." ? formattedHost.substring(4, formattedHost.length) : formattedHost;
434 },
436 _addCookie: function (aStrippedHost, aCookie, aHostCount) {
437 if (!(aStrippedHost in this._hosts) || !this._hosts[aStrippedHost]) {
438 this._hosts[aStrippedHost] = { cookies : [],
439 rawHost : aStrippedHost,
440 level : 0,
441 open : false,
442 container : true };
443 this._hostOrder.push(aStrippedHost);
444 ++aHostCount.value;
445 }
447 var c = this._makeCookieObject(aStrippedHost, aCookie);
448 this._hosts[aStrippedHost].cookies.push(c);
449 },
451 _makeCookieObject: function (aStrippedHost, aCookie) {
452 var host = aCookie.host;
453 var formattedHost = host.charAt(0) == "." ? host.substring(1, host.length) : host;
454 var c = { name : aCookie.name,
455 value : aCookie.value,
456 isDomain : aCookie.isDomain,
457 host : aCookie.host,
458 rawHost : aStrippedHost,
459 path : aCookie.path,
460 isSecure : aCookie.isSecure,
461 expires : aCookie.expires,
462 level : 1,
463 container : false };
464 return c;
465 },
467 _loadCookies: function () {
468 var e = this._cm.enumerator;
469 var hostCount = { value: 0 };
470 this._hosts = {};
471 this._hostOrder = [];
472 while (e.hasMoreElements()) {
473 var cookie = e.getNext();
474 if (cookie && cookie instanceof Components.interfaces.nsICookie) {
475 var strippedHost = this._makeStrippedHost(cookie.host);
476 this._addCookie(strippedHost, cookie, hostCount);
477 }
478 else
479 break;
480 }
481 this._view._rowCount = hostCount.value;
482 },
484 formatExpiresString: function (aExpires) {
485 if (aExpires) {
486 var date = new Date(1000 * aExpires);
487 return this._ds.FormatDateTime("", this._ds.dateFormatLong,
488 this._ds.timeFormatSeconds,
489 date.getFullYear(),
490 date.getMonth() + 1,
491 date.getDate(),
492 date.getHours(),
493 date.getMinutes(),
494 date.getSeconds());
495 }
496 return this._bundle.getString("expireAtEndOfSession");
497 },
499 _updateCookieData: function (aItem) {
500 var seln = this._view.selection;
501 var ids = ["name", "value", "host", "path", "isSecure", "expires"];
502 var properties;
504 if (aItem && !aItem.container && seln.count > 0) {
505 properties = { name: aItem.name, value: aItem.value, host: aItem.host,
506 path: aItem.path, expires: this.formatExpiresString(aItem.expires),
507 isDomain: aItem.isDomain ? this._bundle.getString("domainColon")
508 : this._bundle.getString("hostColon"),
509 isSecure: aItem.isSecure ? this._bundle.getString("forSecureOnly")
510 : this._bundle.getString("forAnyConnection") };
511 for (var i = 0; i < ids.length; ++i)
512 document.getElementById(ids[i]).disabled = false;
513 }
514 else {
515 var noneSelected = this._bundle.getString("noCookieSelected");
516 properties = { name: noneSelected, value: noneSelected, host: noneSelected,
517 path: noneSelected, expires: noneSelected,
518 isSecure: noneSelected };
519 for (i = 0; i < ids.length; ++i)
520 document.getElementById(ids[i]).disabled = true;
521 }
522 for (var property in properties)
523 document.getElementById(property).value = properties[property];
524 },
526 onCookieSelected: function () {
527 var properties, item;
528 var seln = this._tree.view.selection;
529 if (!this._view._filtered)
530 item = this._view._getItemAtIndex(seln.currentIndex);
531 else
532 item = this._view._filterSet[seln.currentIndex];
534 this._updateCookieData(item);
536 var rangeCount = seln.getRangeCount();
537 var selectedCookieCount = 0;
538 for (var i = 0; i < rangeCount; ++i) {
539 var min = {}; var max = {};
540 seln.getRangeAt(i, min, max);
541 for (var j = min.value; j <= max.value; ++j) {
542 item = this._view._getItemAtIndex(j);
543 if (!item) continue;
544 if (item.container && !item.open)
545 selectedCookieCount += item.cookies.length;
546 else if (!item.container)
547 ++selectedCookieCount;
548 }
549 }
550 var item = this._view._getItemAtIndex(seln.currentIndex);
551 if (item && seln.count == 1 && item.container && item.open)
552 selectedCookieCount += 2;
554 var removeCookie = document.getElementById("removeCookie");
555 var removeCookies = document.getElementById("removeCookies");
556 removeCookie.parentNode.selectedPanel =
557 selectedCookieCount == 1 ? removeCookie : removeCookies;
559 removeCookie.disabled = removeCookies.disabled = !(seln.count > 0);
560 },
562 performDeletion: function gCookiesWindow_performDeletion(deleteItems) {
563 var psvc = Components.classes["@mozilla.org/preferences-service;1"]
564 .getService(Components.interfaces.nsIPrefBranch);
565 var blockFutureCookies = false;
566 if (psvc.prefHasUserValue("network.cookie.blockFutureCookies"))
567 blockFutureCookies = psvc.getBoolPref("network.cookie.blockFutureCookies");
568 for (var i = 0; i < deleteItems.length; ++i) {
569 var item = deleteItems[i];
570 this._cm.remove(item.host, item.name, item.path, blockFutureCookies);
571 }
572 },
574 deleteCookie: function () {
575 // Selection Notes
576 // - Selection always moves to *NEXT* adjacent item unless item
577 // is last child at a given level in which case it moves to *PREVIOUS*
578 // item
579 //
580 // Selection Cases (Somewhat Complicated)
581 //
582 // 1) Single cookie selected, host has single child
583 // v cnn.com
584 // //// cnn.com ///////////// goksdjf@ ////
585 // > atwola.com
586 //
587 // Before SelectedIndex: 1 Before RowCount: 3
588 // After SelectedIndex: 0 After RowCount: 1
589 //
590 // 2) Host selected, host open
591 // v goats.com ////////////////////////////
592 // goats.com sldkkfjl
593 // goat.scom flksj133
594 // > atwola.com
595 //
596 // Before SelectedIndex: 0 Before RowCount: 4
597 // After SelectedIndex: 0 After RowCount: 1
598 //
599 // 3) Host selected, host closed
600 // > goats.com ////////////////////////////
601 // > atwola.com
602 //
603 // Before SelectedIndex: 0 Before RowCount: 2
604 // After SelectedIndex: 0 After RowCount: 1
605 //
606 // 4) Single cookie selected, host has many children
607 // v goats.com
608 // goats.com sldkkfjl
609 // //// goats.com /////////// flksjl33 ////
610 // > atwola.com
611 //
612 // Before SelectedIndex: 2 Before RowCount: 4
613 // After SelectedIndex: 1 After RowCount: 3
614 //
615 // 5) Single cookie selected, host has many children
616 // v goats.com
617 // //// goats.com /////////// flksjl33 ////
618 // goats.com sldkkfjl
619 // > atwola.com
620 //
621 // Before SelectedIndex: 1 Before RowCount: 4
622 // After SelectedIndex: 1 After RowCount: 3
623 var seln = this._view.selection;
624 var tbo = this._tree.treeBoxObject;
626 if (seln.count < 1) return;
628 var nextSelected = 0;
629 var rowCountImpact = 0;
630 var deleteItems = [];
631 if (!this._view._filtered) {
632 var ci = seln.currentIndex;
633 nextSelected = ci;
634 var invalidateRow = -1;
635 var item = this._view._getItemAtIndex(ci);
636 if (item.container) {
637 rowCountImpact -= (item.open ? item.cookies.length : 0) + 1;
638 deleteItems = deleteItems.concat(item.cookies);
639 if (!this._view.hasNextSibling(-1, ci))
640 --nextSelected;
641 this._view._removeItemAtIndex(ci);
642 }
643 else {
644 var parent = this._view._getItemAtIndex(item.parentIndex);
645 --rowCountImpact;
646 if (parent.cookies.length == 1) {
647 --rowCountImpact;
648 deleteItems.push(item);
649 if (!this._view.hasNextSibling(-1, ci))
650 --nextSelected;
651 if (!this._view.hasNextSibling(-1, item.parentIndex))
652 --nextSelected;
653 this._view._removeItemAtIndex(item.parentIndex);
654 invalidateRow = item.parentIndex;
655 }
656 else {
657 deleteItems.push(item);
658 if (!this._view.hasNextSibling(-1, ci))
659 --nextSelected;
660 this._view._removeItemAtIndex(ci);
661 }
662 }
663 this._view._rowCount += rowCountImpact;
664 tbo.rowCountChanged(ci, rowCountImpact);
665 if (invalidateRow != -1)
666 tbo.invalidateRow(invalidateRow);
667 }
668 else {
669 var rangeCount = seln.getRangeCount();
670 // Traverse backwards through selections to avoid messing
671 // up the indices when they are deleted.
672 // See bug 388079.
673 for (var i = rangeCount - 1; i >= 0; --i) {
674 var min = {}; var max = {};
675 seln.getRangeAt(i, min, max);
676 nextSelected = min.value;
677 for (var j = min.value; j <= max.value; ++j) {
678 deleteItems.push(this._view._getItemAtIndex(j));
679 if (!this._view.hasNextSibling(-1, max.value))
680 --nextSelected;
681 }
682 var delta = max.value - min.value + 1;
683 this._view._removeItemAtIndex(min.value, delta);
684 rowCountImpact = -1 * delta;
685 this._view._rowCount += rowCountImpact;
686 tbo.rowCountChanged(min.value, rowCountImpact);
687 }
688 }
690 this.performDeletion(deleteItems);
692 if (nextSelected < 0)
693 seln.clearSelection();
694 else {
695 seln.select(nextSelected);
696 this._tree.focus();
697 }
698 },
700 deleteAllCookies: function () {
701 if (this._view._filtered) {
702 var rowCount = this._view.rowCount;
703 var deleteItems = [];
704 for (var index = 0; index < rowCount; index++) {
705 deleteItems.push(this._view._getItemAtIndex(index));
706 }
707 this._view._removeItemAtIndex(0, rowCount);
708 this._view._rowCount = 0;
709 this._tree.treeBoxObject.rowCountChanged(0, -rowCount);
710 this.performDeletion(deleteItems);
711 }
712 else {
713 this._cm.removeAll();
714 }
715 this._updateRemoveAllButton();
716 this.focusFilterBox();
717 },
719 onCookieKeyPress: function (aEvent) {
720 if (aEvent.keyCode == 46)
721 this.deleteCookie();
722 },
724 _lastSortProperty : "",
725 _lastSortAscending: false,
726 sort: function (aProperty) {
727 var ascending = (aProperty == this._lastSortProperty) ? !this._lastSortAscending : true;
728 // Sort the Non-Filtered Host Collections
729 if (aProperty == "rawHost") {
730 function sortByHost(a, b) {
731 return a.toLowerCase().localeCompare(b.toLowerCase());
732 }
733 this._hostOrder.sort(sortByHost);
734 if (!ascending)
735 this._hostOrder.reverse();
736 }
738 function sortByProperty(a, b) {
739 return a[aProperty].toLowerCase().localeCompare(b[aProperty].toLowerCase());
740 }
741 for (var host in this._hosts) {
742 var cookies = this._hosts[host].cookies;
743 cookies.sort(sortByProperty);
744 if (!ascending)
745 cookies.reverse();
746 }
747 // Sort the Filtered List, if in Filtered mode
748 if (this._view._filtered) {
749 this._view._filterSet.sort(sortByProperty);
750 if (!ascending)
751 this._view._filterSet.reverse();
752 }
754 // Adjust the Sort Indicator
755 var domainCol = document.getElementById("domainCol");
756 var nameCol = document.getElementById("nameCol");
757 var sortOrderString = ascending ? "ascending" : "descending";
758 if (aProperty == "rawHost") {
759 domainCol.setAttribute("sortDirection", sortOrderString);
760 nameCol.removeAttribute("sortDirection");
761 }
762 else {
763 nameCol.setAttribute("sortDirection", sortOrderString);
764 domainCol.removeAttribute("sortDirection");
765 }
767 this._view._invalidateCache(0);
768 this._view.selection.clearSelection();
769 this._view.selection.select(0);
770 this._tree.treeBoxObject.invalidate();
771 this._tree.treeBoxObject.ensureRowIsVisible(0);
773 this._lastSortAscending = ascending;
774 this._lastSortProperty = aProperty;
775 },
777 clearFilter: function () {
778 // Revert to single-select in the tree
779 this._tree.setAttribute("seltype", "single");
781 // Clear the Tree Display
782 this._view._filtered = false;
783 this._view._rowCount = 0;
784 this._tree.treeBoxObject.rowCountChanged(0, -this._view._filterSet.length);
785 this._view._filterSet = [];
787 // Just reload the list to make sure deletions are respected
788 this._loadCookies();
789 this._tree.treeBoxObject.view = this._view;
791 // Restore sort order
792 var sortby = this._lastSortProperty;
793 if (sortby == "") {
794 this._lastSortAscending = false;
795 this.sort("rawHost");
796 }
797 else {
798 this._lastSortAscending = !this._lastSortAscending;
799 this.sort(sortby);
800 }
802 // Restore open state
803 for (var i = 0; i < this._openIndices.length; ++i)
804 this._view.toggleOpenState(this._openIndices[i]);
805 this._openIndices = [];
807 // Restore selection
808 this._view.selection.clearSelection();
809 for (i = 0; i < this._lastSelectedRanges.length; ++i) {
810 var range = this._lastSelectedRanges[i];
811 this._view.selection.rangedSelect(range.min, range.max, true);
812 }
813 this._lastSelectedRanges = [];
815 document.getElementById("cookiesIntro").value = this._bundle.getString("cookiesAll");
816 this._updateRemoveAllButton();
817 },
819 _cookieMatchesFilter: function (aCookie) {
820 return aCookie.rawHost.indexOf(this._view._filterValue) != -1 ||
821 aCookie.name.indexOf(this._view._filterValue) != -1 ||
822 aCookie.value.indexOf(this._view._filterValue) != -1;
823 },
825 _filterCookies: function (aFilterValue) {
826 this._view._filterValue = aFilterValue;
827 var cookies = [];
828 for (var i = 0; i < gCookiesWindow._hostOrder.length; ++i) { //var host in gCookiesWindow._hosts) {
829 var currHost = gCookiesWindow._hosts[gCookiesWindow._hostOrder[i]]; // gCookiesWindow._hosts[host];
830 if (!currHost) continue;
831 for (var j = 0; j < currHost.cookies.length; ++j) {
832 var cookie = currHost.cookies[j];
833 if (this._cookieMatchesFilter(cookie))
834 cookies.push(cookie);
835 }
836 }
837 return cookies;
838 },
840 _lastSelectedRanges: [],
841 _openIndices: [],
842 _saveState: function () {
843 // Save selection
844 var seln = this._view.selection;
845 this._lastSelectedRanges = [];
846 var rangeCount = seln.getRangeCount();
847 for (var i = 0; i < rangeCount; ++i) {
848 var min = {}; var max = {};
849 seln.getRangeAt(i, min, max);
850 this._lastSelectedRanges.push({ min: min.value, max: max.value });
851 }
853 // Save open states
854 this._openIndices = [];
855 for (i = 0; i < this._view.rowCount; ++i) {
856 var item = this._view._getItemAtIndex(i);
857 if (item && item.container && item.open)
858 this._openIndices.push(i);
859 }
860 },
862 _updateRemoveAllButton: function gCookiesWindow__updateRemoveAllButton() {
863 document.getElementById("removeAllCookies").disabled = this._view._rowCount == 0;
864 },
866 filter: function () {
867 var filter = document.getElementById("filter").value;
868 if (filter == "") {
869 gCookiesWindow.clearFilter();
870 return;
871 }
872 var view = gCookiesWindow._view;
873 view._filterSet = gCookiesWindow._filterCookies(filter);
874 if (!view._filtered) {
875 // Save Display Info for the Non-Filtered mode when we first
876 // enter Filtered mode.
877 gCookiesWindow._saveState();
878 view._filtered = true;
879 }
880 // Move to multi-select in the tree
881 gCookiesWindow._tree.setAttribute("seltype", "multiple");
883 // Clear the display
884 var oldCount = view._rowCount;
885 view._rowCount = 0;
886 gCookiesWindow._tree.treeBoxObject.rowCountChanged(0, -oldCount);
887 // Set up the filtered display
888 view._rowCount = view._filterSet.length;
889 gCookiesWindow._tree.treeBoxObject.rowCountChanged(0, view.rowCount);
891 // if the view is not empty then select the first item
892 if (view.rowCount > 0)
893 view.selection.select(0);
895 document.getElementById("cookiesIntro").value = gCookiesWindow._bundle.getString("cookiesFiltered");
896 this._updateRemoveAllButton();
897 },
899 setFilter: function (aFilterString) {
900 document.getElementById("filter").value = aFilterString;
901 this.filter();
902 },
904 focusFilterBox: function () {
905 var filter = document.getElementById("filter");
906 filter.focus();
907 filter.select();
908 },
910 onWindowKeyPress: function (aEvent) {
911 if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE)
912 window.close();
913 }
914 };