browser/components/preferences/cookies.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/components/preferences/cookies.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,914 @@
     1.4 +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +const nsICookie = Components.interfaces.nsICookie;
    1.10 +
    1.11 +var gCookiesWindow = {
    1.12 +  _cm               : Components.classes["@mozilla.org/cookiemanager;1"]
    1.13 +                                .getService(Components.interfaces.nsICookieManager),
    1.14 +  _ds               : Components.classes["@mozilla.org/intl/scriptabledateformat;1"]
    1.15 +                                .getService(Components.interfaces.nsIScriptableDateFormat),
    1.16 +  _hosts            : {},
    1.17 +  _hostOrder        : [],
    1.18 +  _tree             : null,
    1.19 +  _bundle           : null,
    1.20 +
    1.21 +  init: function () {
    1.22 +    var os = Components.classes["@mozilla.org/observer-service;1"]
    1.23 +                       .getService(Components.interfaces.nsIObserverService);
    1.24 +    os.addObserver(this, "cookie-changed", false);
    1.25 +    os.addObserver(this, "perm-changed", false);
    1.26 +
    1.27 +    this._bundle = document.getElementById("bundlePreferences");
    1.28 +    this._tree = document.getElementById("cookiesList");
    1.29 +
    1.30 +    this._populateList(true);
    1.31 +
    1.32 +    document.getElementById("filter").focus();
    1.33 +  },
    1.34 +
    1.35 +  uninit: function () {
    1.36 +    var os = Components.classes["@mozilla.org/observer-service;1"]
    1.37 +                       .getService(Components.interfaces.nsIObserverService);
    1.38 +    os.removeObserver(this, "cookie-changed");
    1.39 +    os.removeObserver(this, "perm-changed");
    1.40 +  },
    1.41 +
    1.42 +  _populateList: function (aInitialLoad) {
    1.43 +    this._loadCookies();
    1.44 +    this._tree.treeBoxObject.view = this._view;
    1.45 +    if (aInitialLoad)
    1.46 +      this.sort("rawHost");
    1.47 +    if (this._view.rowCount > 0)
    1.48 +      this._tree.view.selection.select(0);
    1.49 +
    1.50 +    if (aInitialLoad) {
    1.51 +      if ("arguments" in window &&
    1.52 +          window.arguments[0] &&
    1.53 +          window.arguments[0].filterString)
    1.54 +        this.setFilter(window.arguments[0].filterString);
    1.55 +    }
    1.56 +    else {
    1.57 +      if (document.getElementById("filter").value != "")
    1.58 +        this.filter();
    1.59 +    }
    1.60 +
    1.61 +    this._updateRemoveAllButton();
    1.62 +
    1.63 +    this._saveState();
    1.64 +  },
    1.65 +
    1.66 +  _cookieEquals: function (aCookieA, aCookieB, aStrippedHost) {
    1.67 +    return aCookieA.rawHost == aStrippedHost &&
    1.68 +           aCookieA.name == aCookieB.name &&
    1.69 +           aCookieA.path == aCookieB.path;
    1.70 +  },
    1.71 +
    1.72 +  observe: function (aCookie, aTopic, aData) {
    1.73 +    if (aTopic != "cookie-changed")
    1.74 +      return;
    1.75 +
    1.76 +    if (aCookie instanceof Components.interfaces.nsICookie) {
    1.77 +      var strippedHost = this._makeStrippedHost(aCookie.host);
    1.78 +      if (aData == "changed")
    1.79 +        this._handleCookieChanged(aCookie, strippedHost);
    1.80 +      else if (aData == "added")
    1.81 +        this._handleCookieAdded(aCookie, strippedHost);
    1.82 +    }
    1.83 +    else if (aData == "cleared") {
    1.84 +      this._hosts = {};
    1.85 +      this._hostOrder = [];
    1.86 +
    1.87 +      var oldRowCount = this._view._rowCount;
    1.88 +      this._view._rowCount = 0;
    1.89 +      this._tree.treeBoxObject.rowCountChanged(0, -oldRowCount);
    1.90 +      this._view.selection.clearSelection();
    1.91 +      this._updateRemoveAllButton();
    1.92 +    }
    1.93 +    else if (aData == "reload") {
    1.94 +      // first, clear any existing entries
    1.95 +      this.observe(aCookie, aTopic, "cleared");
    1.96 +
    1.97 +      // then, reload the list
    1.98 +      this._populateList(false);
    1.99 +    }
   1.100 +
   1.101 +    // We don't yet handle aData == "deleted" - it's a less common case
   1.102 +    // and is rather complicated as selection tracking is difficult
   1.103 +  },
   1.104 +
   1.105 +  _handleCookieChanged: function (changedCookie, strippedHost) {
   1.106 +    var rowIndex = 0;
   1.107 +    var cookieItem = null;
   1.108 +    if (!this._view._filtered) {
   1.109 +      for (var i = 0; i < this._hostOrder.length; ++i) { // (var host in this._hosts) {
   1.110 +        ++rowIndex;
   1.111 +        var hostItem = this._hosts[this._hostOrder[i]]; // var hostItem = this._hosts[host];
   1.112 +        if (this._hostOrder[i] == strippedHost) { // host == strippedHost) {
   1.113 +          // Host matches, look for the cookie within this Host collection
   1.114 +          // and update its data
   1.115 +          for (var j = 0; j < hostItem.cookies.length; ++j) {
   1.116 +            ++rowIndex;
   1.117 +            var currCookie = hostItem.cookies[j];
   1.118 +            if (this._cookieEquals(currCookie, changedCookie, strippedHost)) {
   1.119 +              currCookie.value    = changedCookie.value;
   1.120 +              currCookie.isSecure = changedCookie.isSecure;
   1.121 +              currCookie.isDomain = changedCookie.isDomain;
   1.122 +              currCookie.expires  = changedCookie.expires;
   1.123 +              cookieItem = currCookie;
   1.124 +              break;
   1.125 +            }
   1.126 +          }
   1.127 +        }
   1.128 +        else if (hostItem.open)
   1.129 +          rowIndex += hostItem.cookies.length;
   1.130 +      }
   1.131 +    }
   1.132 +    else {
   1.133 +      // Just walk the filter list to find the item. It doesn't matter that
   1.134 +      // we don't update the main Host collection when we do this, because
   1.135 +      // when the filter is reset the Host collection is rebuilt anyway.
   1.136 +      for (rowIndex = 0; rowIndex < this._view._filterSet.length; ++rowIndex) {
   1.137 +        currCookie = this._view._filterSet[rowIndex];
   1.138 +        if (this._cookieEquals(currCookie, changedCookie, strippedHost)) {
   1.139 +          currCookie.value    = changedCookie.value;
   1.140 +          currCookie.isSecure = changedCookie.isSecure;
   1.141 +          currCookie.isDomain = changedCookie.isDomain;
   1.142 +          currCookie.expires  = changedCookie.expires;
   1.143 +          cookieItem = currCookie;
   1.144 +          break;
   1.145 +        }
   1.146 +      }
   1.147 +    }
   1.148 +
   1.149 +    // Make sure the tree display is up to date...
   1.150 +    this._tree.treeBoxObject.invalidateRow(rowIndex);
   1.151 +    // ... and if the cookie is selected, update the displayed metadata too
   1.152 +    if (cookieItem != null && this._view.selection.currentIndex == rowIndex)
   1.153 +      this._updateCookieData(cookieItem);
   1.154 +  },
   1.155 +
   1.156 +  _handleCookieAdded: function (changedCookie, strippedHost) {
   1.157 +    var rowCountImpact = 0;
   1.158 +    var addedHost = { value: 0 };
   1.159 +    this._addCookie(strippedHost, changedCookie, addedHost);
   1.160 +    if (!this._view._filtered) {
   1.161 +      // The Host collection for this cookie already exists, and it's not open,
   1.162 +      // so don't increment the rowCountImpact becaues the user is not going to
   1.163 +      // see the additional rows as they're hidden.
   1.164 +      if (addedHost.value || this._hosts[strippedHost].open)
   1.165 +        ++rowCountImpact;
   1.166 +    }
   1.167 +    else {
   1.168 +      // We're in search mode, and the cookie being added matches
   1.169 +      // the search condition, so add it to the list.
   1.170 +      var c = this._makeCookieObject(strippedHost, changedCookie);
   1.171 +      if (this._cookieMatchesFilter(c)) {
   1.172 +        this._view._filterSet.push(this._makeCookieObject(strippedHost, changedCookie));
   1.173 +        ++rowCountImpact;
   1.174 +      }
   1.175 +    }
   1.176 +    // Now update the tree display at the end (we could/should re run the sort
   1.177 +    // if any to get the position correct.)
   1.178 +    var oldRowCount = this._rowCount;
   1.179 +    this._view._rowCount += rowCountImpact;
   1.180 +    this._tree.treeBoxObject.rowCountChanged(oldRowCount - 1, rowCountImpact);
   1.181 +
   1.182 +    this._updateRemoveAllButton();
   1.183 +  },
   1.184 +
   1.185 +  _view: {
   1.186 +    _filtered   : false,
   1.187 +    _filterSet  : [],
   1.188 +    _filterValue: "",
   1.189 +    _rowCount   : 0,
   1.190 +    _cacheValid : 0,
   1.191 +    _cacheItems : [],
   1.192 +    get rowCount() {
   1.193 +      return this._rowCount;
   1.194 +    },
   1.195 +
   1.196 +    _getItemAtIndex: function (aIndex) {
   1.197 +      if (this._filtered)
   1.198 +        return this._filterSet[aIndex];
   1.199 +
   1.200 +      var start = 0;
   1.201 +      var count = 0, hostIndex = 0;
   1.202 +
   1.203 +      var cacheIndex = Math.min(this._cacheValid, aIndex);
   1.204 +      if (cacheIndex > 0) {
   1.205 +        var cacheItem = this._cacheItems[cacheIndex];
   1.206 +        start = cacheItem['start'];
   1.207 +        count = hostIndex = cacheItem['count'];
   1.208 +      }
   1.209 +
   1.210 +      for (var i = start; i < gCookiesWindow._hostOrder.length; ++i) { // var host in gCookiesWindow._hosts) {
   1.211 +        var currHost = gCookiesWindow._hosts[gCookiesWindow._hostOrder[i]]; // gCookiesWindow._hosts[host];
   1.212 +        if (!currHost) continue;
   1.213 +        if (count == aIndex)
   1.214 +          return currHost;
   1.215 +        hostIndex = count;
   1.216 +
   1.217 +        var cacheEntry = { 'start' : i, 'count' : count };
   1.218 +        var cacheStart = count;
   1.219 +
   1.220 +        if (currHost.open) {
   1.221 +          if (count < aIndex && aIndex <= (count + currHost.cookies.length)) {
   1.222 +            // We are looking for an entry within this host's children,
   1.223 +            // enumerate them looking for the index.
   1.224 +            ++count;
   1.225 +            for (var i = 0; i < currHost.cookies.length; ++i) {
   1.226 +              if (count == aIndex) {
   1.227 +                var cookie = currHost.cookies[i];
   1.228 +                cookie.parentIndex = hostIndex;
   1.229 +                return cookie;
   1.230 +              }
   1.231 +              ++count;
   1.232 +            }
   1.233 +          }
   1.234 +          else {
   1.235 +            // A host entry was open, but we weren't looking for an index
   1.236 +            // within that host entry's children, so skip forward over the
   1.237 +            // entry's children. We need to add one to increment for the
   1.238 +            // host value too.
   1.239 +            count += currHost.cookies.length + 1;
   1.240 +          }
   1.241 +        }
   1.242 +        else
   1.243 +          ++count;
   1.244 +
   1.245 +        for (var j = cacheStart; j < count; j++)
   1.246 +          this._cacheItems[j] = cacheEntry;
   1.247 +        this._cacheValid = count - 1;
   1.248 +      }
   1.249 +      return null;
   1.250 +    },
   1.251 +
   1.252 +    _removeItemAtIndex: function (aIndex, aCount) {
   1.253 +      var removeCount = aCount === undefined ? 1 : aCount;
   1.254 +      if (this._filtered) {
   1.255 +        // remove the cookies from the unfiltered set so that they
   1.256 +        // don't reappear when the filter is changed. See bug 410863.
   1.257 +        for (var i = aIndex; i < aIndex + removeCount; ++i) {
   1.258 +          var item = this._filterSet[i];
   1.259 +          var parent = gCookiesWindow._hosts[item.rawHost];
   1.260 +          for (var j = 0; j < parent.cookies.length; ++j) {
   1.261 +            if (item == parent.cookies[j]) {
   1.262 +              parent.cookies.splice(j, 1);
   1.263 +              break;
   1.264 +            }
   1.265 +          }
   1.266 +        }
   1.267 +        this._filterSet.splice(aIndex, removeCount);
   1.268 +        return;
   1.269 +      }
   1.270 +
   1.271 +      var item = this._getItemAtIndex(aIndex);
   1.272 +      if (!item) return;
   1.273 +      this._invalidateCache(aIndex - 1);
   1.274 +      if (item.container)
   1.275 +        gCookiesWindow._hosts[item.rawHost] = null;
   1.276 +      else {
   1.277 +        var parent = this._getItemAtIndex(item.parentIndex);
   1.278 +        for (var i = 0; i < parent.cookies.length; ++i) {
   1.279 +          var cookie = parent.cookies[i];
   1.280 +          if (item.rawHost == cookie.rawHost &&
   1.281 +              item.name == cookie.name && item.path == cookie.path)
   1.282 +            parent.cookies.splice(i, removeCount);
   1.283 +        }
   1.284 +      }
   1.285 +    },
   1.286 +
   1.287 +    _invalidateCache: function (aIndex) {
   1.288 +      this._cacheValid = Math.min(this._cacheValid, aIndex);
   1.289 +    },
   1.290 +
   1.291 +    getCellText: function (aIndex, aColumn) {
   1.292 +      if (!this._filtered) {
   1.293 +        var item = this._getItemAtIndex(aIndex);
   1.294 +        if (!item)
   1.295 +          return "";
   1.296 +        if (aColumn.id == "domainCol")
   1.297 +          return item.rawHost;
   1.298 +        else if (aColumn.id == "nameCol")
   1.299 +          return item.name;
   1.300 +      }
   1.301 +      else {
   1.302 +        if (aColumn.id == "domainCol")
   1.303 +          return this._filterSet[aIndex].rawHost;
   1.304 +        else if (aColumn.id == "nameCol")
   1.305 +          return this._filterSet[aIndex].name;
   1.306 +      }
   1.307 +      return "";
   1.308 +    },
   1.309 +
   1.310 +    _selection: null,
   1.311 +    get selection () { return this._selection; },
   1.312 +    set selection (val) { this._selection = val; return val; },
   1.313 +    getRowProperties: function (aIndex) { return ""; },
   1.314 +    getCellProperties: function (aIndex, aColumn) { return ""; },
   1.315 +    getColumnProperties: function (aColumn) { return ""; },
   1.316 +    isContainer: function (aIndex) {
   1.317 +      if (!this._filtered) {
   1.318 +        var item = this._getItemAtIndex(aIndex);
   1.319 +        if (!item) return false;
   1.320 +        return item.container;
   1.321 +      }
   1.322 +      return false;
   1.323 +    },
   1.324 +    isContainerOpen: function (aIndex) {
   1.325 +      if (!this._filtered) {
   1.326 +        var item = this._getItemAtIndex(aIndex);
   1.327 +        if (!item) return false;
   1.328 +        return item.open;
   1.329 +      }
   1.330 +      return false;
   1.331 +    },
   1.332 +    isContainerEmpty: function (aIndex) {
   1.333 +      if (!this._filtered) {
   1.334 +        var item = this._getItemAtIndex(aIndex);
   1.335 +        if (!item) return false;
   1.336 +        return item.cookies.length == 0;
   1.337 +      }
   1.338 +      return false;
   1.339 +    },
   1.340 +    isSeparator: function (aIndex) { return false; },
   1.341 +    isSorted: function (aIndex) { return false; },
   1.342 +    canDrop: function (aIndex, aOrientation) { return false; },
   1.343 +    drop: function (aIndex, aOrientation) {},
   1.344 +    getParentIndex: function (aIndex) {
   1.345 +      if (!this._filtered) {
   1.346 +        var item = this._getItemAtIndex(aIndex);
   1.347 +        // If an item has no parent index (i.e. it is at the top level) this
   1.348 +        // function MUST return -1 otherwise we will go into an infinite loop.
   1.349 +        // Containers are always top level items in the cookies tree, so make
   1.350 +        // sure to return the appropriate value here.
   1.351 +        if (!item || item.container) return -1;
   1.352 +        return item.parentIndex;
   1.353 +      }
   1.354 +      return -1;
   1.355 +    },
   1.356 +    hasNextSibling: function (aParentIndex, aIndex) {
   1.357 +      if (!this._filtered) {
   1.358 +        // |aParentIndex| appears to be bogus, but we can get the real
   1.359 +        // parent index by getting the entry for |aIndex| and reading the
   1.360 +        // parentIndex field.
   1.361 +        // The index of the last item in this host collection is the
   1.362 +        // index of the parent + the size of the host collection, and
   1.363 +        // aIndex has a next sibling if it is less than this value.
   1.364 +        var item = this._getItemAtIndex(aIndex);
   1.365 +        if (item) {
   1.366 +          if (item.container) {
   1.367 +            for (var i = aIndex + 1; i < this.rowCount; ++i) {
   1.368 +              var subsequent = this._getItemAtIndex(i);
   1.369 +              if (subsequent.container)
   1.370 +                return true;
   1.371 +            }
   1.372 +            return false;
   1.373 +          }
   1.374 +          else {
   1.375 +            var parent = this._getItemAtIndex(item.parentIndex);
   1.376 +            if (parent && parent.container)
   1.377 +              return aIndex < item.parentIndex + parent.cookies.length;
   1.378 +          }
   1.379 +        }
   1.380 +      }
   1.381 +      return aIndex < this.rowCount - 1;
   1.382 +    },
   1.383 +    hasPreviousSibling: function (aIndex) {
   1.384 +      if (!this._filtered) {
   1.385 +        var item = this._getItemAtIndex(aIndex);
   1.386 +        if (!item) return false;
   1.387 +        var parent = this._getItemAtIndex(item.parentIndex);
   1.388 +        if (parent && parent.container)
   1.389 +          return aIndex > item.parentIndex + 1;
   1.390 +      }
   1.391 +      return aIndex > 0;
   1.392 +    },
   1.393 +    getLevel: function (aIndex) {
   1.394 +      if (!this._filtered) {
   1.395 +        var item = this._getItemAtIndex(aIndex);
   1.396 +        if (!item) return 0;
   1.397 +        return item.level;
   1.398 +      }
   1.399 +      return 0;
   1.400 +    },
   1.401 +    getImageSrc: function (aIndex, aColumn) {},
   1.402 +    getProgressMode: function (aIndex, aColumn) {},
   1.403 +    getCellValue: function (aIndex, aColumn) {},
   1.404 +    setTree: function (aTree) {},
   1.405 +    toggleOpenState: function (aIndex) {
   1.406 +      if (!this._filtered) {
   1.407 +        var item = this._getItemAtIndex(aIndex);
   1.408 +        if (!item) return;
   1.409 +        this._invalidateCache(aIndex);
   1.410 +        var multiplier = item.open ? -1 : 1;
   1.411 +        var delta = multiplier * item.cookies.length;
   1.412 +        this._rowCount += delta;
   1.413 +        item.open = !item.open;
   1.414 +        gCookiesWindow._tree.treeBoxObject.rowCountChanged(aIndex + 1, delta);
   1.415 +        gCookiesWindow._tree.treeBoxObject.invalidateRow(aIndex);
   1.416 +      }
   1.417 +    },
   1.418 +    cycleHeader: function (aColumn) {},
   1.419 +    selectionChanged: function () {},
   1.420 +    cycleCell: function (aIndex, aColumn) {},
   1.421 +    isEditable: function (aIndex, aColumn) {
   1.422 +      return false;
   1.423 +    },
   1.424 +    isSelectable: function (aIndex, aColumn) {
   1.425 +      return false;
   1.426 +    },
   1.427 +    setCellValue: function (aIndex, aColumn, aValue) {},
   1.428 +    setCellText: function (aIndex, aColumn, aValue) {},
   1.429 +    performAction: function (aAction) {},
   1.430 +    performActionOnRow: function (aAction, aIndex) {},
   1.431 +    performActionOnCell: function (aAction, aindex, aColumn) {}
   1.432 +  },
   1.433 +
   1.434 +  _makeStrippedHost: function (aHost) {
   1.435 +    var formattedHost = aHost.charAt(0) == "." ? aHost.substring(1, aHost.length) : aHost;
   1.436 +    return formattedHost.substring(0, 4) == "www." ? formattedHost.substring(4, formattedHost.length) : formattedHost;
   1.437 +  },
   1.438 +
   1.439 +  _addCookie: function (aStrippedHost, aCookie, aHostCount) {
   1.440 +    if (!(aStrippedHost in this._hosts) || !this._hosts[aStrippedHost]) {
   1.441 +      this._hosts[aStrippedHost] = { cookies   : [],
   1.442 +                                     rawHost   : aStrippedHost,
   1.443 +                                     level     : 0,
   1.444 +                                     open      : false,
   1.445 +                                     container : true };
   1.446 +      this._hostOrder.push(aStrippedHost);
   1.447 +      ++aHostCount.value;
   1.448 +    }
   1.449 +
   1.450 +    var c = this._makeCookieObject(aStrippedHost, aCookie);
   1.451 +    this._hosts[aStrippedHost].cookies.push(c);
   1.452 +  },
   1.453 +
   1.454 +  _makeCookieObject: function (aStrippedHost, aCookie) {
   1.455 +    var host = aCookie.host;
   1.456 +    var formattedHost = host.charAt(0) == "." ? host.substring(1, host.length) : host;
   1.457 +    var c = { name        : aCookie.name,
   1.458 +              value       : aCookie.value,
   1.459 +              isDomain    : aCookie.isDomain,
   1.460 +              host        : aCookie.host,
   1.461 +              rawHost     : aStrippedHost,
   1.462 +              path        : aCookie.path,
   1.463 +              isSecure    : aCookie.isSecure,
   1.464 +              expires     : aCookie.expires,
   1.465 +              level       : 1,
   1.466 +              container   : false };
   1.467 +    return c;
   1.468 +  },
   1.469 +
   1.470 +  _loadCookies: function () {
   1.471 +    var e = this._cm.enumerator;
   1.472 +    var hostCount = { value: 0 };
   1.473 +    this._hosts = {};
   1.474 +    this._hostOrder = [];
   1.475 +    while (e.hasMoreElements()) {
   1.476 +      var cookie = e.getNext();
   1.477 +      if (cookie && cookie instanceof Components.interfaces.nsICookie) {
   1.478 +        var strippedHost = this._makeStrippedHost(cookie.host);
   1.479 +        this._addCookie(strippedHost, cookie, hostCount);
   1.480 +      }
   1.481 +      else
   1.482 +        break;
   1.483 +    }
   1.484 +    this._view._rowCount = hostCount.value;
   1.485 +  },
   1.486 +
   1.487 +  formatExpiresString: function (aExpires) {
   1.488 +    if (aExpires) {
   1.489 +      var date = new Date(1000 * aExpires);
   1.490 +      return this._ds.FormatDateTime("", this._ds.dateFormatLong,
   1.491 +                                     this._ds.timeFormatSeconds,
   1.492 +                                     date.getFullYear(),
   1.493 +                                     date.getMonth() + 1,
   1.494 +                                     date.getDate(),
   1.495 +                                     date.getHours(),
   1.496 +                                     date.getMinutes(),
   1.497 +                                     date.getSeconds());
   1.498 +    }
   1.499 +    return this._bundle.getString("expireAtEndOfSession");
   1.500 +  },
   1.501 +
   1.502 +  _updateCookieData: function (aItem) {
   1.503 +    var seln = this._view.selection;
   1.504 +    var ids = ["name", "value", "host", "path", "isSecure", "expires"];
   1.505 +    var properties;
   1.506 +
   1.507 +    if (aItem && !aItem.container && seln.count > 0) {
   1.508 +      properties = { name: aItem.name, value: aItem.value, host: aItem.host,
   1.509 +                     path: aItem.path, expires: this.formatExpiresString(aItem.expires),
   1.510 +                     isDomain: aItem.isDomain ? this._bundle.getString("domainColon")
   1.511 +                                              : this._bundle.getString("hostColon"),
   1.512 +                     isSecure: aItem.isSecure ? this._bundle.getString("forSecureOnly")
   1.513 +                                              : this._bundle.getString("forAnyConnection") };
   1.514 +      for (var i = 0; i < ids.length; ++i)
   1.515 +        document.getElementById(ids[i]).disabled = false;
   1.516 +    }
   1.517 +    else {
   1.518 +      var noneSelected = this._bundle.getString("noCookieSelected");
   1.519 +      properties = { name: noneSelected, value: noneSelected, host: noneSelected,
   1.520 +                     path: noneSelected, expires: noneSelected,
   1.521 +                     isSecure: noneSelected };
   1.522 +      for (i = 0; i < ids.length; ++i)
   1.523 +        document.getElementById(ids[i]).disabled = true;
   1.524 +    }
   1.525 +    for (var property in properties)
   1.526 +      document.getElementById(property).value = properties[property];
   1.527 +  },
   1.528 +
   1.529 +  onCookieSelected: function () {
   1.530 +    var properties, item;
   1.531 +    var seln = this._tree.view.selection;
   1.532 +    if (!this._view._filtered)
   1.533 +      item = this._view._getItemAtIndex(seln.currentIndex);
   1.534 +    else
   1.535 +      item = this._view._filterSet[seln.currentIndex];
   1.536 +
   1.537 +    this._updateCookieData(item);
   1.538 +
   1.539 +    var rangeCount = seln.getRangeCount();
   1.540 +    var selectedCookieCount = 0;
   1.541 +    for (var i = 0; i < rangeCount; ++i) {
   1.542 +      var min = {}; var max = {};
   1.543 +      seln.getRangeAt(i, min, max);
   1.544 +      for (var j = min.value; j <= max.value; ++j) {
   1.545 +        item = this._view._getItemAtIndex(j);
   1.546 +        if (!item) continue;
   1.547 +        if (item.container && !item.open)
   1.548 +          selectedCookieCount += item.cookies.length;
   1.549 +        else if (!item.container)
   1.550 +          ++selectedCookieCount;
   1.551 +      }
   1.552 +    }
   1.553 +    var item = this._view._getItemAtIndex(seln.currentIndex);
   1.554 +    if (item && seln.count == 1 && item.container && item.open)
   1.555 +      selectedCookieCount += 2;
   1.556 +
   1.557 +    var removeCookie = document.getElementById("removeCookie");
   1.558 +    var removeCookies = document.getElementById("removeCookies");
   1.559 +    removeCookie.parentNode.selectedPanel =
   1.560 +      selectedCookieCount == 1 ? removeCookie : removeCookies;
   1.561 +
   1.562 +    removeCookie.disabled = removeCookies.disabled = !(seln.count > 0);
   1.563 +  },
   1.564 +
   1.565 +  performDeletion: function gCookiesWindow_performDeletion(deleteItems) {
   1.566 +    var psvc = Components.classes["@mozilla.org/preferences-service;1"]
   1.567 +                         .getService(Components.interfaces.nsIPrefBranch);
   1.568 +    var blockFutureCookies = false;
   1.569 +    if (psvc.prefHasUserValue("network.cookie.blockFutureCookies"))
   1.570 +      blockFutureCookies = psvc.getBoolPref("network.cookie.blockFutureCookies");
   1.571 +    for (var i = 0; i < deleteItems.length; ++i) {
   1.572 +      var item = deleteItems[i];
   1.573 +      this._cm.remove(item.host, item.name, item.path, blockFutureCookies);
   1.574 +    }
   1.575 +  },
   1.576 +
   1.577 +  deleteCookie: function () {
   1.578 +    // Selection Notes
   1.579 +    // - Selection always moves to *NEXT* adjacent item unless item
   1.580 +    //   is last child at a given level in which case it moves to *PREVIOUS*
   1.581 +    //   item
   1.582 +    //
   1.583 +    // Selection Cases (Somewhat Complicated)
   1.584 +    //
   1.585 +    // 1) Single cookie selected, host has single child
   1.586 +    //    v cnn.com
   1.587 +    //    //// cnn.com ///////////// goksdjf@ ////
   1.588 +    //    > atwola.com
   1.589 +    //
   1.590 +    //    Before SelectedIndex: 1   Before RowCount: 3
   1.591 +    //    After  SelectedIndex: 0   After  RowCount: 1
   1.592 +    //
   1.593 +    // 2) Host selected, host open
   1.594 +    //    v goats.com ////////////////////////////
   1.595 +    //         goats.com             sldkkfjl
   1.596 +    //         goat.scom             flksj133
   1.597 +    //    > atwola.com
   1.598 +    //
   1.599 +    //    Before SelectedIndex: 0   Before RowCount: 4
   1.600 +    //    After  SelectedIndex: 0   After  RowCount: 1
   1.601 +    //
   1.602 +    // 3) Host selected, host closed
   1.603 +    //    > goats.com ////////////////////////////
   1.604 +    //    > atwola.com
   1.605 +    //
   1.606 +    //    Before SelectedIndex: 0   Before RowCount: 2
   1.607 +    //    After  SelectedIndex: 0   After  RowCount: 1
   1.608 +    //
   1.609 +    // 4) Single cookie selected, host has many children
   1.610 +    //    v goats.com
   1.611 +    //         goats.com             sldkkfjl
   1.612 +    //    //// goats.com /////////// flksjl33 ////
   1.613 +    //    > atwola.com
   1.614 +    //
   1.615 +    //    Before SelectedIndex: 2   Before RowCount: 4
   1.616 +    //    After  SelectedIndex: 1   After  RowCount: 3
   1.617 +    //
   1.618 +    // 5) Single cookie selected, host has many children
   1.619 +    //    v goats.com
   1.620 +    //    //// goats.com /////////// flksjl33 ////
   1.621 +    //         goats.com             sldkkfjl
   1.622 +    //    > atwola.com
   1.623 +    //
   1.624 +    //    Before SelectedIndex: 1   Before RowCount: 4
   1.625 +    //    After  SelectedIndex: 1   After  RowCount: 3
   1.626 +    var seln = this._view.selection;
   1.627 +    var tbo = this._tree.treeBoxObject;
   1.628 +
   1.629 +    if (seln.count < 1) return;
   1.630 +
   1.631 +    var nextSelected = 0;
   1.632 +    var rowCountImpact = 0;
   1.633 +    var deleteItems = [];
   1.634 +    if (!this._view._filtered) {
   1.635 +      var ci = seln.currentIndex;
   1.636 +      nextSelected = ci;
   1.637 +      var invalidateRow = -1;
   1.638 +      var item = this._view._getItemAtIndex(ci);
   1.639 +      if (item.container) {
   1.640 +        rowCountImpact -= (item.open ? item.cookies.length : 0) + 1;
   1.641 +        deleteItems = deleteItems.concat(item.cookies);
   1.642 +        if (!this._view.hasNextSibling(-1, ci))
   1.643 +          --nextSelected;
   1.644 +        this._view._removeItemAtIndex(ci);
   1.645 +      }
   1.646 +      else {
   1.647 +        var parent = this._view._getItemAtIndex(item.parentIndex);
   1.648 +        --rowCountImpact;
   1.649 +        if (parent.cookies.length == 1) {
   1.650 +          --rowCountImpact;
   1.651 +          deleteItems.push(item);
   1.652 +          if (!this._view.hasNextSibling(-1, ci))
   1.653 +            --nextSelected;
   1.654 +          if (!this._view.hasNextSibling(-1, item.parentIndex))
   1.655 +            --nextSelected;
   1.656 +          this._view._removeItemAtIndex(item.parentIndex);
   1.657 +          invalidateRow = item.parentIndex;
   1.658 +        }
   1.659 +        else {
   1.660 +          deleteItems.push(item);
   1.661 +          if (!this._view.hasNextSibling(-1, ci))
   1.662 +            --nextSelected;
   1.663 +          this._view._removeItemAtIndex(ci);
   1.664 +        }
   1.665 +      }
   1.666 +      this._view._rowCount += rowCountImpact;
   1.667 +      tbo.rowCountChanged(ci, rowCountImpact);
   1.668 +      if (invalidateRow != -1)
   1.669 +        tbo.invalidateRow(invalidateRow);
   1.670 +    }
   1.671 +    else {
   1.672 +      var rangeCount = seln.getRangeCount();
   1.673 +      // Traverse backwards through selections to avoid messing 
   1.674 +      // up the indices when they are deleted.
   1.675 +      // See bug 388079.
   1.676 +      for (var i = rangeCount - 1; i >= 0; --i) {
   1.677 +        var min = {}; var max = {};
   1.678 +        seln.getRangeAt(i, min, max);
   1.679 +        nextSelected = min.value;
   1.680 +        for (var j = min.value; j <= max.value; ++j) {
   1.681 +          deleteItems.push(this._view._getItemAtIndex(j));
   1.682 +          if (!this._view.hasNextSibling(-1, max.value))
   1.683 +            --nextSelected;
   1.684 +        }
   1.685 +        var delta = max.value - min.value + 1;
   1.686 +        this._view._removeItemAtIndex(min.value, delta);
   1.687 +        rowCountImpact = -1 * delta;
   1.688 +        this._view._rowCount += rowCountImpact;
   1.689 +        tbo.rowCountChanged(min.value, rowCountImpact);
   1.690 +      }
   1.691 +    }
   1.692 +
   1.693 +    this.performDeletion(deleteItems);
   1.694 +
   1.695 +    if (nextSelected < 0)
   1.696 +      seln.clearSelection();
   1.697 +    else {
   1.698 +      seln.select(nextSelected);
   1.699 +      this._tree.focus();
   1.700 +    }
   1.701 +  },
   1.702 +
   1.703 +  deleteAllCookies: function () {
   1.704 +    if (this._view._filtered) {
   1.705 +      var rowCount = this._view.rowCount;
   1.706 +      var deleteItems = [];
   1.707 +      for (var index = 0; index < rowCount; index++) {
   1.708 +        deleteItems.push(this._view._getItemAtIndex(index));
   1.709 +      }
   1.710 +      this._view._removeItemAtIndex(0, rowCount);
   1.711 +      this._view._rowCount = 0;
   1.712 +      this._tree.treeBoxObject.rowCountChanged(0, -rowCount);
   1.713 +      this.performDeletion(deleteItems);
   1.714 +    }
   1.715 +    else {
   1.716 +      this._cm.removeAll();
   1.717 +    }
   1.718 +    this._updateRemoveAllButton();
   1.719 +    this.focusFilterBox();
   1.720 +  },
   1.721 +
   1.722 +  onCookieKeyPress: function (aEvent) {
   1.723 +    if (aEvent.keyCode == 46)
   1.724 +      this.deleteCookie();
   1.725 +  },
   1.726 +
   1.727 +  _lastSortProperty : "",
   1.728 +  _lastSortAscending: false,
   1.729 +  sort: function (aProperty) {
   1.730 +    var ascending = (aProperty == this._lastSortProperty) ? !this._lastSortAscending : true;
   1.731 +    // Sort the Non-Filtered Host Collections
   1.732 +    if (aProperty == "rawHost") {
   1.733 +      function sortByHost(a, b) {
   1.734 +        return a.toLowerCase().localeCompare(b.toLowerCase());
   1.735 +      }
   1.736 +      this._hostOrder.sort(sortByHost);
   1.737 +      if (!ascending)
   1.738 +        this._hostOrder.reverse();
   1.739 +    }
   1.740 +
   1.741 +    function sortByProperty(a, b) {
   1.742 +      return a[aProperty].toLowerCase().localeCompare(b[aProperty].toLowerCase());
   1.743 +    }
   1.744 +    for (var host in this._hosts) {
   1.745 +      var cookies = this._hosts[host].cookies;
   1.746 +      cookies.sort(sortByProperty);
   1.747 +      if (!ascending)
   1.748 +        cookies.reverse();
   1.749 +    }
   1.750 +    // Sort the Filtered List, if in Filtered mode
   1.751 +    if (this._view._filtered) {
   1.752 +      this._view._filterSet.sort(sortByProperty);
   1.753 +      if (!ascending)
   1.754 +        this._view._filterSet.reverse();
   1.755 +    }
   1.756 +
   1.757 +    // Adjust the Sort Indicator
   1.758 +    var domainCol = document.getElementById("domainCol");
   1.759 +    var nameCol = document.getElementById("nameCol");
   1.760 +    var sortOrderString = ascending ? "ascending" : "descending";
   1.761 +    if (aProperty == "rawHost") {
   1.762 +      domainCol.setAttribute("sortDirection", sortOrderString);
   1.763 +      nameCol.removeAttribute("sortDirection");
   1.764 +    }
   1.765 +    else {
   1.766 +      nameCol.setAttribute("sortDirection", sortOrderString);
   1.767 +      domainCol.removeAttribute("sortDirection");
   1.768 +    }
   1.769 +
   1.770 +    this._view._invalidateCache(0);
   1.771 +    this._view.selection.clearSelection();
   1.772 +    this._view.selection.select(0);
   1.773 +    this._tree.treeBoxObject.invalidate();
   1.774 +    this._tree.treeBoxObject.ensureRowIsVisible(0);
   1.775 +
   1.776 +    this._lastSortAscending = ascending;
   1.777 +    this._lastSortProperty = aProperty;
   1.778 +  },
   1.779 +
   1.780 +  clearFilter: function () {
   1.781 +    // Revert to single-select in the tree
   1.782 +    this._tree.setAttribute("seltype", "single");
   1.783 +
   1.784 +    // Clear the Tree Display
   1.785 +    this._view._filtered = false;
   1.786 +    this._view._rowCount = 0;
   1.787 +    this._tree.treeBoxObject.rowCountChanged(0, -this._view._filterSet.length);
   1.788 +    this._view._filterSet = [];
   1.789 +
   1.790 +    // Just reload the list to make sure deletions are respected
   1.791 +    this._loadCookies();
   1.792 +    this._tree.treeBoxObject.view = this._view;
   1.793 +
   1.794 +    // Restore sort order
   1.795 +    var sortby = this._lastSortProperty;
   1.796 +    if (sortby == "") {
   1.797 +      this._lastSortAscending = false;
   1.798 +      this.sort("rawHost");
   1.799 +    }
   1.800 +    else {
   1.801 +      this._lastSortAscending = !this._lastSortAscending;
   1.802 +      this.sort(sortby);
   1.803 +    }
   1.804 +
   1.805 +    // Restore open state
   1.806 +    for (var i = 0; i < this._openIndices.length; ++i)
   1.807 +      this._view.toggleOpenState(this._openIndices[i]);
   1.808 +    this._openIndices = [];
   1.809 +
   1.810 +    // Restore selection
   1.811 +    this._view.selection.clearSelection();
   1.812 +    for (i = 0; i < this._lastSelectedRanges.length; ++i) {
   1.813 +      var range = this._lastSelectedRanges[i];
   1.814 +      this._view.selection.rangedSelect(range.min, range.max, true);
   1.815 +    }
   1.816 +    this._lastSelectedRanges = [];
   1.817 +
   1.818 +    document.getElementById("cookiesIntro").value = this._bundle.getString("cookiesAll");
   1.819 +    this._updateRemoveAllButton();
   1.820 +  },
   1.821 +
   1.822 +  _cookieMatchesFilter: function (aCookie) {
   1.823 +    return aCookie.rawHost.indexOf(this._view._filterValue) != -1 ||
   1.824 +           aCookie.name.indexOf(this._view._filterValue) != -1 ||
   1.825 +           aCookie.value.indexOf(this._view._filterValue) != -1;
   1.826 +  },
   1.827 +
   1.828 +  _filterCookies: function (aFilterValue) {
   1.829 +    this._view._filterValue = aFilterValue;
   1.830 +    var cookies = [];
   1.831 +    for (var i = 0; i < gCookiesWindow._hostOrder.length; ++i) { //var host in gCookiesWindow._hosts) {
   1.832 +      var currHost = gCookiesWindow._hosts[gCookiesWindow._hostOrder[i]]; // gCookiesWindow._hosts[host];
   1.833 +      if (!currHost) continue;
   1.834 +      for (var j = 0; j < currHost.cookies.length; ++j) {
   1.835 +        var cookie = currHost.cookies[j];
   1.836 +        if (this._cookieMatchesFilter(cookie))
   1.837 +          cookies.push(cookie);
   1.838 +      }
   1.839 +    }
   1.840 +    return cookies;
   1.841 +  },
   1.842 +
   1.843 +  _lastSelectedRanges: [],
   1.844 +  _openIndices: [],
   1.845 +  _saveState: function () {
   1.846 +    // Save selection
   1.847 +    var seln = this._view.selection;
   1.848 +    this._lastSelectedRanges = [];
   1.849 +    var rangeCount = seln.getRangeCount();
   1.850 +    for (var i = 0; i < rangeCount; ++i) {
   1.851 +      var min = {}; var max = {};
   1.852 +      seln.getRangeAt(i, min, max);
   1.853 +      this._lastSelectedRanges.push({ min: min.value, max: max.value });
   1.854 +    }
   1.855 +
   1.856 +    // Save open states
   1.857 +    this._openIndices = [];
   1.858 +    for (i = 0; i < this._view.rowCount; ++i) {
   1.859 +      var item = this._view._getItemAtIndex(i);
   1.860 +      if (item && item.container && item.open)
   1.861 +        this._openIndices.push(i);
   1.862 +    }
   1.863 +  },
   1.864 +
   1.865 +  _updateRemoveAllButton: function gCookiesWindow__updateRemoveAllButton() {
   1.866 +    document.getElementById("removeAllCookies").disabled = this._view._rowCount == 0;
   1.867 +  },
   1.868 +
   1.869 +  filter: function () {
   1.870 +    var filter = document.getElementById("filter").value;
   1.871 +    if (filter == "") {
   1.872 +      gCookiesWindow.clearFilter();
   1.873 +      return;
   1.874 +    }
   1.875 +    var view = gCookiesWindow._view;
   1.876 +    view._filterSet = gCookiesWindow._filterCookies(filter);
   1.877 +    if (!view._filtered) {
   1.878 +      // Save Display Info for the Non-Filtered mode when we first
   1.879 +      // enter Filtered mode.
   1.880 +      gCookiesWindow._saveState();
   1.881 +      view._filtered = true;
   1.882 +    }
   1.883 +    // Move to multi-select in the tree
   1.884 +    gCookiesWindow._tree.setAttribute("seltype", "multiple");
   1.885 +
   1.886 +    // Clear the display
   1.887 +    var oldCount = view._rowCount;
   1.888 +    view._rowCount = 0;
   1.889 +    gCookiesWindow._tree.treeBoxObject.rowCountChanged(0, -oldCount);
   1.890 +    // Set up the filtered display
   1.891 +    view._rowCount = view._filterSet.length;
   1.892 +    gCookiesWindow._tree.treeBoxObject.rowCountChanged(0, view.rowCount);
   1.893 +
   1.894 +    // if the view is not empty then select the first item
   1.895 +    if (view.rowCount > 0)
   1.896 +      view.selection.select(0);
   1.897 +
   1.898 +    document.getElementById("cookiesIntro").value = gCookiesWindow._bundle.getString("cookiesFiltered");
   1.899 +    this._updateRemoveAllButton();
   1.900 +  },
   1.901 +
   1.902 +  setFilter: function (aFilterString) {
   1.903 +    document.getElementById("filter").value = aFilterString;
   1.904 +    this.filter();
   1.905 +  },
   1.906 +
   1.907 +  focusFilterBox: function () {
   1.908 +    var filter = document.getElementById("filter");
   1.909 +    filter.focus();
   1.910 +    filter.select();
   1.911 +  },
   1.912 +
   1.913 +  onWindowKeyPress: function (aEvent) {
   1.914 +    if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE)
   1.915 +      window.close();
   1.916 +  }
   1.917 +};

mercurial