Explorar o código

test validity of selectors

Gildas %!s(int64=7) %!d(string=hai) anos
pai
achega
31e65b426c
Modificáronse 1 ficheiros con 30 adicións e 10 borrados
  1. 30 10
      lib/single-file/css-matched-rules.js

+ 30 - 10
lib/single-file/css-matched-rules.js

@@ -24,6 +24,7 @@ this.matchedRules = this.matchedRules || (() => {
 
 	const MEDIA_ALL = "all";
 	const IGNORED_PSEUDO_ELEMENTS = ["after", "before", "first-letter", "first-line", "selection"];
+	const REGEXP_VENDOR_PSEUDO = /:-(ms|webkit|moz|o)-/;
 	const DEBUG = false;
 
 	class MatchedRules {
@@ -32,16 +33,19 @@ this.matchedRules = this.matchedRules || (() => {
 			this.mediaAllInfo = createMediaInfo(MEDIA_ALL);
 			const matchedElementsCache = new Map();
 			let sheetIndex = 0;
+			const styleElement = doc.createElement("style");
+			doc.body.appendChild(styleElement);
 			docStyle.stylesheets.forEach(stylesheetInfo => {
 				if (stylesheetInfo.mediaText && stylesheetInfo.mediaText != MEDIA_ALL) {
 					const mediaInfo = createMediaInfo(stylesheetInfo.mediaText);
 					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 {
-					getMatchedElementsRules(doc, stylesheetInfo.stylesheet.children, this.mediaAllInfo, sheetIndex, docStyle, matchedElementsCache);
+					getMatchedElementsRules(doc, stylesheetInfo.stylesheet.children, this.mediaAllInfo, sheetIndex, docStyle, matchedElementsCache, styleElement.sheet);
 				}
 				sheetIndex++;
 			});
+			styleElement.remove();
 			let startTime;
 			if (DEBUG) {
 				startTime = Date.now();
@@ -78,7 +82,7 @@ this.matchedRules = this.matchedRules || (() => {
 		return mediaInfo;
 	}
 
-	function getMatchedElementsRules(doc, cssRules, mediaInfo, sheetIndex, docStyle, matchedElementsCache) {
+	function getMatchedElementsRules(doc, cssRules, mediaInfo, sheetIndex, docStyle, matchedElementsCache, stylesheet) {
 		let mediaIndex = 0;
 		let ruleIndex = 0;
 		let startTime;
@@ -93,17 +97,19 @@ this.matchedRules = this.matchedRules || (() => {
 					const ruleMediaInfo = createMediaInfo(mediaText);
 					mediaInfo.medias.set("rule-" + sheetIndex + "-" + mediaIndex + "-" + mediaText, ruleMediaInfo);
 					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) {
 					const selectors = cssRule.prelude.children.toArray();
 					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 };
-					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) {
 		let selectorText;
 		const selectorData = cssTree.parse(cssTree.generate(selectorInfo.selector.data), { context: "selector" });
@@ -153,6 +172,7 @@ this.matchedRules = this.matchedRules || (() => {
 
 	function getFilteredSelector(selector) {
 		const removedSelectors = [];
+		selector = { data: cssTree.parse(cssTree.generate(selector.data), { context: "selector" }) };
 		filterPseudoClasses(selector);
 		if (removedSelectors.length) {
 			removedSelectors.forEach(({ parentSelector, selector }) => {
@@ -212,7 +232,7 @@ this.matchedRules = this.matchedRules || (() => {
 	}
 
 	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) {
 				let info;
 				if (elementStyleInfo.selectorInfo.ruleInfo) {