|
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/. */ |
|
5 |
|
6 const nsICookie = Components.interfaces.nsICookie; |
|
7 |
|
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, |
|
17 |
|
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); |
|
23 |
|
24 this._bundle = document.getElementById("bundlePreferences"); |
|
25 this._tree = document.getElementById("cookiesList"); |
|
26 |
|
27 this._populateList(true); |
|
28 |
|
29 document.getElementById("filter").focus(); |
|
30 }, |
|
31 |
|
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 }, |
|
38 |
|
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); |
|
46 |
|
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 } |
|
57 |
|
58 this._updateRemoveAllButton(); |
|
59 |
|
60 this._saveState(); |
|
61 }, |
|
62 |
|
63 _cookieEquals: function (aCookieA, aCookieB, aStrippedHost) { |
|
64 return aCookieA.rawHost == aStrippedHost && |
|
65 aCookieA.name == aCookieB.name && |
|
66 aCookieA.path == aCookieB.path; |
|
67 }, |
|
68 |
|
69 observe: function (aCookie, aTopic, aData) { |
|
70 if (aTopic != "cookie-changed") |
|
71 return; |
|
72 |
|
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 = []; |
|
83 |
|
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"); |
|
93 |
|
94 // then, reload the list |
|
95 this._populateList(false); |
|
96 } |
|
97 |
|
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 }, |
|
101 |
|
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 } |
|
145 |
|
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 }, |
|
152 |
|
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); |
|
178 |
|
179 this._updateRemoveAllButton(); |
|
180 }, |
|
181 |
|
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 }, |
|
192 |
|
193 _getItemAtIndex: function (aIndex) { |
|
194 if (this._filtered) |
|
195 return this._filterSet[aIndex]; |
|
196 |
|
197 var start = 0; |
|
198 var count = 0, hostIndex = 0; |
|
199 |
|
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 } |
|
206 |
|
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; |
|
213 |
|
214 var cacheEntry = { 'start' : i, 'count' : count }; |
|
215 var cacheStart = count; |
|
216 |
|
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; |
|
241 |
|
242 for (var j = cacheStart; j < count; j++) |
|
243 this._cacheItems[j] = cacheEntry; |
|
244 this._cacheValid = count - 1; |
|
245 } |
|
246 return null; |
|
247 }, |
|
248 |
|
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 } |
|
267 |
|
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 }, |
|
283 |
|
284 _invalidateCache: function (aIndex) { |
|
285 this._cacheValid = Math.min(this._cacheValid, aIndex); |
|
286 }, |
|
287 |
|
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 }, |
|
306 |
|
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 }, |
|
430 |
|
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 }, |
|
435 |
|
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 } |
|
446 |
|
447 var c = this._makeCookieObject(aStrippedHost, aCookie); |
|
448 this._hosts[aStrippedHost].cookies.push(c); |
|
449 }, |
|
450 |
|
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 }, |
|
466 |
|
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 }, |
|
483 |
|
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 }, |
|
498 |
|
499 _updateCookieData: function (aItem) { |
|
500 var seln = this._view.selection; |
|
501 var ids = ["name", "value", "host", "path", "isSecure", "expires"]; |
|
502 var properties; |
|
503 |
|
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 }, |
|
525 |
|
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]; |
|
533 |
|
534 this._updateCookieData(item); |
|
535 |
|
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; |
|
553 |
|
554 var removeCookie = document.getElementById("removeCookie"); |
|
555 var removeCookies = document.getElementById("removeCookies"); |
|
556 removeCookie.parentNode.selectedPanel = |
|
557 selectedCookieCount == 1 ? removeCookie : removeCookies; |
|
558 |
|
559 removeCookie.disabled = removeCookies.disabled = !(seln.count > 0); |
|
560 }, |
|
561 |
|
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 }, |
|
573 |
|
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; |
|
625 |
|
626 if (seln.count < 1) return; |
|
627 |
|
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 } |
|
689 |
|
690 this.performDeletion(deleteItems); |
|
691 |
|
692 if (nextSelected < 0) |
|
693 seln.clearSelection(); |
|
694 else { |
|
695 seln.select(nextSelected); |
|
696 this._tree.focus(); |
|
697 } |
|
698 }, |
|
699 |
|
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 }, |
|
718 |
|
719 onCookieKeyPress: function (aEvent) { |
|
720 if (aEvent.keyCode == 46) |
|
721 this.deleteCookie(); |
|
722 }, |
|
723 |
|
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 } |
|
737 |
|
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 } |
|
753 |
|
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 } |
|
766 |
|
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); |
|
772 |
|
773 this._lastSortAscending = ascending; |
|
774 this._lastSortProperty = aProperty; |
|
775 }, |
|
776 |
|
777 clearFilter: function () { |
|
778 // Revert to single-select in the tree |
|
779 this._tree.setAttribute("seltype", "single"); |
|
780 |
|
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 = []; |
|
786 |
|
787 // Just reload the list to make sure deletions are respected |
|
788 this._loadCookies(); |
|
789 this._tree.treeBoxObject.view = this._view; |
|
790 |
|
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 } |
|
801 |
|
802 // Restore open state |
|
803 for (var i = 0; i < this._openIndices.length; ++i) |
|
804 this._view.toggleOpenState(this._openIndices[i]); |
|
805 this._openIndices = []; |
|
806 |
|
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 = []; |
|
814 |
|
815 document.getElementById("cookiesIntro").value = this._bundle.getString("cookiesAll"); |
|
816 this._updateRemoveAllButton(); |
|
817 }, |
|
818 |
|
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 }, |
|
824 |
|
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 }, |
|
839 |
|
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 } |
|
852 |
|
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 }, |
|
861 |
|
862 _updateRemoveAllButton: function gCookiesWindow__updateRemoveAllButton() { |
|
863 document.getElementById("removeAllCookies").disabled = this._view._rowCount == 0; |
|
864 }, |
|
865 |
|
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"); |
|
882 |
|
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); |
|
890 |
|
891 // if the view is not empty then select the first item |
|
892 if (view.rowCount > 0) |
|
893 view.selection.select(0); |
|
894 |
|
895 document.getElementById("cookiesIntro").value = gCookiesWindow._bundle.getString("cookiesFiltered"); |
|
896 this._updateRemoveAllButton(); |
|
897 }, |
|
898 |
|
899 setFilter: function (aFilterString) { |
|
900 document.getElementById("filter").value = aFilterString; |
|
901 this.filter(); |
|
902 }, |
|
903 |
|
904 focusFilterBox: function () { |
|
905 var filter = document.getElementById("filter"); |
|
906 filter.focus(); |
|
907 filter.select(); |
|
908 }, |
|
909 |
|
910 onWindowKeyPress: function (aEvent) { |
|
911 if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE) |
|
912 window.close(); |
|
913 } |
|
914 }; |