diff --git a/browser/devtools/styleinspector/CssHtmlTree.jsm b/browser/devtools/styleinspector/CssHtmlTree.jsm index c0aa503ae465..a8a67c49afc1 100644 --- a/browser/devtools/styleinspector/CssHtmlTree.jsm +++ b/browser/devtools/styleinspector/CssHtmlTree.jsm @@ -133,10 +133,14 @@ XPCOMUtils.defineLazyGetter(CssHtmlTree, "_strings", function() Services.strings .createBundle("chrome://browser/locale/devtools/styleinspector.properties")); CssHtmlTree.prototype = { + // Cache the list of properties that have matched and unmatched properties. + _matchedProperties: null, + _unmatchedProperties: null, + htmlComplete: false, // Used for cancelling timeouts in the style filter. - filterChangedTimeout: null, + _filterChangedTimeout: null, // The search filter searchField: null, @@ -167,12 +171,18 @@ CssHtmlTree.prototype = { } this.viewedElement = aElement; + this._unmatchedProperties = null; + this._matchedProperties = null; CssHtmlTree.processTemplate(this.templatePath, this.path, this); if (this.htmlComplete) { this.refreshPanel(); } else { + if (this._panelRefreshTimeout) { + this.win.clearTimeout(this._panelRefreshTimeout); + } + CssHtmlTree.processTemplate(this.templateRoot, this.root, this); // We use a setTimeout loop to display the properties in batches of 15 at a @@ -194,14 +204,17 @@ CssHtmlTree.prototype = { if (i < max) { // There are still some properties to display. We loop here to display // the next batch of 15. - this.win.setTimeout(displayProperties.bind(this), 50); + this._panelRefreshTimeout = + this.win.setTimeout(displayProperties.bind(this), 15); } else { this.htmlComplete = true; + this._panelRefreshTimeout = null; Services.obs.notifyObservers(null, "StyleInspector-populated", null); } } } - this.win.setTimeout(displayProperties.bind(this), 50); + this._panelRefreshTimeout = + this.win.setTimeout(displayProperties.bind(this), 15); } }, @@ -210,7 +223,9 @@ CssHtmlTree.prototype = { */ refreshPanel: function CssHtmlTree_refreshPanel() { - this.win.clearTimeout(this._panelRefreshTimeout); + if (this._panelRefreshTimeout) { + this.win.clearTimeout(this._panelRefreshTimeout); + } // Reset zebra striping. this._darkStripe = true; @@ -228,12 +243,13 @@ CssHtmlTree.prototype = { if (i < max) { // There are still some property views to refresh. We loop here to // display the next batch of 15. - this._panelRefreshTimeout = this.win.setTimeout(refreshView.bind(this), 0); + this._panelRefreshTimeout = this.win.setTimeout(refreshView.bind(this), 15); } else { + this._panelRefreshTimeout = null; Services.obs.notifyObservers(null, "StyleInspector-populated", null); } } - this._panelRefreshTimeout = this.win.setTimeout(refreshView.bind(this), 0); + this._panelRefreshTimeout = this.win.setTimeout(refreshView.bind(this), 15); }, /** @@ -258,13 +274,14 @@ CssHtmlTree.prototype = { filterChanged: function CssHtmlTree_filterChanged(aEvent) { let win = this.styleWin.contentWindow; - if (this.filterChangedTimeout) { - win.clearTimeout(this.filterChangedTimeout); - this.filterChangeTimeout = null; + + if (this._filterChangedTimeout) { + win.clearTimeout(this._filterChangedTimeout); } - this.filterChangedTimeout = win.setTimeout(function() { + this._filterChangedTimeout = win.setTimeout(function() { this.refreshPanel(); + this._filterChangeTimeout = null; }.bind(this), FILTER_CHANGED_TIMEOUT); }, @@ -279,6 +296,7 @@ CssHtmlTree.prototype = { */ onlyUserStylesChanged: function CssHtmltree_onlyUserStylesChanged(aEvent) { + this._matchedProperties = null; this.cssLogic.sourceFilter = this.showOnlyUserStyles ? CssLogic.FILTER.ALL : CssLogic.FILTER.UA; @@ -326,6 +344,56 @@ CssHtmlTree.prototype = { mozProps.sort()); }, + /** + * Get a list of properties that have matched selectors. + * + * @return {object} the object maps property names (keys) to booleans (values) + * that tell if the given property has matched selectors or not. + */ + get matchedProperties() + { + if (!this._matchedProperties) { + this._matchedProperties = + this.cssLogic.hasMatchedSelectors(CssHtmlTree.propertyNames); + } + return this._matchedProperties; + }, + + /** + * Check if a property has unmatched selectors. Result is cached. + * + * @param {string} aProperty the name of the property you want to check. + * @return {boolean} true if the property has unmatched selectors, false + * otherwise. + */ + hasUnmatchedSelectors: function CssHtmlTree_hasUnmatchedSelectors(aProperty) + { + // Initially check all of the properties that return false for + // hasMatchedSelectors(). This speeds-up the UI. + if (!this._unmatchedProperties) { + let properties = []; + CssHtmlTree.propertyNames.forEach(function(aName) { + if (!this.matchedProperties[aName]) { + properties.push(aName); + } + }, this); + + if (properties.indexOf(aProperty) == -1) { + properties.push(aProperty); + } + + this._unmatchedProperties = this.cssLogic.hasUnmatchedSelectors(properties); + } + + // Lazy-get the result for properties we do not have cached. + if (!(aProperty in this._unmatchedProperties)) { + let result = this.cssLogic.hasUnmatchedSelectors([aProperty]); + this._unmatchedProperties[aProperty] = result[aProperty]; + } + + return this._unmatchedProperties[aProperty]; + }, + /** * Destructor for CssHtmlTree. */ @@ -446,7 +514,7 @@ PropertyView.prototype = { */ get hasMatchedSelectors() { - return this.propertyInfo.hasMatchedSelectors(); + return this.tree.matchedProperties[this.name]; }, /** @@ -454,7 +522,7 @@ PropertyView.prototype = { */ get hasUnmatchedSelectors() { - return this.propertyInfo.hasUnmatchedSelectors(); + return this.tree.hasUnmatchedSelectors(this.name); }, /** diff --git a/browser/devtools/styleinspector/CssLogic.jsm b/browser/devtools/styleinspector/CssLogic.jsm index c81c74df237f..278b915b5f5d 100644 --- a/browser/devtools/styleinspector/CssLogic.jsm +++ b/browser/devtools/styleinspector/CssLogic.jsm @@ -151,6 +151,7 @@ CssLogic.prototype = { // in processUnmatchedSelectors(). _matchId: 0, + _matchedRules: null, _matchedSelectors: null, _unmatchedSelectors: null, @@ -166,6 +167,7 @@ CssLogic.prototype = { this._sheetIndex = 0; this._sheets = {}; this._sheetsCached = false; + this._matchedRules = null; this._matchedSelectors = null; this._unmatchedSelectors = null; }, @@ -200,6 +202,7 @@ CssLogic.prototype = { this._propertyInfos = {}; } + this._matchedRules = null; this._matchedSelectors = null; this._unmatchedSelectors = null; let win = this.viewedDocument.defaultView; @@ -242,6 +245,7 @@ CssLogic.prototype = { aValue == CssLogic.FILTER.UA); if (needFullUpdate) { + this._matchedRules = null; this._matchedSelectors = null; this._unmatchedSelectors = null; this._propertyInfos = {}; @@ -289,7 +293,7 @@ CssLogic.prototype = { // styleSheets isn't an array, but forEach can work on it anyway Array.prototype.forEach.call(this.viewedDocument.styleSheets, this._cacheSheet, this); - + this._sheetsCached = true; }, @@ -309,19 +313,19 @@ CssLogic.prototype = { } // Only work with stylesheets that have their media allowed. - if (!CssLogic.sheetMediaAllowed(aDomSheet)) { + if (!this.mediaMatches(aDomSheet)) { return; } // Cache the sheet. - let cssSheet = this.getSheet(aDomSheet, false, this._sheetIndex++); + let cssSheet = this.getSheet(aDomSheet, this._sheetIndex++); if (cssSheet._passId != this._passId) { cssSheet._passId = this._passId; // Find import rules. Array.prototype.forEach.call(aDomSheet.cssRules, function(aDomRule) { if (aDomRule.type == Ci.nsIDOMCSSRule.IMPORT_RULE && aDomRule.styleSheet && - CssLogic.sheetMediaAllowed(aDomRule)) { + this.mediaMatches(aDomRule)) { this._cacheSheet(aDomRule.styleSheet); } }, this); @@ -355,20 +359,18 @@ CssLogic.prototype = { * otherwise the new CSSStyleSheet object is cached. * * @param {CSSStyleSheet} aDomSheet the CSSStyleSheet object you want. - * @param {boolean} aSystemSheet tells if the stylesheet is a browser-provided - * sheet or not. * @param {number} aIndex the index, within the document, of the stylesheet. * * @return {CssSheet} the CssSheet object for the given CSSStyleSheet object. */ - getSheet: function CL_getSheet(aDomSheet, aSystemSheet, aIndex) + getSheet: function CL_getSheet(aDomSheet, aIndex) { - let cacheId = aSystemSheet ? "1" : "0"; + let cacheId = ""; if (aDomSheet.href) { - cacheId += aDomSheet.href; + cacheId = aDomSheet.href; } else if (aDomSheet.ownerNode && aDomSheet.ownerNode.ownerDocument) { - cacheId += aDomSheet.ownerNode.ownerDocument.location; + cacheId = aDomSheet.ownerNode.ownerDocument.location; } let sheet = null; @@ -377,8 +379,10 @@ CssLogic.prototype = { if (cacheId in this._sheets) { for (let i = 0, numSheets = this._sheets[cacheId].length; i < numSheets; i++) { sheet = this._sheets[cacheId][i]; - if (sheet.domSheet == aDomSheet) { - sheet.index = aIndex; + if (sheet.domSheet === aDomSheet) { + if (aIndex != -1) { + sheet.index = aIndex; + } sheetFound = true; break; } @@ -390,8 +394,8 @@ CssLogic.prototype = { this._sheets[cacheId] = []; } - sheet = new CssSheet(this, aDomSheet, aSystemSheet, aIndex); - if (sheet.sheetAllowed && !aSystemSheet) { + sheet = new CssSheet(this, aDomSheet, aIndex); + if (sheet.sheetAllowed && !sheet.systemSheet) { this._ruleCount += sheet.ruleCount; } @@ -486,77 +490,54 @@ CssLogic.prototype = { return; } + if (!this._matchedRules) { + this._buildMatchedRules(); + } + this._matchedSelectors = []; this._unmatchedSelectors = null; this._passId++; - this._matchId++; - let element = this.viewedElement; - let filter = this.sourceFilter; - let sheetIndex = 0; - let domRules = null; - do { - try { - domRules = this.domUtils.getCSSStyleRules(element); - } catch (ex) { - Services.console. - logStringMessage("CssLogic_processMatchedSelectors error: " + ex); - continue; - } + for (let i = 0; i < this._matchedRules.length; i++) { + let rule = this._matchedRules[i][0]; + let status = this._matchedRules[i][1]; - let status = (this.viewedElement == element) ? - CssLogic.STATUS.MATCHED : CssLogic.STATUS.PARENT_MATCH; - - for (let i = 0, numRules = domRules.Count(); i < numRules; i++) { - let domRule = domRules.GetElementAt(i); - if (domRule.type !== Ci.nsIDOMCSSRule.STYLE_RULE) { - continue; - } - - let domSheet = domRule.parentStyleSheet; - let systemSheet = CssLogic.isSystemStyleSheet(domSheet); - if (filter !== CssLogic.FILTER.UA && systemSheet) { - continue; - } - - let sheet = this.getSheet(domSheet, systemSheet, sheetIndex); - let rule = sheet.getRule(domRule); - - rule.selectors.forEach(function (aSelector) { - if (aSelector._matchId !== this._matchId && - element.mozMatchesSelector(aSelector)) { - aSelector._matchId = this._matchId; - this._matchedSelectors.push([ aSelector, status ]); - if (aCallback) { - aCallback.call(aScope, aSelector, status); - } + rule.selectors.forEach(function (aSelector) { + if (aSelector._matchId !== this._matchId && + (aSelector.elementStyle || + this._selectorMatchesElement(aSelector))) { + aSelector._matchId = this._matchId; + this._matchedSelectors.push([ aSelector, status ]); + if (aCallback) { + aCallback.call(aScope, aSelector, status); } - }, this); - - if (sheet._passId !== this._passId) { - sheetIndex++; - sheet._passId = this._passId; } + }, this); - if (rule._passId !== this._passId) { - rule._passId = this._passId; - } - } + rule._passId = this._passId; + } + }, - // Add element.style information. - if (element.style.length > 0) { - let rule = new CssRule(null, { style: element.style }, element); - let selector = rule.selectors[0]; - selector._matchId = this._matchId; - - this._matchedSelectors.push([ selector, status ]); - if (aCallback) { - aCallback.call(aScope, selector, status); - } - rule._passId = this._passId; + /** + * Check if the given selector matches the highlighted element or any of its + * parents. + * + * @private + * @param {string} aSelector the selector string you want to check. + * @return {boolean} true if the given selector matches the highlighted + * element or any of its parents, otherwise false is returned. + */ + _selectorMatchesElement: function CL__selectorMatchesElement(aSelector) + { + let element = this.viewedElement; + do { + if (element.mozMatchesSelector(aSelector)) { + return true; } } while ((element = element.parentNode) && - element.nodeType === Ci.nsIDOMNode.ELEMENT_NODE); + element.nodeType === Ci.nsIDOMNode.ELEMENT_NODE); + + return false; }, /** @@ -573,9 +554,6 @@ CssLogic.prototype = { */ processUnmatchedSelectors: function CL_processUnmatchedSelectors(aCallback, aScope) { - if (!this._matchedSelectors) { - this.processMatchedSelectors(); - } if (this._unmatchedSelectors) { if (aCallback) { this._unmatchedSelectors.forEach(aCallback, aScope); @@ -583,17 +561,21 @@ CssLogic.prototype = { return; } + if (!this._matchedSelectors) { + this.processMatchedSelectors(); + } + this._unmatchedSelectors = []; this.forEachSheet(function (aSheet) { // We do not show unmatched selectors from system stylesheets - if (aSheet.systemSheet) { + if (aSheet.systemSheet || aSheet.disabled || !aSheet.mediaMatches) { return; } aSheet.forEachRule(function (aRule) { aRule.selectors.forEach(function (aSelector) { - if (aSelector._matchId != this._matchId) { + if (aSelector._matchId !== this._matchId) { this._unmatchedSelectors.push(aSelector); if (aCallback) { aCallback.call(aScope, aSelector); @@ -607,88 +589,205 @@ CssLogic.prototype = { /** * Check if the highlighted element or it's parents have matched selectors. * - * @param {function} [aCallback] Simple callback method. If aCallback is - * provided then the domRules for each element in the loop are passed to - * the callback function. When the element has .style properties, the callback - * receives {style: element.style}. If the callback returns true then the - * element has matched rules, otherwise not. - * @return {Boolean} true if the current element or it's parents have - * matching CssSelector objects, false otherwise + * @param {array} aProperties The list of properties you want to check if they + * have matched selectors or not. + * @return {object} An object that tells for each property if it has matched + * selectors or not. Object keys are property names and values are booleans. */ - hasMatchedSelectors: function CL_hasMatchedSelectors(aCallback) + hasMatchedSelectors: function CL_hasMatchedSelectors(aProperties) + { + if (!this._matchedRules) { + this._buildMatchedRules(); + } + + let result = {}; + + this._matchedRules.some(function(aValue) { + let rule = aValue[0]; + aProperties = aProperties.filter(function(aProperty) { + if (rule.getPropertyValue(aProperty)) { + // We just need to find if a rule has this property while it matches + // the viewedElement (or its parents). + result[aProperty] = true; + return false; + } + return true; // Keep the property for the next rule. + }); + return aProperties.length == 0; + }, this); + + return result; + }, + + /** + * Build the array of matched rules for the currently highlighted element. + * The array will hold rules that match the viewedElement and its parents. + * + * @private + */ + _buildMatchedRules: function CL__buildMatchedRules() { let domRules; let element = this.viewedElement; - let matched = false; + let filter = this.sourceFilter; + let sheetIndex = 0; + + this._matchId++; + this._passId++; + this._matchedRules = []; do { + let status = this.viewedElement === element ? + CssLogic.STATUS.MATCHED : CssLogic.STATUS.PARENT_MATCH; + try { domRules = this.domUtils.getCSSStyleRules(element); } catch (ex) { Services.console. - logStringMessage("CssLogic_hasMatchedSelectors error: " + ex); + logStringMessage("CL__buildMatchedRules error: " + ex); continue; } - // Check if the are DOM rules that we can consider as matched rules - // (depending on the callback). - if (domRules.Count() && (!aCallback || aCallback(domRules))) { - matched = true; + for (let i = 0, n = domRules.Count(); i < n; i++) { + let domRule = domRules.GetElementAt(i); + if (domRule.type !== Ci.nsIDOMCSSRule.STYLE_RULE) { + continue; + } + + let sheet = this.getSheet(domRule.parentStyleSheet, -1); + if (sheet._passId !== this._passId) { + sheet.index = sheetIndex++; + sheet._passId = this._passId; + } + + if (filter !== CssLogic.FILTER.UA && sheet.systemSheet) { + continue; + } + + let rule = sheet.getRule(domRule); + if (rule._passId === this._passId) { + continue; + } + + rule._matchId = this._matchId; + rule._passId = this._passId; + this._matchedRules.push([rule, status]); } - // Check if the element has any element.style properties that we can - // consider as "matched" (depending on the callback). - if (element.style.length > 0 && - (!aCallback || aCallback({style: element.style}))) { - matched = true; - } - if (matched) { - break; + // Add element.style information. + if (element.style.length > 0) { + let rule = new CssRule(null, { style: element.style }, element); + rule._matchId = this._matchId; + rule._passId = this._passId; + this._matchedRules.push([rule, status]); } } while ((element = element.parentNode) && - element.nodeType === Ci.nsIDOMNode.ELEMENT_NODE); - - return matched; + element.nodeType === Ci.nsIDOMNode.ELEMENT_NODE); }, /** * Check if the highlighted element or it's parents have unmatched selectors. * - * @param {String} aProperty The CSS property to check against - * @return {Boolean} true if the current element or it's parents have - * unmatched CssSelector objects, false otherwise + * Please note that this method is far slower than hasMatchedSelectors() + * because it needs to do a lot more checks in the DOM. + * + * @param {array} aProperties The list of properties you want to check if they + * have unmatched selectors or not. + * @return {object} An object that tells for each property if it has unmatched + * selectors or not. Object keys are property names and values are booleans. */ - hasUnmatchedSelectors: function CL_hasUnmatchedSelectors(aProperty) + hasUnmatchedSelectors: function CL_hasUnmatchedSelectors(aProperties) { - return this.forSomeSheets(function (aSheet) { - // We do not show unmatched selectors from system stylesheets - if (aSheet.systemSheet) { + if (!this._matchedRules) { + this._buildMatchedRules(); + } + + let result = {}; + + this.forSomeSheets(function (aSheet) { + if (aSheet.systemSheet || aSheet.disabled || !aSheet.mediaMatches) { return false; } return aSheet.forSomeRules(function (aRule) { - if (aRule.getPropertyValue(aProperty)) { - let element = this.viewedElement; - let selectorText = aRule._domRule.selectorText; - let matches = false; + let unmatched = aRule._matchId !== this._matchId || + this._ruleHasUnmatchedSelector(aRule); + if (!unmatched) { + return false; + } - do { - if (element.mozMatchesSelector(selectorText)) { - matches = true; - break; - } - } while ((element = element.parentNode) && - element.nodeType === Ci.nsIDOMNode.ELEMENT_NODE); - - if (!matches) { - // Now we know that there are rules but none match. + aProperties = aProperties.filter(function(aProperty) { + if (!aRule.getPropertyValue(aProperty)) { + // Keep this property for the next rule. We need to find a rule + // which has the property. return true; } - } + + result[aProperty] = true; + + // We found a rule that has the current property while it does not + // match the current element. We can remove this property from the + // array. + return false; + }); + + return aProperties.length == 0; }, this); }, this); + + aProperties.forEach(function(aProperty) { result[aProperty] = false; }); + + return result; }, + + /** + * Check if a CssRule has an unmatched selector for the highlighted element or + * its parents. + * + * @private + * @param {CssRule} aRule The rule you want to check if it has an unmatched + * selector. + * @return {boolean} True if the rule has an unmatched selector, false + * otherwise. + */ + _ruleHasUnmatchedSelector: function CL__ruleHasUnmatchedSelector(aRule) + { + if (!aRule._cssSheet && aRule.sourceElement) { + // CssRule wraps element.style, which never has unmatched selectors. + return false; + } + + let element = this.viewedElement; + let selectors = aRule.selectors; + + do { + selectors = selectors.filter(function(aSelector) { + return !element.mozMatchesSelector(aSelector); + }); + + if (selectors.length == 0) { + break; + } + } while ((element = element.parentNode) && + element.nodeType === Ci.nsIDOMNode.ELEMENT_NODE); + + return selectors.length > 0; + }, + + /** + * Tells if the given DOM CSS object matches the current view media. + * + * @param {object} aDomObject The DOM CSS object to check. + * @return {boolean} True if the DOM CSS object matches the current view + * media, or false otherwise. + */ + mediaMatches: function CL_mediaMatches(aDomObject) + { + let mediaText = aDomObject.media.mediaText; + return !mediaText || this.viewedDocument.defaultView. + matchMedia(mediaText).matches; + }, }; /** @@ -789,37 +888,6 @@ CssLogic.isSystemStyleSheet = function CssLogic_isSystemStyleSheet(aSheet) return false; }; -/** - * Check if the given DOM CSS object holds an allowed media. Currently we only - * allow media screen or all. - * - * @param {CSSStyleSheet|CSSImportRule|CSSMediaRule} aDomObject the - * DOM object you want checked. - * @return {boolean} true if the media description is allowed, or false - * otherwise. - */ -CssLogic.sheetMediaAllowed = function CssLogic_sheetMediaAllowed(aDomObject) -{ - let result = false; - let media = aDomObject.media; - - if (media.length > 0) { - let mediaItem = null; - for (let m = 0, mediaLen = media.length; m < mediaLen; m++) { - mediaItem = media.item(m).toLowerCase(); - if (mediaItem === CssLogic.MEDIA.SCREEN || - mediaItem === CssLogic.MEDIA.ALL) { - result = true; - break; - } - } - } else { - result = true; - } - - return result; -}; - /** * Return a shortened version of a style sheet's source. * @@ -827,27 +895,33 @@ CssLogic.sheetMediaAllowed = function CssLogic_sheetMediaAllowed(aDomObject) */ CssLogic.shortSource = function CssLogic_shortSource(aSheet) { - // Use a string like "inline" if there is no source href - if (!aSheet || !aSheet.href) { - return CssLogic.l10n("rule.sourceInline"); - } + // Use a string like "inline" if there is no source href + if (!aSheet || !aSheet.href) { + return CssLogic.l10n("rule.sourceInline"); + } - // We try, in turn, the filename, filePath, query string, whole thing - let url = Services.io.newURI(aSheet.href, null, null); + // We try, in turn, the filename, filePath, query string, whole thing + let url = {}; + try { + url = Services.io.newURI(aSheet.href, null, null); url = url.QueryInterface(Ci.nsIURL); - if (url.fileName) { - return url.fileName; - } + } catch (ex) { + // Some UA-provided stylesheets are not valid URLs. + } - if (url.filePath) { - return url.filePath; - } + if (url.fileName) { + return url.fileName; + } - if (url.query) { - return url.query; - } + if (url.filePath) { + return url.filePath; + } - return this.domSheet.href; + if (url.query) { + return url.query; + } + + return aSheet.href; } /** @@ -857,15 +931,13 @@ CssLogic.shortSource = function CssLogic_shortSource(aSheet) * @param {CssLogic} aCssLogic pointer to the CssLogic instance working with * this CssSheet object. * @param {CSSStyleSheet} aDomSheet reference to a DOM CSSStyleSheet object. - * @param {boolean} aSystemSheet tells if the stylesheet is system-provided. * @param {number} aIndex tells the index/position of the stylesheet within the * main document. */ -function CssSheet(aCssLogic, aDomSheet, aSystemSheet, aIndex) +function CssSheet(aCssLogic, aDomSheet, aIndex) { this._cssLogic = aCssLogic; this.domSheet = aDomSheet; - this.systemSheet = aSystemSheet; this.index = this.systemSheet ? -100 * aIndex : aIndex; // Cache of the sheets href. Cached by the getter. @@ -884,6 +956,44 @@ function CssSheet(aCssLogic, aDomSheet, aSystemSheet, aIndex) CssSheet.prototype = { _passId: null, + _systemSheet: null, + _mediaMatches: null, + + /** + * Tells if the stylesheet is provided by the browser or not. + * + * @return {boolean} true if this is a browser-provided stylesheet, or false + * otherwise. + */ + get systemSheet() + { + if (this._systemSheet === null) { + this._systemSheet = CssLogic.isSystemStyleSheet(this.domSheet); + } + return this._systemSheet; + }, + + /** + * Tells if the stylesheet is disabled or not. + * @return {boolean} true if this stylesheet is disabled, or false otherwise. + */ + get disabled() + { + return this.domSheet.disabled; + }, + + /** + * Tells if the stylesheet matches the current browser view media. + * @return {boolean} true if this stylesheet matches the current browser view + * media, or false otherwise. + */ + get mediaMatches() + { + if (this._mediaMatches === null) { + this._mediaMatches = this._cssLogic.mediaMatches(this.domSheet); + } + return this._mediaMatches; + }, /** * Get a source for a stylesheet, taking into account embedded stylesheets @@ -976,7 +1086,7 @@ CssSheet.prototype = { if (cacheId in this._rules) { for (let i = 0, rulesLen = this._rules[cacheId].length; i < rulesLen; i++) { rule = this._rules[cacheId][i]; - if (rule._domRule == aDomRule) { + if (rule._domRule === aDomRule) { ruleFound = true; break; } @@ -1018,7 +1128,7 @@ CssSheet.prototype = { aCallback.call(aScope, this.getRule(aDomRule)); ruleCount++; } else if (aDomRule.type == Ci.nsIDOMCSSRule.MEDIA_RULE && - aDomRule.cssRules && CssLogic.sheetMediaAllowed(aDomRule)) { + aDomRule.cssRules && this._cssLogic.mediaMatches(aDomRule)) { Array.prototype.forEach.call(aDomRule.cssRules, _iterator, this); } } @@ -1051,7 +1161,7 @@ CssSheet.prototype = { if (aDomRule.type == Ci.nsIDOMCSSRule.STYLE_RULE) { return aCallback.call(aScope, this.getRule(aDomRule)); } else if (aDomRule.type == Ci.nsIDOMCSSRule.MEDIA_RULE && - aDomRule.cssRules && CssLogic.sheetMediaAllowed(aDomRule)) { + aDomRule.cssRules && this._cssLogic.mediaMatches(aDomRule)) { return Array.prototype.some.call(aDomRule.cssRules, _iterator, this); } } @@ -1409,8 +1519,6 @@ function CssPropertyInfo(aCssLogic, aProperty) // that have sheetAllowed = false). this._matchedSelectors = null; this._unmatchedSelectors = null; - this._hasMatchedSelectors = null; - this._hasUnmatchedSelectors = null; } CssPropertyInfo.prototype = { @@ -1508,60 +1616,6 @@ CssPropertyInfo.prototype = { return this._unmatchedSelectors; }, - /** - * Check if the property has any matched selectors. - * - * @return {Boolean} true if the current element or it's parents have - * matching CssSelector objects, false otherwise - */ - hasMatchedSelectors: function CssPropertyInfo_hasMatchedSelectors() - { - if (this._hasMatchedSelectors === null) { - this._hasMatchedSelectors = this._cssLogic.hasMatchedSelectors(function(aDomRules) { - if (!aDomRules.Count) { - // For element.style. - return !!aDomRules.style.getPropertyValue(this.property); - } - - for (let i = 0; i < aDomRules.Count(); i++) { - let domRule = aDomRules.GetElementAt(i); - - if (domRule.type !== Ci.nsIDOMCSSRule.STYLE_RULE) { - continue; - } - - let domSheet = domRule.parentStyleSheet; - let systemSheet = CssLogic.isSystemStyleSheet(domSheet); - let filter = this._cssLogic.sourceFilter; - if (filter !== CssLogic.FILTER.UA && systemSheet) { - continue; - } - - if (domRule.style.getPropertyValue(this.property)) { - return true; - } - } - return false; - }.bind(this)); - } - - return this._hasMatchedSelectors; - }, - - /** - * Check if the property has any matched selectors. - * - * @return {Boolean} true if the current element or it's parents have - * unmatched CssSelector objects, false otherwise - */ - hasUnmatchedSelectors: function CssPropertyInfo_hasUnmatchedSelectors() - { - if (this._hasUnmatchedSelectors === null) { - this._hasUnmatchedSelectors = this._cssLogic.hasUnmatchedSelectors(this.property); - } - return this._hasUnmatchedSelectors; - }, - /** * Find the selectors that match the highlighted element and its parents. * Uses CssLogic.processMatchedSelectors() to find the matched selectors, diff --git a/browser/devtools/styleinspector/test/browser/browser_bug683672.js b/browser/devtools/styleinspector/test/browser/browser_bug683672.js index 583a1537a4e3..01c11c243a07 100644 --- a/browser/devtools/styleinspector/test/browser/browser_bug683672.js +++ b/browser/devtools/styleinspector/test/browser/browser_bug683672.js @@ -65,7 +65,7 @@ function testMatchedSelectors() is(numMatchedSelectors, 6, "CssLogic returns the correct number of matched selectors for div"); - is(propertyView.propertyInfo.hasMatchedSelectors(), true, + is(propertyView.hasMatchedSelectors, true, "hasMatchedSelectors returns true"); } @@ -89,7 +89,7 @@ function testUnmatchedSelectors() is(numUnmatchedSelectors, 13, "CssLogic returns the correct number of unmatched selectors for body"); - is(propertyView.propertyInfo.hasUnmatchedSelectors(), true, + is(propertyView.hasUnmatchedSelectors, true, "hasUnmatchedSelectors returns true"); }