|
@@ -24,6 +24,7 @@ this.matchedRules = this.matchedRules || (() => {
|
|
|
|
|
|
|
|
const MEDIA_ALL = "all";
|
|
const MEDIA_ALL = "all";
|
|
|
const IGNORED_PSEUDO_ELEMENTS = ["after", "before", "first-letter", "first-line", "selection"];
|
|
const IGNORED_PSEUDO_ELEMENTS = ["after", "before", "first-letter", "first-line", "selection"];
|
|
|
|
|
+ const REGEXP_VENDOR_PSEUDO = /:-(ms|webkit|moz|o)-/;
|
|
|
const DEBUG = false;
|
|
const DEBUG = false;
|
|
|
|
|
|
|
|
class MatchedRules {
|
|
class MatchedRules {
|
|
@@ -32,16 +33,19 @@ this.matchedRules = this.matchedRules || (() => {
|
|
|
this.mediaAllInfo = createMediaInfo(MEDIA_ALL);
|
|
this.mediaAllInfo = createMediaInfo(MEDIA_ALL);
|
|
|
const matchedElementsCache = new Map();
|
|
const matchedElementsCache = new Map();
|
|
|
let sheetIndex = 0;
|
|
let sheetIndex = 0;
|
|
|
|
|
+ const styleElement = doc.createElement("style");
|
|
|
|
|
+ doc.body.appendChild(styleElement);
|
|
|
docStyle.stylesheets.forEach(stylesheetInfo => {
|
|
docStyle.stylesheets.forEach(stylesheetInfo => {
|
|
|
if (stylesheetInfo.mediaText && stylesheetInfo.mediaText != MEDIA_ALL) {
|
|
if (stylesheetInfo.mediaText && stylesheetInfo.mediaText != MEDIA_ALL) {
|
|
|
const mediaInfo = createMediaInfo(stylesheetInfo.mediaText);
|
|
const mediaInfo = createMediaInfo(stylesheetInfo.mediaText);
|
|
|
this.mediaAllInfo.medias.set("style-" + sheetIndex + "-" + stylesheetInfo.mediaText, mediaInfo);
|
|
this.mediaAllInfo.medias.set("style-" + sheetIndex + "-" + stylesheetInfo.mediaText, mediaInfo);
|
|
|
- getMatchedElementsRules(doc, stylesheetInfo.stylesheet.children, mediaInfo, sheetIndex, docStyle, matchedElementsCache);
|
|
|
|
|
|
|
+ getMatchedElementsRules(doc, stylesheetInfo.stylesheet.children, mediaInfo, sheetIndex, docStyle, matchedElementsCache, styleElement.sheet);
|
|
|
} else {
|
|
} else {
|
|
|
- getMatchedElementsRules(doc, stylesheetInfo.stylesheet.children, this.mediaAllInfo, sheetIndex, docStyle, matchedElementsCache);
|
|
|
|
|
|
|
+ getMatchedElementsRules(doc, stylesheetInfo.stylesheet.children, this.mediaAllInfo, sheetIndex, docStyle, matchedElementsCache, styleElement.sheet);
|
|
|
}
|
|
}
|
|
|
sheetIndex++;
|
|
sheetIndex++;
|
|
|
});
|
|
});
|
|
|
|
|
+ styleElement.remove();
|
|
|
let startTime;
|
|
let startTime;
|
|
|
if (DEBUG) {
|
|
if (DEBUG) {
|
|
|
startTime = Date.now();
|
|
startTime = Date.now();
|
|
@@ -78,7 +82,7 @@ this.matchedRules = this.matchedRules || (() => {
|
|
|
return mediaInfo;
|
|
return mediaInfo;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- function getMatchedElementsRules(doc, cssRules, mediaInfo, sheetIndex, docStyle, matchedElementsCache) {
|
|
|
|
|
|
|
+ function getMatchedElementsRules(doc, cssRules, mediaInfo, sheetIndex, docStyle, matchedElementsCache, stylesheet) {
|
|
|
let mediaIndex = 0;
|
|
let mediaIndex = 0;
|
|
|
let ruleIndex = 0;
|
|
let ruleIndex = 0;
|
|
|
let startTime;
|
|
let startTime;
|
|
@@ -93,17 +97,19 @@ this.matchedRules = this.matchedRules || (() => {
|
|
|
const ruleMediaInfo = createMediaInfo(mediaText);
|
|
const ruleMediaInfo = createMediaInfo(mediaText);
|
|
|
mediaInfo.medias.set("rule-" + sheetIndex + "-" + mediaIndex + "-" + mediaText, ruleMediaInfo);
|
|
mediaInfo.medias.set("rule-" + sheetIndex + "-" + mediaIndex + "-" + mediaText, ruleMediaInfo);
|
|
|
mediaIndex++;
|
|
mediaIndex++;
|
|
|
- getMatchedElementsRules(doc, cssRule.block.children, ruleMediaInfo, sheetIndex, docStyle, matchedElementsCache);
|
|
|
|
|
|
|
+ getMatchedElementsRules(doc, cssRule.block.children, ruleMediaInfo, sheetIndex, docStyle, matchedElementsCache, stylesheet);
|
|
|
} else if (cssRule.type == "Rule" && cssRule.prelude.children) {
|
|
} else if (cssRule.type == "Rule" && cssRule.prelude.children) {
|
|
|
const selectors = cssRule.prelude.children.toArray();
|
|
const selectors = cssRule.prelude.children.toArray();
|
|
|
const selectorsText = cssRule.prelude.children.toArray().map(selector => cssTree.generate(selector));
|
|
const selectorsText = cssRule.prelude.children.toArray().map(selector => cssTree.generate(selector));
|
|
|
const ruleInfo = { cssRule, mediaInfo, ruleIndex, sheetIndex, matchedSelectors: new Set(), style: new Map(), selectors, selectorsText };
|
|
const ruleInfo = { cssRule, mediaInfo, ruleIndex, sheetIndex, matchedSelectors: new Set(), style: new Map(), selectors, selectorsText };
|
|
|
- ruleIndex++;
|
|
|
|
|
- for (let selector = cssRule.prelude.children.head, selectorIndex = 0; selector; selector = selector.next, selectorIndex++) {
|
|
|
|
|
- const selectorText = selectorsText[selectorIndex];
|
|
|
|
|
- const selectorInfo = { selector, selectorText, ruleInfo };
|
|
|
|
|
- getMatchedElementsSelector(doc, selectorInfo, docStyle, matchedElementsCache);
|
|
|
|
|
|
|
+ if (!invalidSelector(selectorsText.join(","), stylesheet)) {
|
|
|
|
|
+ for (let selector = cssRule.prelude.children.head, selectorIndex = 0; selector; selector = selector.next, selectorIndex++) {
|
|
|
|
|
+ const selectorText = selectorsText[selectorIndex];
|
|
|
|
|
+ const selectorInfo = { selector, selectorText, ruleInfo };
|
|
|
|
|
+ getMatchedElementsSelector(doc, selectorInfo, docStyle, matchedElementsCache);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
+ ruleIndex++;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
@@ -112,6 +118,19 @@ this.matchedRules = this.matchedRules || (() => {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ function invalidSelector(selectorText, stylesheet) {
|
|
|
|
|
+ let invalidSelector;
|
|
|
|
|
+ try {
|
|
|
|
|
+ stylesheet.insertRule(selectorText + "{}");
|
|
|
|
|
+ stylesheet.deleteRule(0);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ if (!selectorText.match(REGEXP_VENDOR_PSEUDO)) {
|
|
|
|
|
+ invalidSelector = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return invalidSelector;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
function getMatchedElementsSelector(doc, selectorInfo, docStyle, matchedElementsCache) {
|
|
function getMatchedElementsSelector(doc, selectorInfo, docStyle, matchedElementsCache) {
|
|
|
let selectorText;
|
|
let selectorText;
|
|
|
const selectorData = cssTree.parse(cssTree.generate(selectorInfo.selector.data), { context: "selector" });
|
|
const selectorData = cssTree.parse(cssTree.generate(selectorInfo.selector.data), { context: "selector" });
|
|
@@ -153,6 +172,7 @@ this.matchedRules = this.matchedRules || (() => {
|
|
|
|
|
|
|
|
function getFilteredSelector(selector) {
|
|
function getFilteredSelector(selector) {
|
|
|
const removedSelectors = [];
|
|
const removedSelectors = [];
|
|
|
|
|
+ selector = { data: cssTree.parse(cssTree.generate(selector.data), { context: "selector" }) };
|
|
|
filterPseudoClasses(selector);
|
|
filterPseudoClasses(selector);
|
|
|
if (removedSelectors.length) {
|
|
if (removedSelectors.length) {
|
|
|
removedSelectors.forEach(({ parentSelector, selector }) => {
|
|
removedSelectors.forEach(({ parentSelector, selector }) => {
|
|
@@ -212,7 +232,7 @@ this.matchedRules = this.matchedRules || (() => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function computeCascade(mediaInfo, parentMediaInfo, mediaAllInfo) {
|
|
function computeCascade(mediaInfo, parentMediaInfo, mediaAllInfo) {
|
|
|
- mediaInfo.elements.forEach((elementInfo, element) => getStylesInfo(elementInfo, element).forEach((elementStyleInfo, styleName) => {
|
|
|
|
|
|
|
+ mediaInfo.elements.forEach((elementInfo, element) => getStylesInfo(elementInfo, element).forEach((elementStyleInfo, styleName) => {
|
|
|
if (elementStyleInfo.selectorInfo.ruleInfo || mediaInfo == mediaAllInfo) {
|
|
if (elementStyleInfo.selectorInfo.ruleInfo || mediaInfo == mediaAllInfo) {
|
|
|
let info;
|
|
let info;
|
|
|
if (elementStyleInfo.selectorInfo.ruleInfo) {
|
|
if (elementStyleInfo.selectorInfo.ruleInfo) {
|