зеркало из https://github.com/mozilla/gecko-dev.git
Bug 692543 - Make the Style Inspector UI faster; r=dcamp
This commit is contained in:
Родитель
5177bed31c
Коммит
4e75493f67
|
@ -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);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче