toolkit/devtools/styleinspector/css-logic.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/devtools/styleinspector/css-logic.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1803 @@
     1.4 +/* -*- Mode: Javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +/*
    1.11 + * About the objects defined in this file:
    1.12 + * - CssLogic contains style information about a view context. It provides
    1.13 + *   access to 2 sets of objects: Css[Sheet|Rule|Selector] provide access to
    1.14 + *   information that does not change when the selected element changes while
    1.15 + *   Css[Property|Selector]Info provide information that is dependent on the
    1.16 + *   selected element.
    1.17 + *   Its key methods are highlight(), getPropertyInfo() and forEachSheet(), etc
    1.18 + *   It also contains a number of static methods for l10n, naming, etc
    1.19 + *
    1.20 + * - CssSheet provides a more useful API to a DOM CSSSheet for our purposes,
    1.21 + *   including shortSource and href.
    1.22 + * - CssRule a more useful API to a nsIDOMCSSRule including access to the group
    1.23 + *   of CssSelectors that the rule provides properties for
    1.24 + * - CssSelector A single selector - i.e. not a selector group. In other words
    1.25 + *   a CssSelector does not contain ','. This terminology is different from the
    1.26 + *   standard DOM API, but more inline with the definition in the spec.
    1.27 + *
    1.28 + * - CssPropertyInfo contains style information for a single property for the
    1.29 + *   highlighted element.
    1.30 + * - CssSelectorInfo is a wrapper around CssSelector, which adds sorting with
    1.31 + *   reference to the selected element.
    1.32 + */
    1.33 +
    1.34 +/**
    1.35 + * Provide access to the style information in a page.
    1.36 + * CssLogic uses the standard DOM API, and the Gecko inIDOMUtils API to access
    1.37 + * styling information in the page, and present this to the user in a way that
    1.38 + * helps them understand:
    1.39 + * - why their expectations may not have been fulfilled
    1.40 + * - how browsers process CSS
    1.41 + * @constructor
    1.42 + */
    1.43 +
    1.44 +const {Cc, Ci, Cu} = require("chrome");
    1.45 +
    1.46 +const RX_UNIVERSAL_SELECTOR = /\s*\*\s*/g;
    1.47 +const RX_NOT = /:not\((.*?)\)/g;
    1.48 +const RX_PSEUDO_CLASS_OR_ELT = /(:[\w-]+\().*?\)/g;
    1.49 +const RX_CONNECTORS = /\s*[\s>+~]\s*/g;
    1.50 +const RX_ID = /\s*#\w+\s*/g;
    1.51 +const RX_CLASS_OR_ATTRIBUTE = /\s*(?:\.\w+|\[.+?\])\s*/g;
    1.52 +const RX_PSEUDO = /\s*:?:([\w-]+)(\(?\)?)\s*/g;
    1.53 +
    1.54 +Cu.import("resource://gre/modules/Services.jsm");
    1.55 +Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    1.56 +
    1.57 +function CssLogic()
    1.58 +{
    1.59 +  // The cache of examined CSS properties.
    1.60 +  _propertyInfos: {};
    1.61 +}
    1.62 +
    1.63 +exports.CssLogic = CssLogic;
    1.64 +
    1.65 +/**
    1.66 + * Special values for filter, in addition to an href these values can be used
    1.67 + */
    1.68 +CssLogic.FILTER = {
    1.69 +  USER: "user", // show properties for all user style sheets.
    1.70 +  UA: "ua",    // USER, plus user-agent (i.e. browser) style sheets
    1.71 +};
    1.72 +
    1.73 +/**
    1.74 + * Known media values. To distinguish "all" stylesheets (above) from "all" media
    1.75 + * The full list includes braille, embossed, handheld, print, projection,
    1.76 + * speech, tty, and tv, but this is only a hack because these are not defined
    1.77 + * in the DOM at all.
    1.78 + * @see http://www.w3.org/TR/CSS21/media.html#media-types
    1.79 + */
    1.80 +CssLogic.MEDIA = {
    1.81 +  ALL: "all",
    1.82 +  SCREEN: "screen",
    1.83 +};
    1.84 +
    1.85 +/**
    1.86 + * Each rule has a status, the bigger the number, the better placed it is to
    1.87 + * provide styling information.
    1.88 + *
    1.89 + * These statuses are localized inside the styleinspector.properties string bundle.
    1.90 + * @see csshtmltree.js RuleView._cacheStatusNames()
    1.91 + */
    1.92 +CssLogic.STATUS = {
    1.93 +  BEST: 3,
    1.94 +  MATCHED: 2,
    1.95 +  PARENT_MATCH: 1,
    1.96 +  UNMATCHED: 0,
    1.97 +  UNKNOWN: -1,
    1.98 +};
    1.99 +
   1.100 +CssLogic.prototype = {
   1.101 +  // Both setup by highlight().
   1.102 +  viewedElement: null,
   1.103 +  viewedDocument: null,
   1.104 +
   1.105 +  // The cache of the known sheets.
   1.106 +  _sheets: null,
   1.107 +
   1.108 +  // Have the sheets been cached?
   1.109 +  _sheetsCached: false,
   1.110 +
   1.111 +  // The total number of rules, in all stylesheets, after filtering.
   1.112 +  _ruleCount: 0,
   1.113 +
   1.114 +  // The computed styles for the viewedElement.
   1.115 +  _computedStyle: null,
   1.116 +
   1.117 +  // Source filter. Only display properties coming from the given source
   1.118 +  _sourceFilter: CssLogic.FILTER.USER,
   1.119 +
   1.120 +  // Used for tracking unique CssSheet/CssRule/CssSelector objects, in a run of
   1.121 +  // processMatchedSelectors().
   1.122 +  _passId: 0,
   1.123 +
   1.124 +  // Used for tracking matched CssSelector objects.
   1.125 +  _matchId: 0,
   1.126 +
   1.127 +  _matchedRules: null,
   1.128 +  _matchedSelectors: null,
   1.129 +
   1.130 +  /**
   1.131 +   * Reset various properties
   1.132 +   */
   1.133 +  reset: function CssLogic_reset()
   1.134 +  {
   1.135 +    this._propertyInfos = {};
   1.136 +    this._ruleCount = 0;
   1.137 +    this._sheetIndex = 0;
   1.138 +    this._sheets = {};
   1.139 +    this._sheetsCached = false;
   1.140 +    this._matchedRules = null;
   1.141 +    this._matchedSelectors = null;
   1.142 +  },
   1.143 +
   1.144 +  /**
   1.145 +   * Focus on a new element - remove the style caches.
   1.146 +   *
   1.147 +   * @param {nsIDOMElement} aViewedElement the element the user has highlighted
   1.148 +   * in the Inspector.
   1.149 +   */
   1.150 +  highlight: function CssLogic_highlight(aViewedElement)
   1.151 +  {
   1.152 +    if (!aViewedElement) {
   1.153 +      this.viewedElement = null;
   1.154 +      this.viewedDocument = null;
   1.155 +      this._computedStyle = null;
   1.156 +      this.reset();
   1.157 +      return;
   1.158 +    }
   1.159 +
   1.160 +    this.viewedElement = aViewedElement;
   1.161 +
   1.162 +    let doc = this.viewedElement.ownerDocument;
   1.163 +    if (doc != this.viewedDocument) {
   1.164 +      // New document: clear/rebuild the cache.
   1.165 +      this.viewedDocument = doc;
   1.166 +
   1.167 +      // Hunt down top level stylesheets, and cache them.
   1.168 +      this._cacheSheets();
   1.169 +    } else {
   1.170 +      // Clear cached data in the CssPropertyInfo objects.
   1.171 +      this._propertyInfos = {};
   1.172 +    }
   1.173 +
   1.174 +    this._matchedRules = null;
   1.175 +    this._matchedSelectors = null;
   1.176 +    let win = this.viewedDocument.defaultView;
   1.177 +    this._computedStyle = win.getComputedStyle(this.viewedElement, "");
   1.178 +  },
   1.179 +
   1.180 +  /**
   1.181 +   * Get the source filter.
   1.182 +   * @returns {string} The source filter being used.
   1.183 +   */
   1.184 +  get sourceFilter() {
   1.185 +    return this._sourceFilter;
   1.186 +  },
   1.187 +
   1.188 +  /**
   1.189 +   * Source filter. Only display properties coming from the given source (web
   1.190 +   * address). Note that in order to avoid information overload we DO NOT show
   1.191 +   * unmatched system rules.
   1.192 +   * @see CssLogic.FILTER.*
   1.193 +   */
   1.194 +  set sourceFilter(aValue) {
   1.195 +    let oldValue = this._sourceFilter;
   1.196 +    this._sourceFilter = aValue;
   1.197 +
   1.198 +    let ruleCount = 0;
   1.199 +
   1.200 +    // Update the CssSheet objects.
   1.201 +    this.forEachSheet(function(aSheet) {
   1.202 +      aSheet._sheetAllowed = -1;
   1.203 +      if (aSheet.contentSheet && aSheet.sheetAllowed) {
   1.204 +        ruleCount += aSheet.ruleCount;
   1.205 +      }
   1.206 +    }, this);
   1.207 +
   1.208 +    this._ruleCount = ruleCount;
   1.209 +
   1.210 +    // Full update is needed because the this.processMatchedSelectors() method
   1.211 +    // skips UA stylesheets if the filter does not allow such sheets.
   1.212 +    let needFullUpdate = (oldValue == CssLogic.FILTER.UA ||
   1.213 +        aValue == CssLogic.FILTER.UA);
   1.214 +
   1.215 +    if (needFullUpdate) {
   1.216 +      this._matchedRules = null;
   1.217 +      this._matchedSelectors = null;
   1.218 +      this._propertyInfos = {};
   1.219 +    } else {
   1.220 +      // Update the CssPropertyInfo objects.
   1.221 +      for each (let propertyInfo in this._propertyInfos) {
   1.222 +        propertyInfo.needRefilter = true;
   1.223 +      }
   1.224 +    }
   1.225 +  },
   1.226 +
   1.227 +  /**
   1.228 +   * Return a CssPropertyInfo data structure for the currently viewed element
   1.229 +   * and the specified CSS property. If there is no currently viewed element we
   1.230 +   * return an empty object.
   1.231 +   *
   1.232 +   * @param {string} aProperty The CSS property to look for.
   1.233 +   * @return {CssPropertyInfo} a CssPropertyInfo structure for the given
   1.234 +   * property.
   1.235 +   */
   1.236 +  getPropertyInfo: function CssLogic_getPropertyInfo(aProperty)
   1.237 +  {
   1.238 +    if (!this.viewedElement) {
   1.239 +      return {};
   1.240 +    }
   1.241 +
   1.242 +    let info = this._propertyInfos[aProperty];
   1.243 +    if (!info) {
   1.244 +      info = new CssPropertyInfo(this, aProperty);
   1.245 +      this._propertyInfos[aProperty] = info;
   1.246 +    }
   1.247 +
   1.248 +    return info;
   1.249 +  },
   1.250 +
   1.251 +  /**
   1.252 +   * Cache all the stylesheets in the inspected document
   1.253 +   * @private
   1.254 +   */
   1.255 +  _cacheSheets: function CssLogic_cacheSheets()
   1.256 +  {
   1.257 +    this._passId++;
   1.258 +    this.reset();
   1.259 +
   1.260 +    // styleSheets isn't an array, but forEach can work on it anyway
   1.261 +    Array.prototype.forEach.call(this.viewedDocument.styleSheets,
   1.262 +        this._cacheSheet, this);
   1.263 +
   1.264 +    this._sheetsCached = true;
   1.265 +  },
   1.266 +
   1.267 +  /**
   1.268 +   * Cache a stylesheet if it falls within the requirements: if it's enabled,
   1.269 +   * and if the @media is allowed. This method also walks through the stylesheet
   1.270 +   * cssRules to find @imported rules, to cache the stylesheets of those rules
   1.271 +   * as well.
   1.272 +   *
   1.273 +   * @private
   1.274 +   * @param {CSSStyleSheet} aDomSheet the CSSStyleSheet object to cache.
   1.275 +   */
   1.276 +  _cacheSheet: function CssLogic_cacheSheet(aDomSheet)
   1.277 +  {
   1.278 +    if (aDomSheet.disabled) {
   1.279 +      return;
   1.280 +    }
   1.281 +
   1.282 +    // Only work with stylesheets that have their media allowed.
   1.283 +    if (!this.mediaMatches(aDomSheet)) {
   1.284 +      return;
   1.285 +    }
   1.286 +
   1.287 +    // Cache the sheet.
   1.288 +    let cssSheet = this.getSheet(aDomSheet, this._sheetIndex++);
   1.289 +    if (cssSheet._passId != this._passId) {
   1.290 +      cssSheet._passId = this._passId;
   1.291 +
   1.292 +      // Find import rules.
   1.293 +      Array.prototype.forEach.call(aDomSheet.cssRules, function(aDomRule) {
   1.294 +        if (aDomRule.type == Ci.nsIDOMCSSRule.IMPORT_RULE && aDomRule.styleSheet &&
   1.295 +            this.mediaMatches(aDomRule)) {
   1.296 +          this._cacheSheet(aDomRule.styleSheet);
   1.297 +        }
   1.298 +      }, this);
   1.299 +    }
   1.300 +  },
   1.301 +
   1.302 +  /**
   1.303 +   * Retrieve the list of stylesheets in the document.
   1.304 +   *
   1.305 +   * @return {array} the list of stylesheets in the document.
   1.306 +   */
   1.307 +  get sheets()
   1.308 +  {
   1.309 +    if (!this._sheetsCached) {
   1.310 +      this._cacheSheets();
   1.311 +    }
   1.312 +
   1.313 +    let sheets = [];
   1.314 +    this.forEachSheet(function (aSheet) {
   1.315 +      if (aSheet.contentSheet) {
   1.316 +        sheets.push(aSheet);
   1.317 +      }
   1.318 +    }, this);
   1.319 +
   1.320 +    return sheets;
   1.321 +  },
   1.322 +
   1.323 +  /**
   1.324 +   * Retrieve a CssSheet object for a given a CSSStyleSheet object. If the
   1.325 +   * stylesheet is already cached, you get the existing CssSheet object,
   1.326 +   * otherwise the new CSSStyleSheet object is cached.
   1.327 +   *
   1.328 +   * @param {CSSStyleSheet} aDomSheet the CSSStyleSheet object you want.
   1.329 +   * @param {number} aIndex the index, within the document, of the stylesheet.
   1.330 +   *
   1.331 +   * @return {CssSheet} the CssSheet object for the given CSSStyleSheet object.
   1.332 +   */
   1.333 +  getSheet: function CL_getSheet(aDomSheet, aIndex)
   1.334 +  {
   1.335 +    let cacheId = "";
   1.336 +
   1.337 +    if (aDomSheet.href) {
   1.338 +      cacheId = aDomSheet.href;
   1.339 +    } else if (aDomSheet.ownerNode && aDomSheet.ownerNode.ownerDocument) {
   1.340 +      cacheId = aDomSheet.ownerNode.ownerDocument.location;
   1.341 +    }
   1.342 +
   1.343 +    let sheet = null;
   1.344 +    let sheetFound = false;
   1.345 +
   1.346 +    if (cacheId in this._sheets) {
   1.347 +      for (let i = 0, numSheets = this._sheets[cacheId].length; i < numSheets; i++) {
   1.348 +        sheet = this._sheets[cacheId][i];
   1.349 +        if (sheet.domSheet === aDomSheet) {
   1.350 +          if (aIndex != -1) {
   1.351 +            sheet.index = aIndex;
   1.352 +          }
   1.353 +          sheetFound = true;
   1.354 +          break;
   1.355 +        }
   1.356 +      }
   1.357 +    }
   1.358 +
   1.359 +    if (!sheetFound) {
   1.360 +      if (!(cacheId in this._sheets)) {
   1.361 +        this._sheets[cacheId] = [];
   1.362 +      }
   1.363 +
   1.364 +      sheet = new CssSheet(this, aDomSheet, aIndex);
   1.365 +      if (sheet.sheetAllowed && sheet.contentSheet) {
   1.366 +        this._ruleCount += sheet.ruleCount;
   1.367 +      }
   1.368 +
   1.369 +      this._sheets[cacheId].push(sheet);
   1.370 +    }
   1.371 +
   1.372 +    return sheet;
   1.373 +  },
   1.374 +
   1.375 +  /**
   1.376 +   * Process each cached stylesheet in the document using your callback.
   1.377 +   *
   1.378 +   * @param {function} aCallback the function you want executed for each of the
   1.379 +   * CssSheet objects cached.
   1.380 +   * @param {object} aScope the scope you want for the callback function. aScope
   1.381 +   * will be the this object when aCallback executes.
   1.382 +   */
   1.383 +  forEachSheet: function CssLogic_forEachSheet(aCallback, aScope)
   1.384 +  {
   1.385 +    for each (let sheets in this._sheets) {
   1.386 +      for (let i = 0; i < sheets.length; i ++) {
   1.387 +        // We take this as an opportunity to clean dead sheets
   1.388 +        try {
   1.389 +          let sheet = sheets[i];
   1.390 +          sheet.domSheet; // If accessing domSheet raises an exception, then the
   1.391 +          // style sheet is a dead object
   1.392 +          aCallback.call(aScope, sheet, i, sheets);
   1.393 +        } catch (e) {
   1.394 +          sheets.splice(i, 1);
   1.395 +          i --;
   1.396 +        }
   1.397 +      }
   1.398 +    }
   1.399 +  },
   1.400 +
   1.401 +  /**
   1.402 +   * Process *some* cached stylesheets in the document using your callback. The
   1.403 +   * callback function should return true in order to halt processing.
   1.404 +   *
   1.405 +   * @param {function} aCallback the function you want executed for some of the
   1.406 +   * CssSheet objects cached.
   1.407 +   * @param {object} aScope the scope you want for the callback function. aScope
   1.408 +   * will be the this object when aCallback executes.
   1.409 +   * @return {Boolean} true if aCallback returns true during any iteration,
   1.410 +   * otherwise false is returned.
   1.411 +   */
   1.412 +  forSomeSheets: function CssLogic_forSomeSheets(aCallback, aScope)
   1.413 +  {
   1.414 +    for each (let sheets in this._sheets) {
   1.415 +      if (sheets.some(aCallback, aScope)) {
   1.416 +        return true;
   1.417 +      }
   1.418 +    }
   1.419 +    return false;
   1.420 +  },
   1.421 +
   1.422 +  /**
   1.423 +   * Get the number nsIDOMCSSRule objects in the document, counted from all of
   1.424 +   * the stylesheets. System sheets are excluded. If a filter is active, this
   1.425 +   * tells only the number of nsIDOMCSSRule objects inside the selected
   1.426 +   * CSSStyleSheet.
   1.427 +   *
   1.428 +   * WARNING: This only provides an estimate of the rule count, and the results
   1.429 +   * could change at a later date. Todo remove this
   1.430 +   *
   1.431 +   * @return {number} the number of nsIDOMCSSRule (all rules).
   1.432 +   */
   1.433 +  get ruleCount()
   1.434 +  {
   1.435 +    if (!this._sheetsCached) {
   1.436 +      this._cacheSheets();
   1.437 +    }
   1.438 +
   1.439 +    return this._ruleCount;
   1.440 +  },
   1.441 +
   1.442 +  /**
   1.443 +   * Process the CssSelector objects that match the highlighted element and its
   1.444 +   * parent elements. aScope.aCallback() is executed for each CssSelector
   1.445 +   * object, being passed the CssSelector object and the match status.
   1.446 +   *
   1.447 +   * This method also includes all of the element.style properties, for each
   1.448 +   * highlighted element parent and for the highlighted element itself.
   1.449 +   *
   1.450 +   * Note that the matched selectors are cached, such that next time your
   1.451 +   * callback is invoked for the cached list of CssSelector objects.
   1.452 +   *
   1.453 +   * @param {function} aCallback the function you want to execute for each of
   1.454 +   * the matched selectors.
   1.455 +   * @param {object} aScope the scope you want for the callback function. aScope
   1.456 +   * will be the this object when aCallback executes.
   1.457 +   */
   1.458 +  processMatchedSelectors: function CL_processMatchedSelectors(aCallback, aScope)
   1.459 +  {
   1.460 +    if (this._matchedSelectors) {
   1.461 +      if (aCallback) {
   1.462 +        this._passId++;
   1.463 +        this._matchedSelectors.forEach(function(aValue) {
   1.464 +          aCallback.call(aScope, aValue[0], aValue[1]);
   1.465 +          aValue[0].cssRule._passId = this._passId;
   1.466 +        }, this);
   1.467 +      }
   1.468 +      return;
   1.469 +    }
   1.470 +
   1.471 +    if (!this._matchedRules) {
   1.472 +      this._buildMatchedRules();
   1.473 +    }
   1.474 +
   1.475 +    this._matchedSelectors = [];
   1.476 +    this._passId++;
   1.477 +
   1.478 +    for (let i = 0; i < this._matchedRules.length; i++) {
   1.479 +      let rule = this._matchedRules[i][0];
   1.480 +      let status = this._matchedRules[i][1];
   1.481 +
   1.482 +      rule.selectors.forEach(function (aSelector) {
   1.483 +        if (aSelector._matchId !== this._matchId &&
   1.484 +           (aSelector.elementStyle ||
   1.485 +            this.selectorMatchesElement(rule.domRule, aSelector.selectorIndex))) {
   1.486 +
   1.487 +          aSelector._matchId = this._matchId;
   1.488 +          this._matchedSelectors.push([ aSelector, status ]);
   1.489 +          if (aCallback) {
   1.490 +            aCallback.call(aScope, aSelector, status);
   1.491 +          }
   1.492 +        }
   1.493 +      }, this);
   1.494 +
   1.495 +      rule._passId = this._passId;
   1.496 +    }
   1.497 +  },
   1.498 +
   1.499 +  /**
   1.500 +   * Check if the given selector matches the highlighted element or any of its
   1.501 +   * parents.
   1.502 +   *
   1.503 +   * @private
   1.504 +   * @param {DOMRule} domRule
   1.505 +   *        The DOM Rule containing the selector.
   1.506 +   * @param {Number} idx
   1.507 +   *        The index of the selector within the DOMRule.
   1.508 +   * @return {boolean}
   1.509 +   *         true if the given selector matches the highlighted element or any
   1.510 +   *         of its parents, otherwise false is returned.
   1.511 +   */
   1.512 +  selectorMatchesElement: function CL_selectorMatchesElement2(domRule, idx)
   1.513 +  {
   1.514 +    let element = this.viewedElement;
   1.515 +    do {
   1.516 +      if (domUtils.selectorMatchesElement(element, domRule, idx)) {
   1.517 +        return true;
   1.518 +      }
   1.519 +    } while ((element = element.parentNode) &&
   1.520 +             element.nodeType === Ci.nsIDOMNode.ELEMENT_NODE);
   1.521 +
   1.522 +    return false;
   1.523 +  },
   1.524 +
   1.525 +  /**
   1.526 +   * Check if the highlighted element or it's parents have matched selectors.
   1.527 +   *
   1.528 +   * @param {array} aProperties The list of properties you want to check if they
   1.529 +   * have matched selectors or not.
   1.530 +   * @return {object} An object that tells for each property if it has matched
   1.531 +   * selectors or not. Object keys are property names and values are booleans.
   1.532 +   */
   1.533 +  hasMatchedSelectors: function CL_hasMatchedSelectors(aProperties)
   1.534 +  {
   1.535 +    if (!this._matchedRules) {
   1.536 +      this._buildMatchedRules();
   1.537 +    }
   1.538 +
   1.539 +    let result = {};
   1.540 +
   1.541 +    this._matchedRules.some(function(aValue) {
   1.542 +      let rule = aValue[0];
   1.543 +      let status = aValue[1];
   1.544 +      aProperties = aProperties.filter(function(aProperty) {
   1.545 +        // We just need to find if a rule has this property while it matches
   1.546 +        // the viewedElement (or its parents).
   1.547 +        if (rule.getPropertyValue(aProperty) &&
   1.548 +            (status == CssLogic.STATUS.MATCHED ||
   1.549 +             (status == CssLogic.STATUS.PARENT_MATCH &&
   1.550 +              domUtils.isInheritedProperty(aProperty)))) {
   1.551 +          result[aProperty] = true;
   1.552 +          return false;
   1.553 +        }
   1.554 +        return true; // Keep the property for the next rule.
   1.555 +      }.bind(this));
   1.556 +      return aProperties.length == 0;
   1.557 +    }, this);
   1.558 +
   1.559 +    return result;
   1.560 +  },
   1.561 +
   1.562 +  /**
   1.563 +   * Build the array of matched rules for the currently highlighted element.
   1.564 +   * The array will hold rules that match the viewedElement and its parents.
   1.565 +   *
   1.566 +   * @private
   1.567 +   */
   1.568 +  _buildMatchedRules: function CL__buildMatchedRules()
   1.569 +  {
   1.570 +    let domRules;
   1.571 +    let element = this.viewedElement;
   1.572 +    let filter = this.sourceFilter;
   1.573 +    let sheetIndex = 0;
   1.574 +
   1.575 +    this._matchId++;
   1.576 +    this._passId++;
   1.577 +    this._matchedRules = [];
   1.578 +
   1.579 +    if (!element) {
   1.580 +      return;
   1.581 +    }
   1.582 +
   1.583 +    do {
   1.584 +      let status = this.viewedElement === element ?
   1.585 +                   CssLogic.STATUS.MATCHED : CssLogic.STATUS.PARENT_MATCH;
   1.586 +
   1.587 +      try {
   1.588 +        domRules = domUtils.getCSSStyleRules(element);
   1.589 +      } catch (ex) {
   1.590 +        Services.console.
   1.591 +          logStringMessage("CL__buildMatchedRules error: " + ex);
   1.592 +        continue;
   1.593 +      }
   1.594 +
   1.595 +      for (let i = 0, n = domRules.Count(); i < n; i++) {
   1.596 +        let domRule = domRules.GetElementAt(i);
   1.597 +        if (domRule.type !== Ci.nsIDOMCSSRule.STYLE_RULE) {
   1.598 +          continue;
   1.599 +        }
   1.600 +
   1.601 +        let sheet = this.getSheet(domRule.parentStyleSheet, -1);
   1.602 +        if (sheet._passId !== this._passId) {
   1.603 +          sheet.index = sheetIndex++;
   1.604 +          sheet._passId = this._passId;
   1.605 +        }
   1.606 +
   1.607 +        if (filter === CssLogic.FILTER.USER && !sheet.contentSheet) {
   1.608 +          continue;
   1.609 +        }
   1.610 +
   1.611 +        let rule = sheet.getRule(domRule);
   1.612 +        if (rule._passId === this._passId) {
   1.613 +          continue;
   1.614 +        }
   1.615 +
   1.616 +        rule._matchId = this._matchId;
   1.617 +        rule._passId = this._passId;
   1.618 +        this._matchedRules.push([rule, status]);
   1.619 +      }
   1.620 +
   1.621 +
   1.622 +      // Add element.style information.
   1.623 +      if (element.style && element.style.length > 0) {
   1.624 +        let rule = new CssRule(null, { style: element.style }, element);
   1.625 +        rule._matchId = this._matchId;
   1.626 +        rule._passId = this._passId;
   1.627 +        this._matchedRules.push([rule, status]);
   1.628 +      }
   1.629 +    } while ((element = element.parentNode) &&
   1.630 +              element.nodeType === Ci.nsIDOMNode.ELEMENT_NODE);
   1.631 +  },
   1.632 +
   1.633 +  /**
   1.634 +   * Tells if the given DOM CSS object matches the current view media.
   1.635 +   *
   1.636 +   * @param {object} aDomObject The DOM CSS object to check.
   1.637 +   * @return {boolean} True if the DOM CSS object matches the current view
   1.638 +   * media, or false otherwise.
   1.639 +   */
   1.640 +  mediaMatches: function CL_mediaMatches(aDomObject)
   1.641 +  {
   1.642 +    let mediaText = aDomObject.media.mediaText;
   1.643 +    return !mediaText || this.viewedDocument.defaultView.
   1.644 +                         matchMedia(mediaText).matches;
   1.645 +   },
   1.646 +};
   1.647 +
   1.648 +/**
   1.649 + * If the element has an id, return '#id'. Otherwise return 'tagname[n]' where
   1.650 + * n is the index of this element in its siblings.
   1.651 + * <p>A technically more 'correct' output from the no-id case might be:
   1.652 + * 'tagname:nth-of-type(n)' however this is unlikely to be more understood
   1.653 + * and it is longer.
   1.654 + *
   1.655 + * @param {nsIDOMElement} aElement the element for which you want the short name.
   1.656 + * @return {string} the string to be displayed for aElement.
   1.657 + */
   1.658 +CssLogic.getShortName = function CssLogic_getShortName(aElement)
   1.659 +{
   1.660 +  if (!aElement) {
   1.661 +    return "null";
   1.662 +  }
   1.663 +  if (aElement.id) {
   1.664 +    return "#" + aElement.id;
   1.665 +  }
   1.666 +  let priorSiblings = 0;
   1.667 +  let temp = aElement;
   1.668 +  while (temp = temp.previousElementSibling) {
   1.669 +    priorSiblings++;
   1.670 +  }
   1.671 +  return aElement.tagName + "[" + priorSiblings + "]";
   1.672 +};
   1.673 +
   1.674 +/**
   1.675 + * Get an array of short names from the given element to document.body.
   1.676 + *
   1.677 + * @param {nsIDOMElement} aElement the element for which you want the array of
   1.678 + * short names.
   1.679 + * @return {array} The array of elements.
   1.680 + * <p>Each element is an object of the form:
   1.681 + * <ul>
   1.682 + * <li>{ display: "what to display for the given (parent) element",
   1.683 + * <li>  element: referenceToTheElement }
   1.684 + * </ul>
   1.685 + */
   1.686 +CssLogic.getShortNamePath = function CssLogic_getShortNamePath(aElement)
   1.687 +{
   1.688 +  let doc = aElement.ownerDocument;
   1.689 +  let reply = [];
   1.690 +
   1.691 +  if (!aElement) {
   1.692 +    return reply;
   1.693 +  }
   1.694 +
   1.695 +  // We want to exclude nodes high up the tree (body/html) unless the user
   1.696 +  // has selected that node, in which case we need to report something.
   1.697 +  do {
   1.698 +    reply.unshift({
   1.699 +      display: CssLogic.getShortName(aElement),
   1.700 +      element: aElement
   1.701 +    });
   1.702 +    aElement = aElement.parentNode;
   1.703 +  } while (aElement && aElement != doc.body && aElement != doc.head && aElement != doc);
   1.704 +
   1.705 +  return reply;
   1.706 +};
   1.707 +
   1.708 +/**
   1.709 + * Get a string list of selectors for a given DOMRule.
   1.710 + *
   1.711 + * @param {DOMRule} aDOMRule
   1.712 + *        The DOMRule to parse.
   1.713 + * @return {Array}
   1.714 + *         An array of string selectors.
   1.715 + */
   1.716 +CssLogic.getSelectors = function CssLogic_getSelectors(aDOMRule)
   1.717 +{
   1.718 +  let selectors = [];
   1.719 +
   1.720 +  let len = domUtils.getSelectorCount(aDOMRule);
   1.721 +  for (let i = 0; i < len; i++) {
   1.722 +    let text = domUtils.getSelectorText(aDOMRule, i);
   1.723 +    selectors.push(text);
   1.724 +  }
   1.725 +  return selectors;
   1.726 +}
   1.727 +
   1.728 +/**
   1.729 + * Memonized lookup of a l10n string from a string bundle.
   1.730 + * @param {string} aName The key to lookup.
   1.731 + * @returns A localized version of the given key.
   1.732 + */
   1.733 +CssLogic.l10n = function(aName) CssLogic._strings.GetStringFromName(aName);
   1.734 +
   1.735 +XPCOMUtils.defineLazyGetter(CssLogic, "_strings", function() Services.strings
   1.736 +        .createBundle("chrome://global/locale/devtools/styleinspector.properties"));
   1.737 +
   1.738 +/**
   1.739 + * Is the given property sheet a content stylesheet?
   1.740 + *
   1.741 + * @param {CSSStyleSheet} aSheet a stylesheet
   1.742 + * @return {boolean} true if the given stylesheet is a content stylesheet,
   1.743 + * false otherwise.
   1.744 + */
   1.745 +CssLogic.isContentStylesheet = function CssLogic_isContentStylesheet(aSheet)
   1.746 +{
   1.747 +  // All sheets with owner nodes have been included by content.
   1.748 +  if (aSheet.ownerNode) {
   1.749 +    return true;
   1.750 +  }
   1.751 +
   1.752 +  // If the sheet has a CSSImportRule we need to check the parent stylesheet.
   1.753 +  if (aSheet.ownerRule instanceof Ci.nsIDOMCSSImportRule) {
   1.754 +    return CssLogic.isContentStylesheet(aSheet.parentStyleSheet);
   1.755 +  }
   1.756 +
   1.757 +  return false;
   1.758 +};
   1.759 +
   1.760 +/**
   1.761 + * Get a source for a stylesheet, taking into account embedded stylesheets
   1.762 + * for which we need to use document.defaultView.location.href rather than
   1.763 + * sheet.href
   1.764 + *
   1.765 + * @param {CSSStyleSheet} aSheet the DOM object for the style sheet.
   1.766 + * @return {string} the address of the stylesheet.
   1.767 + */
   1.768 +CssLogic.href = function CssLogic_href(aSheet)
   1.769 +{
   1.770 +  let href = aSheet.href;
   1.771 +  if (!href) {
   1.772 +    href = aSheet.ownerNode.ownerDocument.location;
   1.773 +  }
   1.774 +
   1.775 +  return href;
   1.776 +};
   1.777 +
   1.778 +/**
   1.779 + * Return a shortened version of a style sheet's source.
   1.780 + *
   1.781 + * @param {CSSStyleSheet} aSheet the DOM object for the style sheet.
   1.782 + */
   1.783 +CssLogic.shortSource = function CssLogic_shortSource(aSheet)
   1.784 +{
   1.785 +  // Use a string like "inline" if there is no source href
   1.786 +  if (!aSheet || !aSheet.href) {
   1.787 +    return CssLogic.l10n("rule.sourceInline");
   1.788 +  }
   1.789 +
   1.790 +  // We try, in turn, the filename, filePath, query string, whole thing
   1.791 +  let url = {};
   1.792 +  try {
   1.793 +    url = Services.io.newURI(aSheet.href, null, null);
   1.794 +    url = url.QueryInterface(Ci.nsIURL);
   1.795 +  } catch (ex) {
   1.796 +    // Some UA-provided stylesheets are not valid URLs.
   1.797 +  }
   1.798 +
   1.799 +  if (url.fileName) {
   1.800 +    return url.fileName;
   1.801 +  }
   1.802 +
   1.803 +  if (url.filePath) {
   1.804 +    return url.filePath;
   1.805 +  }
   1.806 +
   1.807 +  if (url.query) {
   1.808 +    return url.query;
   1.809 +  }
   1.810 +
   1.811 +  let dataUrl = aSheet.href.match(/^(data:[^,]*),/);
   1.812 +  return dataUrl ? dataUrl[1] : aSheet.href;
   1.813 +}
   1.814 +
   1.815 +/**
   1.816 + * Extract the background image URL (if any) from a property value.
   1.817 + * Used, for example, for the preview tooltip in the rule view and
   1.818 + * computed view.
   1.819 + *
   1.820 + * @param {String} aProperty
   1.821 + * @param {String} aSheetHref
   1.822 + * @return {string} a image URL
   1.823 + */
   1.824 +CssLogic.getBackgroundImageUriFromProperty = function(aProperty, aSheetHref) {
   1.825 +  let startToken = "url(", start = aProperty.indexOf(startToken), end;
   1.826 +  if (start === -1) {
   1.827 +    return null;
   1.828 +  }
   1.829 +
   1.830 +  aProperty = aProperty.substring(start + startToken.length).trim();
   1.831 +  let quote = aProperty.substring(0, 1);
   1.832 +  if (quote === "'" || quote === '"') {
   1.833 +    end = aProperty.search(new RegExp(quote + "\\s*\\)"));
   1.834 +    start = 1;
   1.835 +  } else {
   1.836 +    end = aProperty.indexOf(")");
   1.837 +    start = 0;
   1.838 +  }
   1.839 +
   1.840 +  let uri = aProperty.substring(start, end).trim();
   1.841 +  if (aSheetHref) {
   1.842 +    let IOService = Cc["@mozilla.org/network/io-service;1"]
   1.843 +      .getService(Ci.nsIIOService);
   1.844 +    let sheetUri = IOService.newURI(aSheetHref, null, null);
   1.845 +    uri = sheetUri.resolve(uri);
   1.846 +  }
   1.847 +
   1.848 +  return uri;
   1.849 +}
   1.850 +
   1.851 +/**
   1.852 + * Find the position of [element] in [nodeList].
   1.853 + * @returns an index of the match, or -1 if there is no match
   1.854 + */
   1.855 +function positionInNodeList(element, nodeList) {
   1.856 +  for (var i = 0; i < nodeList.length; i++) {
   1.857 +    if (element === nodeList[i]) {
   1.858 +      return i;
   1.859 +    }
   1.860 +  }
   1.861 +  return -1;
   1.862 +}
   1.863 +
   1.864 +/**
   1.865 + * Find a unique CSS selector for a given element
   1.866 + * @returns a string such that ele.ownerDocument.querySelector(reply) === ele
   1.867 + * and ele.ownerDocument.querySelectorAll(reply).length === 1
   1.868 + */
   1.869 +CssLogic.findCssSelector = function CssLogic_findCssSelector(ele) {
   1.870 +  var document = ele.ownerDocument;
   1.871 +  if (ele.id && document.getElementById(ele.id) === ele) {
   1.872 +    return '#' + ele.id;
   1.873 +  }
   1.874 +
   1.875 +  // Inherently unique by tag name
   1.876 +  var tagName = ele.tagName.toLowerCase();
   1.877 +  if (tagName === 'html') {
   1.878 +    return 'html';
   1.879 +  }
   1.880 +  if (tagName === 'head') {
   1.881 +    return 'head';
   1.882 +  }
   1.883 +  if (tagName === 'body') {
   1.884 +    return 'body';
   1.885 +  }
   1.886 +
   1.887 +  if (ele.parentNode == null) {
   1.888 +    console.log('danger: ' + tagName);
   1.889 +  }
   1.890 +
   1.891 +  // We might be able to find a unique class name
   1.892 +  var selector, index, matches;
   1.893 +  if (ele.classList.length > 0) {
   1.894 +    for (var i = 0; i < ele.classList.length; i++) {
   1.895 +      // Is this className unique by itself?
   1.896 +      selector = '.' + ele.classList.item(i);
   1.897 +      matches = document.querySelectorAll(selector);
   1.898 +      if (matches.length === 1) {
   1.899 +        return selector;
   1.900 +      }
   1.901 +      // Maybe it's unique with a tag name?
   1.902 +      selector = tagName + selector;
   1.903 +      matches = document.querySelectorAll(selector);
   1.904 +      if (matches.length === 1) {
   1.905 +        return selector;
   1.906 +      }
   1.907 +      // Maybe it's unique using a tag name and nth-child
   1.908 +      index = positionInNodeList(ele, ele.parentNode.children) + 1;
   1.909 +      selector = selector + ':nth-child(' + index + ')';
   1.910 +      matches = document.querySelectorAll(selector);
   1.911 +      if (matches.length === 1) {
   1.912 +        return selector;
   1.913 +      }
   1.914 +    }
   1.915 +  }
   1.916 +
   1.917 +  // So we can be unique w.r.t. our parent, and use recursion
   1.918 +  index = positionInNodeList(ele, ele.parentNode.children) + 1;
   1.919 +  selector = CssLogic_findCssSelector(ele.parentNode) + ' > ' +
   1.920 +          tagName + ':nth-child(' + index + ')';
   1.921 +
   1.922 +  return selector;
   1.923 +};
   1.924 +
   1.925 +/**
   1.926 + * A safe way to access cached bits of information about a stylesheet.
   1.927 + *
   1.928 + * @constructor
   1.929 + * @param {CssLogic} aCssLogic pointer to the CssLogic instance working with
   1.930 + * this CssSheet object.
   1.931 + * @param {CSSStyleSheet} aDomSheet reference to a DOM CSSStyleSheet object.
   1.932 + * @param {number} aIndex tells the index/position of the stylesheet within the
   1.933 + * main document.
   1.934 + */
   1.935 +function CssSheet(aCssLogic, aDomSheet, aIndex)
   1.936 +{
   1.937 +  this._cssLogic = aCssLogic;
   1.938 +  this.domSheet = aDomSheet;
   1.939 +  this.index = this.contentSheet ? aIndex : -100 * aIndex;
   1.940 +
   1.941 +  // Cache of the sheets href. Cached by the getter.
   1.942 +  this._href = null;
   1.943 +  // Short version of href for use in select boxes etc. Cached by getter.
   1.944 +  this._shortSource = null;
   1.945 +
   1.946 +  // null for uncached.
   1.947 +  this._sheetAllowed = null;
   1.948 +
   1.949 +  // Cached CssRules from the given stylesheet.
   1.950 +  this._rules = {};
   1.951 +
   1.952 +  this._ruleCount = -1;
   1.953 +}
   1.954 +
   1.955 +CssSheet.prototype = {
   1.956 +  _passId: null,
   1.957 +  _contentSheet: null,
   1.958 +  _mediaMatches: null,
   1.959 +
   1.960 +  /**
   1.961 +   * Tells if the stylesheet is provided by the browser or not.
   1.962 +   *
   1.963 +   * @return {boolean} false if this is a browser-provided stylesheet, or true
   1.964 +   * otherwise.
   1.965 +   */
   1.966 +  get contentSheet()
   1.967 +  {
   1.968 +    if (this._contentSheet === null) {
   1.969 +      this._contentSheet = CssLogic.isContentStylesheet(this.domSheet);
   1.970 +    }
   1.971 +    return this._contentSheet;
   1.972 +  },
   1.973 +
   1.974 +  /**
   1.975 +   * Tells if the stylesheet is disabled or not.
   1.976 +   * @return {boolean} true if this stylesheet is disabled, or false otherwise.
   1.977 +   */
   1.978 +  get disabled()
   1.979 +  {
   1.980 +    return this.domSheet.disabled;
   1.981 +  },
   1.982 +
   1.983 +  /**
   1.984 +   * Tells if the stylesheet matches the current browser view media.
   1.985 +   * @return {boolean} true if this stylesheet matches the current browser view
   1.986 +   * media, or false otherwise.
   1.987 +   */
   1.988 +  get mediaMatches()
   1.989 +  {
   1.990 +    if (this._mediaMatches === null) {
   1.991 +      this._mediaMatches = this._cssLogic.mediaMatches(this.domSheet);
   1.992 +    }
   1.993 +    return this._mediaMatches;
   1.994 +  },
   1.995 +
   1.996 +  /**
   1.997 +   * Get a source for a stylesheet, using CssLogic.href
   1.998 +   *
   1.999 +   * @return {string} the address of the stylesheet.
  1.1000 +   */
  1.1001 +  get href()
  1.1002 +  {
  1.1003 +    if (this._href) {
  1.1004 +      return this._href;
  1.1005 +    }
  1.1006 +
  1.1007 +    this._href = CssLogic.href(this.domSheet);
  1.1008 +    return this._href;
  1.1009 +  },
  1.1010 +
  1.1011 +  /**
  1.1012 +   * Create a shorthand version of the href of a stylesheet.
  1.1013 +   *
  1.1014 +   * @return {string} the shorthand source of the stylesheet.
  1.1015 +   */
  1.1016 +  get shortSource()
  1.1017 +  {
  1.1018 +    if (this._shortSource) {
  1.1019 +      return this._shortSource;
  1.1020 +    }
  1.1021 +
  1.1022 +    this._shortSource = CssLogic.shortSource(this.domSheet);
  1.1023 +    return this._shortSource;
  1.1024 +  },
  1.1025 +
  1.1026 +  /**
  1.1027 +   * Tells if the sheet is allowed or not by the current CssLogic.sourceFilter.
  1.1028 +   *
  1.1029 +   * @return {boolean} true if the stylesheet is allowed by the sourceFilter, or
  1.1030 +   * false otherwise.
  1.1031 +   */
  1.1032 +  get sheetAllowed()
  1.1033 +  {
  1.1034 +    if (this._sheetAllowed !== null) {
  1.1035 +      return this._sheetAllowed;
  1.1036 +    }
  1.1037 +
  1.1038 +    this._sheetAllowed = true;
  1.1039 +
  1.1040 +    let filter = this._cssLogic.sourceFilter;
  1.1041 +    if (filter === CssLogic.FILTER.USER && !this.contentSheet) {
  1.1042 +      this._sheetAllowed = false;
  1.1043 +    }
  1.1044 +    if (filter !== CssLogic.FILTER.USER && filter !== CssLogic.FILTER.UA) {
  1.1045 +      this._sheetAllowed = (filter === this.href);
  1.1046 +    }
  1.1047 +
  1.1048 +    return this._sheetAllowed;
  1.1049 +  },
  1.1050 +
  1.1051 +  /**
  1.1052 +   * Retrieve the number of rules in this stylesheet.
  1.1053 +   *
  1.1054 +   * @return {number} the number of nsIDOMCSSRule objects in this stylesheet.
  1.1055 +   */
  1.1056 +  get ruleCount()
  1.1057 +  {
  1.1058 +    return this._ruleCount > -1 ?
  1.1059 +      this._ruleCount :
  1.1060 +      this.domSheet.cssRules.length;
  1.1061 +  },
  1.1062 +
  1.1063 +  /**
  1.1064 +   * Retrieve a CssRule object for the given CSSStyleRule. The CssRule object is
  1.1065 +   * cached, such that subsequent retrievals return the same CssRule object for
  1.1066 +   * the same CSSStyleRule object.
  1.1067 +   *
  1.1068 +   * @param {CSSStyleRule} aDomRule the CSSStyleRule object for which you want a
  1.1069 +   * CssRule object.
  1.1070 +   * @return {CssRule} the cached CssRule object for the given CSSStyleRule
  1.1071 +   * object.
  1.1072 +   */
  1.1073 +  getRule: function CssSheet_getRule(aDomRule)
  1.1074 +  {
  1.1075 +    let cacheId = aDomRule.type + aDomRule.selectorText;
  1.1076 +
  1.1077 +    let rule = null;
  1.1078 +    let ruleFound = false;
  1.1079 +
  1.1080 +    if (cacheId in this._rules) {
  1.1081 +      for (let i = 0, rulesLen = this._rules[cacheId].length; i < rulesLen; i++) {
  1.1082 +        rule = this._rules[cacheId][i];
  1.1083 +        if (rule.domRule === aDomRule) {
  1.1084 +          ruleFound = true;
  1.1085 +          break;
  1.1086 +        }
  1.1087 +      }
  1.1088 +    }
  1.1089 +
  1.1090 +    if (!ruleFound) {
  1.1091 +      if (!(cacheId in this._rules)) {
  1.1092 +        this._rules[cacheId] = [];
  1.1093 +      }
  1.1094 +
  1.1095 +      rule = new CssRule(this, aDomRule);
  1.1096 +      this._rules[cacheId].push(rule);
  1.1097 +    }
  1.1098 +
  1.1099 +    return rule;
  1.1100 +  },
  1.1101 +
  1.1102 +  /**
  1.1103 +   * Process each rule in this stylesheet using your callback function. Your
  1.1104 +   * function receives one argument: the CssRule object for each CSSStyleRule
  1.1105 +   * inside the stylesheet.
  1.1106 +   *
  1.1107 +   * Note that this method also iterates through @media rules inside the
  1.1108 +   * stylesheet.
  1.1109 +   *
  1.1110 +   * @param {function} aCallback the function you want to execute for each of
  1.1111 +   * the style rules.
  1.1112 +   * @param {object} aScope the scope you want for the callback function. aScope
  1.1113 +   * will be the this object when aCallback executes.
  1.1114 +   */
  1.1115 +  forEachRule: function CssSheet_forEachRule(aCallback, aScope)
  1.1116 +  {
  1.1117 +    let ruleCount = 0;
  1.1118 +    let domRules = this.domSheet.cssRules;
  1.1119 +
  1.1120 +    function _iterator(aDomRule) {
  1.1121 +      if (aDomRule.type == Ci.nsIDOMCSSRule.STYLE_RULE) {
  1.1122 +        aCallback.call(aScope, this.getRule(aDomRule));
  1.1123 +        ruleCount++;
  1.1124 +      } else if (aDomRule.type == Ci.nsIDOMCSSRule.MEDIA_RULE &&
  1.1125 +          aDomRule.cssRules && this._cssLogic.mediaMatches(aDomRule)) {
  1.1126 +        Array.prototype.forEach.call(aDomRule.cssRules, _iterator, this);
  1.1127 +      }
  1.1128 +    }
  1.1129 +
  1.1130 +    Array.prototype.forEach.call(domRules, _iterator, this);
  1.1131 +
  1.1132 +    this._ruleCount = ruleCount;
  1.1133 +  },
  1.1134 +
  1.1135 +  /**
  1.1136 +   * Process *some* rules in this stylesheet using your callback function. Your
  1.1137 +   * function receives one argument: the CssRule object for each CSSStyleRule
  1.1138 +   * inside the stylesheet. In order to stop processing the callback function
  1.1139 +   * needs to return a value.
  1.1140 +   *
  1.1141 +   * Note that this method also iterates through @media rules inside the
  1.1142 +   * stylesheet.
  1.1143 +   *
  1.1144 +   * @param {function} aCallback the function you want to execute for each of
  1.1145 +   * the style rules.
  1.1146 +   * @param {object} aScope the scope you want for the callback function. aScope
  1.1147 +   * will be the this object when aCallback executes.
  1.1148 +   * @return {Boolean} true if aCallback returns true during any iteration,
  1.1149 +   * otherwise false is returned.
  1.1150 +   */
  1.1151 +  forSomeRules: function CssSheet_forSomeRules(aCallback, aScope)
  1.1152 +  {
  1.1153 +    let domRules = this.domSheet.cssRules;
  1.1154 +    function _iterator(aDomRule) {
  1.1155 +      if (aDomRule.type == Ci.nsIDOMCSSRule.STYLE_RULE) {
  1.1156 +        return aCallback.call(aScope, this.getRule(aDomRule));
  1.1157 +      } else if (aDomRule.type == Ci.nsIDOMCSSRule.MEDIA_RULE &&
  1.1158 +          aDomRule.cssRules && this._cssLogic.mediaMatches(aDomRule)) {
  1.1159 +        return Array.prototype.some.call(aDomRule.cssRules, _iterator, this);
  1.1160 +      }
  1.1161 +    }
  1.1162 +    return Array.prototype.some.call(domRules, _iterator, this);
  1.1163 +  },
  1.1164 +
  1.1165 +  toString: function CssSheet_toString()
  1.1166 +  {
  1.1167 +    return "CssSheet[" + this.shortSource + "]";
  1.1168 +  }
  1.1169 +};
  1.1170 +
  1.1171 +/**
  1.1172 + * Information about a single CSSStyleRule.
  1.1173 + *
  1.1174 + * @param {CSSSheet|null} aCssSheet the CssSheet object of the stylesheet that
  1.1175 + * holds the CSSStyleRule. If the rule comes from element.style, set this
  1.1176 + * argument to null.
  1.1177 + * @param {CSSStyleRule|object} aDomRule the DOM CSSStyleRule for which you want
  1.1178 + * to cache data. If the rule comes from element.style, then provide
  1.1179 + * an object of the form: {style: element.style}.
  1.1180 + * @param {Element} [aElement] If the rule comes from element.style, then this
  1.1181 + * argument must point to the element.
  1.1182 + * @constructor
  1.1183 + */
  1.1184 +function CssRule(aCssSheet, aDomRule, aElement)
  1.1185 +{
  1.1186 +  this._cssSheet = aCssSheet;
  1.1187 +  this.domRule = aDomRule;
  1.1188 +
  1.1189 +  let parentRule = aDomRule.parentRule;
  1.1190 +  if (parentRule && parentRule.type == Ci.nsIDOMCSSRule.MEDIA_RULE) {
  1.1191 +    this.mediaText = parentRule.media.mediaText;
  1.1192 +  }
  1.1193 +
  1.1194 +  if (this._cssSheet) {
  1.1195 +    // parse domRule.selectorText on call to this.selectors
  1.1196 +    this._selectors = null;
  1.1197 +    this.line = domUtils.getRuleLine(this.domRule);
  1.1198 +    this.source = this._cssSheet.shortSource + ":" + this.line;
  1.1199 +    if (this.mediaText) {
  1.1200 +      this.source += " @media " + this.mediaText;
  1.1201 +    }
  1.1202 +    this.href = this._cssSheet.href;
  1.1203 +    this.contentRule = this._cssSheet.contentSheet;
  1.1204 +  } else if (aElement) {
  1.1205 +    this._selectors = [ new CssSelector(this, "@element.style", 0) ];
  1.1206 +    this.line = -1;
  1.1207 +    this.source = CssLogic.l10n("rule.sourceElement");
  1.1208 +    this.href = "#";
  1.1209 +    this.contentRule = true;
  1.1210 +    this.sourceElement = aElement;
  1.1211 +  }
  1.1212 +}
  1.1213 +
  1.1214 +CssRule.prototype = {
  1.1215 +  _passId: null,
  1.1216 +
  1.1217 +  mediaText: "",
  1.1218 +
  1.1219 +  get isMediaRule()
  1.1220 +  {
  1.1221 +    return !!this.mediaText;
  1.1222 +  },
  1.1223 +
  1.1224 +  /**
  1.1225 +   * Check if the parent stylesheet is allowed by the CssLogic.sourceFilter.
  1.1226 +   *
  1.1227 +   * @return {boolean} true if the parent stylesheet is allowed by the current
  1.1228 +   * sourceFilter, or false otherwise.
  1.1229 +   */
  1.1230 +  get sheetAllowed()
  1.1231 +  {
  1.1232 +    return this._cssSheet ? this._cssSheet.sheetAllowed : true;
  1.1233 +  },
  1.1234 +
  1.1235 +  /**
  1.1236 +   * Retrieve the parent stylesheet index/position in the viewed document.
  1.1237 +   *
  1.1238 +   * @return {number} the parent stylesheet index/position in the viewed
  1.1239 +   * document.
  1.1240 +   */
  1.1241 +  get sheetIndex()
  1.1242 +  {
  1.1243 +    return this._cssSheet ? this._cssSheet.index : 0;
  1.1244 +  },
  1.1245 +
  1.1246 +  /**
  1.1247 +   * Retrieve the style property value from the current CSSStyleRule.
  1.1248 +   *
  1.1249 +   * @param {string} aProperty the CSS property name for which you want the
  1.1250 +   * value.
  1.1251 +   * @return {string} the property value.
  1.1252 +   */
  1.1253 +  getPropertyValue: function(aProperty)
  1.1254 +  {
  1.1255 +    return this.domRule.style.getPropertyValue(aProperty);
  1.1256 +  },
  1.1257 +
  1.1258 +  /**
  1.1259 +   * Retrieve the style property priority from the current CSSStyleRule.
  1.1260 +   *
  1.1261 +   * @param {string} aProperty the CSS property name for which you want the
  1.1262 +   * priority.
  1.1263 +   * @return {string} the property priority.
  1.1264 +   */
  1.1265 +  getPropertyPriority: function(aProperty)
  1.1266 +  {
  1.1267 +    return this.domRule.style.getPropertyPriority(aProperty);
  1.1268 +  },
  1.1269 +
  1.1270 +  /**
  1.1271 +   * Retrieve the list of CssSelector objects for each of the parsed selectors
  1.1272 +   * of the current CSSStyleRule.
  1.1273 +   *
  1.1274 +   * @return {array} the array hold the CssSelector objects.
  1.1275 +   */
  1.1276 +  get selectors()
  1.1277 +  {
  1.1278 +    if (this._selectors) {
  1.1279 +      return this._selectors;
  1.1280 +    }
  1.1281 +
  1.1282 +    // Parse the CSSStyleRule.selectorText string.
  1.1283 +    this._selectors = [];
  1.1284 +
  1.1285 +    if (!this.domRule.selectorText) {
  1.1286 +      return this._selectors;
  1.1287 +    }
  1.1288 +
  1.1289 +    let selectors = CssLogic.getSelectors(this.domRule);
  1.1290 +
  1.1291 +    for (let i = 0, len = selectors.length; i < len; i++) {
  1.1292 +      this._selectors.push(new CssSelector(this, selectors[i], i));
  1.1293 +    }
  1.1294 +
  1.1295 +    return this._selectors;
  1.1296 +  },
  1.1297 +
  1.1298 +  toString: function CssRule_toString()
  1.1299 +  {
  1.1300 +    return "[CssRule " + this.domRule.selectorText + "]";
  1.1301 +  },
  1.1302 +};
  1.1303 +
  1.1304 +/**
  1.1305 + * The CSS selector class allows us to document the ranking of various CSS
  1.1306 + * selectors.
  1.1307 + *
  1.1308 + * @constructor
  1.1309 + * @param {CssRule} aCssRule the CssRule instance from where the selector comes.
  1.1310 + * @param {string} aSelector The selector that we wish to investigate.
  1.1311 + * @param {Number} aIndex The index of the selector within it's rule.
  1.1312 + */
  1.1313 +function CssSelector(aCssRule, aSelector, aIndex)
  1.1314 +{
  1.1315 +  this.cssRule = aCssRule;
  1.1316 +  this.text = aSelector;
  1.1317 +  this.elementStyle = this.text == "@element.style";
  1.1318 +  this._specificity = null;
  1.1319 +  this.selectorIndex = aIndex;
  1.1320 +}
  1.1321 +
  1.1322 +exports.CssSelector = CssSelector;
  1.1323 +
  1.1324 +CssSelector.prototype = {
  1.1325 +  _matchId: null,
  1.1326 +
  1.1327 +  /**
  1.1328 +   * Retrieve the CssSelector source, which is the source of the CssSheet owning
  1.1329 +   * the selector.
  1.1330 +   *
  1.1331 +   * @return {string} the selector source.
  1.1332 +   */
  1.1333 +  get source()
  1.1334 +  {
  1.1335 +    return this.cssRule.source;
  1.1336 +  },
  1.1337 +
  1.1338 +  /**
  1.1339 +   * Retrieve the CssSelector source element, which is the source of the CssRule
  1.1340 +   * owning the selector. This is only available when the CssSelector comes from
  1.1341 +   * an element.style.
  1.1342 +   *
  1.1343 +   * @return {string} the source element selector.
  1.1344 +   */
  1.1345 +  get sourceElement()
  1.1346 +  {
  1.1347 +    return this.cssRule.sourceElement;
  1.1348 +  },
  1.1349 +
  1.1350 +  /**
  1.1351 +   * Retrieve the address of the CssSelector. This points to the address of the
  1.1352 +   * CssSheet owning this selector.
  1.1353 +   *
  1.1354 +   * @return {string} the address of the CssSelector.
  1.1355 +   */
  1.1356 +  get href()
  1.1357 +  {
  1.1358 +    return this.cssRule.href;
  1.1359 +  },
  1.1360 +
  1.1361 +  /**
  1.1362 +   * Check if the selector comes from a browser-provided stylesheet.
  1.1363 +   *
  1.1364 +   * @return {boolean} true if the selector comes from a content-provided
  1.1365 +   * stylesheet, or false otherwise.
  1.1366 +   */
  1.1367 +  get contentRule()
  1.1368 +  {
  1.1369 +    return this.cssRule.contentRule;
  1.1370 +  },
  1.1371 +
  1.1372 +  /**
  1.1373 +   * Check if the parent stylesheet is allowed by the CssLogic.sourceFilter.
  1.1374 +   *
  1.1375 +   * @return {boolean} true if the parent stylesheet is allowed by the current
  1.1376 +   * sourceFilter, or false otherwise.
  1.1377 +   */
  1.1378 +  get sheetAllowed()
  1.1379 +  {
  1.1380 +    return this.cssRule.sheetAllowed;
  1.1381 +  },
  1.1382 +
  1.1383 +  /**
  1.1384 +   * Retrieve the parent stylesheet index/position in the viewed document.
  1.1385 +   *
  1.1386 +   * @return {number} the parent stylesheet index/position in the viewed
  1.1387 +   * document.
  1.1388 +   */
  1.1389 +  get sheetIndex()
  1.1390 +  {
  1.1391 +    return this.cssRule.sheetIndex;
  1.1392 +  },
  1.1393 +
  1.1394 +  /**
  1.1395 +   * Retrieve the line of the parent CSSStyleRule in the parent CSSStyleSheet.
  1.1396 +   *
  1.1397 +   * @return {number} the line of the parent CSSStyleRule in the parent
  1.1398 +   * stylesheet.
  1.1399 +   */
  1.1400 +  get ruleLine()
  1.1401 +  {
  1.1402 +    return this.cssRule.line;
  1.1403 +  },
  1.1404 +
  1.1405 +  /**
  1.1406 +   * Retrieve the pseudo-elements that we support. This list should match the
  1.1407 +   * elements specified in layout/style/nsCSSPseudoElementList.h
  1.1408 +   */
  1.1409 +  get pseudoElements()
  1.1410 +  {
  1.1411 +    if (!CssSelector._pseudoElements) {
  1.1412 +      let pseudos = CssSelector._pseudoElements = new Set();
  1.1413 +      pseudos.add("after");
  1.1414 +      pseudos.add("before");
  1.1415 +      pseudos.add("first-letter");
  1.1416 +      pseudos.add("first-line");
  1.1417 +      pseudos.add("selection");
  1.1418 +      pseudos.add("-moz-color-swatch");
  1.1419 +      pseudos.add("-moz-focus-inner");
  1.1420 +      pseudos.add("-moz-focus-outer");
  1.1421 +      pseudos.add("-moz-list-bullet");
  1.1422 +      pseudos.add("-moz-list-number");
  1.1423 +      pseudos.add("-moz-math-anonymous");
  1.1424 +      pseudos.add("-moz-math-stretchy");
  1.1425 +      pseudos.add("-moz-progress-bar");
  1.1426 +      pseudos.add("-moz-selection");
  1.1427 +    }
  1.1428 +    return CssSelector._pseudoElements;
  1.1429 +  },
  1.1430 +
  1.1431 +  /**
  1.1432 +   * Retrieve specificity information for the current selector.
  1.1433 +   *
  1.1434 +   * @see http://www.w3.org/TR/css3-selectors/#specificity
  1.1435 +   * @see http://www.w3.org/TR/CSS2/selector.html
  1.1436 +   *
  1.1437 +   * @return {Number} The selector's specificity.
  1.1438 +   */
  1.1439 +  get specificity()
  1.1440 +  {
  1.1441 +    if (this._specificity) {
  1.1442 +      return this._specificity;
  1.1443 +    }
  1.1444 +
  1.1445 +    this._specificity = domUtils.getSpecificity(this.cssRule.domRule,
  1.1446 +                                                this.selectorIndex);
  1.1447 +
  1.1448 +    return this._specificity;
  1.1449 +  },
  1.1450 +
  1.1451 +  toString: function CssSelector_toString()
  1.1452 +  {
  1.1453 +    return this.text;
  1.1454 +  },
  1.1455 +};
  1.1456 +
  1.1457 +/**
  1.1458 + * A cache of information about the matched rules, selectors and values attached
  1.1459 + * to a CSS property, for the highlighted element.
  1.1460 + *
  1.1461 + * The heart of the CssPropertyInfo object is the _findMatchedSelectors()
  1.1462 + * method. This are invoked when the PropertyView tries to access the
  1.1463 + * .matchedSelectors array.
  1.1464 + * Results are cached, for later reuse.
  1.1465 + *
  1.1466 + * @param {CssLogic} aCssLogic Reference to the parent CssLogic instance
  1.1467 + * @param {string} aProperty The CSS property we are gathering information for
  1.1468 + * @constructor
  1.1469 + */
  1.1470 +function CssPropertyInfo(aCssLogic, aProperty)
  1.1471 +{
  1.1472 +  this._cssLogic = aCssLogic;
  1.1473 +  this.property = aProperty;
  1.1474 +  this._value = "";
  1.1475 +
  1.1476 +  // The number of matched rules holding the this.property style property.
  1.1477 +  // Additionally, only rules that come from allowed stylesheets are counted.
  1.1478 +  this._matchedRuleCount = 0;
  1.1479 +
  1.1480 +  // An array holding CssSelectorInfo objects for each of the matched selectors
  1.1481 +  // that are inside a CSS rule. Only rules that hold the this.property are
  1.1482 +  // counted. This includes rules that come from filtered stylesheets (those
  1.1483 +  // that have sheetAllowed = false).
  1.1484 +  this._matchedSelectors = null;
  1.1485 +}
  1.1486 +
  1.1487 +CssPropertyInfo.prototype = {
  1.1488 +  /**
  1.1489 +   * Retrieve the computed style value for the current property, for the
  1.1490 +   * highlighted element.
  1.1491 +   *
  1.1492 +   * @return {string} the computed style value for the current property, for the
  1.1493 +   * highlighted element.
  1.1494 +   */
  1.1495 +  get value()
  1.1496 +  {
  1.1497 +    if (!this._value && this._cssLogic._computedStyle) {
  1.1498 +      try {
  1.1499 +        this._value = this._cssLogic._computedStyle.getPropertyValue(this.property);
  1.1500 +      } catch (ex) {
  1.1501 +        Services.console.logStringMessage('Error reading computed style for ' +
  1.1502 +          this.property);
  1.1503 +        Services.console.logStringMessage(ex);
  1.1504 +      }
  1.1505 +    }
  1.1506 +    return this._value;
  1.1507 +  },
  1.1508 +
  1.1509 +  /**
  1.1510 +   * Retrieve the number of matched rules holding the this.property style
  1.1511 +   * property. Only rules that come from allowed stylesheets are counted.
  1.1512 +   *
  1.1513 +   * @return {number} the number of matched rules.
  1.1514 +   */
  1.1515 +  get matchedRuleCount()
  1.1516 +  {
  1.1517 +    if (!this._matchedSelectors) {
  1.1518 +      this._findMatchedSelectors();
  1.1519 +    } else if (this.needRefilter) {
  1.1520 +      this._refilterSelectors();
  1.1521 +    }
  1.1522 +
  1.1523 +    return this._matchedRuleCount;
  1.1524 +  },
  1.1525 +
  1.1526 +  /**
  1.1527 +   * Retrieve the array holding CssSelectorInfo objects for each of the matched
  1.1528 +   * selectors, from each of the matched rules. Only selectors coming from
  1.1529 +   * allowed stylesheets are included in the array.
  1.1530 +   *
  1.1531 +   * @return {array} the list of CssSelectorInfo objects of selectors that match
  1.1532 +   * the highlighted element and its parents.
  1.1533 +   */
  1.1534 +  get matchedSelectors()
  1.1535 +  {
  1.1536 +    if (!this._matchedSelectors) {
  1.1537 +      this._findMatchedSelectors();
  1.1538 +    } else if (this.needRefilter) {
  1.1539 +      this._refilterSelectors();
  1.1540 +    }
  1.1541 +
  1.1542 +    return this._matchedSelectors;
  1.1543 +  },
  1.1544 +
  1.1545 +  /**
  1.1546 +   * Find the selectors that match the highlighted element and its parents.
  1.1547 +   * Uses CssLogic.processMatchedSelectors() to find the matched selectors,
  1.1548 +   * passing in a reference to CssPropertyInfo._processMatchedSelector() to
  1.1549 +   * create CssSelectorInfo objects, which we then sort
  1.1550 +   * @private
  1.1551 +   */
  1.1552 +  _findMatchedSelectors: function CssPropertyInfo_findMatchedSelectors()
  1.1553 +  {
  1.1554 +    this._matchedSelectors = [];
  1.1555 +    this._matchedRuleCount = 0;
  1.1556 +    this.needRefilter = false;
  1.1557 +
  1.1558 +    this._cssLogic.processMatchedSelectors(this._processMatchedSelector, this);
  1.1559 +
  1.1560 +    // Sort the selectors by how well they match the given element.
  1.1561 +    this._matchedSelectors.sort(function(aSelectorInfo1, aSelectorInfo2) {
  1.1562 +      if (aSelectorInfo1.status > aSelectorInfo2.status) {
  1.1563 +        return -1;
  1.1564 +      } else if (aSelectorInfo2.status > aSelectorInfo1.status) {
  1.1565 +        return 1;
  1.1566 +      } else {
  1.1567 +        return aSelectorInfo1.compareTo(aSelectorInfo2);
  1.1568 +      }
  1.1569 +    });
  1.1570 +
  1.1571 +    // Now we know which of the matches is best, we can mark it BEST_MATCH.
  1.1572 +    if (this._matchedSelectors.length > 0 &&
  1.1573 +        this._matchedSelectors[0].status > CssLogic.STATUS.UNMATCHED) {
  1.1574 +      this._matchedSelectors[0].status = CssLogic.STATUS.BEST;
  1.1575 +    }
  1.1576 +  },
  1.1577 +
  1.1578 +  /**
  1.1579 +   * Process a matched CssSelector object.
  1.1580 +   *
  1.1581 +   * @private
  1.1582 +   * @param {CssSelector} aSelector the matched CssSelector object.
  1.1583 +   * @param {CssLogic.STATUS} aStatus the CssSelector match status.
  1.1584 +   */
  1.1585 +  _processMatchedSelector: function CssPropertyInfo_processMatchedSelector(aSelector, aStatus)
  1.1586 +  {
  1.1587 +    let cssRule = aSelector.cssRule;
  1.1588 +    let value = cssRule.getPropertyValue(this.property);
  1.1589 +    if (value &&
  1.1590 +        (aStatus == CssLogic.STATUS.MATCHED ||
  1.1591 +         (aStatus == CssLogic.STATUS.PARENT_MATCH &&
  1.1592 +          domUtils.isInheritedProperty(this.property)))) {
  1.1593 +      let selectorInfo = new CssSelectorInfo(aSelector, this.property, value,
  1.1594 +          aStatus);
  1.1595 +      this._matchedSelectors.push(selectorInfo);
  1.1596 +      if (this._cssLogic._passId !== cssRule._passId && cssRule.sheetAllowed) {
  1.1597 +        this._matchedRuleCount++;
  1.1598 +      }
  1.1599 +    }
  1.1600 +  },
  1.1601 +
  1.1602 +  /**
  1.1603 +   * Refilter the matched selectors array when the CssLogic.sourceFilter
  1.1604 +   * changes. This allows for quick filter changes.
  1.1605 +   * @private
  1.1606 +   */
  1.1607 +  _refilterSelectors: function CssPropertyInfo_refilterSelectors()
  1.1608 +  {
  1.1609 +    let passId = ++this._cssLogic._passId;
  1.1610 +    let ruleCount = 0;
  1.1611 +
  1.1612 +    let iterator = function(aSelectorInfo) {
  1.1613 +      let cssRule = aSelectorInfo.selector.cssRule;
  1.1614 +      if (cssRule._passId != passId) {
  1.1615 +        if (cssRule.sheetAllowed) {
  1.1616 +          ruleCount++;
  1.1617 +        }
  1.1618 +        cssRule._passId = passId;
  1.1619 +      }
  1.1620 +    };
  1.1621 +
  1.1622 +    if (this._matchedSelectors) {
  1.1623 +      this._matchedSelectors.forEach(iterator);
  1.1624 +      this._matchedRuleCount = ruleCount;
  1.1625 +    }
  1.1626 +
  1.1627 +    this.needRefilter = false;
  1.1628 +  },
  1.1629 +
  1.1630 +  toString: function CssPropertyInfo_toString()
  1.1631 +  {
  1.1632 +    return "CssPropertyInfo[" + this.property + "]";
  1.1633 +  },
  1.1634 +};
  1.1635 +
  1.1636 +/**
  1.1637 + * A class that holds information about a given CssSelector object.
  1.1638 + *
  1.1639 + * Instances of this class are given to CssHtmlTree in the array of matched
  1.1640 + * selectors. Each such object represents a displayable row in the PropertyView
  1.1641 + * objects. The information given by this object blends data coming from the
  1.1642 + * CssSheet, CssRule and from the CssSelector that own this object.
  1.1643 + *
  1.1644 + * @param {CssSelector} aSelector The CssSelector object for which to present information.
  1.1645 + * @param {string} aProperty The property for which information should be retrieved.
  1.1646 + * @param {string} aValue The property value from the CssRule that owns the selector.
  1.1647 + * @param {CssLogic.STATUS} aStatus The selector match status.
  1.1648 + * @constructor
  1.1649 + */
  1.1650 +function CssSelectorInfo(aSelector, aProperty, aValue, aStatus)
  1.1651 +{
  1.1652 +  this.selector = aSelector;
  1.1653 +  this.property = aProperty;
  1.1654 +  this.status = aStatus;
  1.1655 +  this.value = aValue;
  1.1656 +  let priority = this.selector.cssRule.getPropertyPriority(this.property);
  1.1657 +  this.important = (priority === "important");
  1.1658 +}
  1.1659 +
  1.1660 +CssSelectorInfo.prototype = {
  1.1661 +  /**
  1.1662 +   * Retrieve the CssSelector source, which is the source of the CssSheet owning
  1.1663 +   * the selector.
  1.1664 +   *
  1.1665 +   * @return {string} the selector source.
  1.1666 +   */
  1.1667 +  get source()
  1.1668 +  {
  1.1669 +    return this.selector.source;
  1.1670 +  },
  1.1671 +
  1.1672 +  /**
  1.1673 +   * Retrieve the CssSelector source element, which is the source of the CssRule
  1.1674 +   * owning the selector. This is only available when the CssSelector comes from
  1.1675 +   * an element.style.
  1.1676 +   *
  1.1677 +   * @return {string} the source element selector.
  1.1678 +   */
  1.1679 +  get sourceElement()
  1.1680 +  {
  1.1681 +    return this.selector.sourceElement;
  1.1682 +  },
  1.1683 +
  1.1684 +  /**
  1.1685 +   * Retrieve the address of the CssSelector. This points to the address of the
  1.1686 +   * CssSheet owning this selector.
  1.1687 +   *
  1.1688 +   * @return {string} the address of the CssSelector.
  1.1689 +   */
  1.1690 +  get href()
  1.1691 +  {
  1.1692 +    return this.selector.href;
  1.1693 +  },
  1.1694 +
  1.1695 +  /**
  1.1696 +   * Check if the CssSelector comes from element.style or not.
  1.1697 +   *
  1.1698 +   * @return {boolean} true if the CssSelector comes from element.style, or
  1.1699 +   * false otherwise.
  1.1700 +   */
  1.1701 +  get elementStyle()
  1.1702 +  {
  1.1703 +    return this.selector.elementStyle;
  1.1704 +  },
  1.1705 +
  1.1706 +  /**
  1.1707 +   * Retrieve specificity information for the current selector.
  1.1708 +   *
  1.1709 +   * @return {object} an object holding specificity information for the current
  1.1710 +   * selector.
  1.1711 +   */
  1.1712 +  get specificity()
  1.1713 +  {
  1.1714 +    return this.selector.specificity;
  1.1715 +  },
  1.1716 +
  1.1717 +  /**
  1.1718 +   * Retrieve the parent stylesheet index/position in the viewed document.
  1.1719 +   *
  1.1720 +   * @return {number} the parent stylesheet index/position in the viewed
  1.1721 +   * document.
  1.1722 +   */
  1.1723 +  get sheetIndex()
  1.1724 +  {
  1.1725 +    return this.selector.sheetIndex;
  1.1726 +  },
  1.1727 +
  1.1728 +  /**
  1.1729 +   * Check if the parent stylesheet is allowed by the CssLogic.sourceFilter.
  1.1730 +   *
  1.1731 +   * @return {boolean} true if the parent stylesheet is allowed by the current
  1.1732 +   * sourceFilter, or false otherwise.
  1.1733 +   */
  1.1734 +  get sheetAllowed()
  1.1735 +  {
  1.1736 +    return this.selector.sheetAllowed;
  1.1737 +  },
  1.1738 +
  1.1739 +  /**
  1.1740 +   * Retrieve the line of the parent CSSStyleRule in the parent CSSStyleSheet.
  1.1741 +   *
  1.1742 +   * @return {number} the line of the parent CSSStyleRule in the parent
  1.1743 +   * stylesheet.
  1.1744 +   */
  1.1745 +  get ruleLine()
  1.1746 +  {
  1.1747 +    return this.selector.ruleLine;
  1.1748 +  },
  1.1749 +
  1.1750 +  /**
  1.1751 +   * Check if the selector comes from a browser-provided stylesheet.
  1.1752 +   *
  1.1753 +   * @return {boolean} true if the selector comes from a browser-provided
  1.1754 +   * stylesheet, or false otherwise.
  1.1755 +   */
  1.1756 +  get contentRule()
  1.1757 +  {
  1.1758 +    return this.selector.contentRule;
  1.1759 +  },
  1.1760 +
  1.1761 +  /**
  1.1762 +   * Compare the current CssSelectorInfo instance to another instance, based on
  1.1763 +   * specificity information.
  1.1764 +   *
  1.1765 +   * @param {CssSelectorInfo} aThat The instance to compare ourselves against.
  1.1766 +   * @return number -1, 0, 1 depending on how aThat compares with this.
  1.1767 +   */
  1.1768 +  compareTo: function CssSelectorInfo_compareTo(aThat)
  1.1769 +  {
  1.1770 +    if (!this.contentRule && aThat.contentRule) return 1;
  1.1771 +    if (this.contentRule && !aThat.contentRule) return -1;
  1.1772 +
  1.1773 +    if (this.elementStyle && !aThat.elementStyle) {
  1.1774 +      if (!this.important && aThat.important) return 1;
  1.1775 +      else return -1;
  1.1776 +    }
  1.1777 +
  1.1778 +    if (!this.elementStyle && aThat.elementStyle) {
  1.1779 +      if (this.important && !aThat.important) return -1;
  1.1780 +      else return 1;
  1.1781 +    }
  1.1782 +
  1.1783 +    if (this.important && !aThat.important) return -1;
  1.1784 +    if (aThat.important && !this.important) return 1;
  1.1785 +
  1.1786 +    if (this.specificity > aThat.specificity) return -1;
  1.1787 +    if (aThat.specificity > this.specificity) return 1;
  1.1788 +
  1.1789 +    if (this.sheetIndex > aThat.sheetIndex) return -1;
  1.1790 +    if (aThat.sheetIndex > this.sheetIndex) return 1;
  1.1791 +
  1.1792 +    if (this.ruleLine > aThat.ruleLine) return -1;
  1.1793 +    if (aThat.ruleLine > this.ruleLine) return 1;
  1.1794 +
  1.1795 +    return 0;
  1.1796 +  },
  1.1797 +
  1.1798 +  toString: function CssSelectorInfo_toString()
  1.1799 +  {
  1.1800 +    return this.selector + " -> " + this.value;
  1.1801 +  },
  1.1802 +};
  1.1803 +
  1.1804 +XPCOMUtils.defineLazyGetter(this, "domUtils", function() {
  1.1805 +  return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
  1.1806 +});

mercurial