@acemir/cssom 0.9.20 → 0.9.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/CSSOM.js +638 -84
- package/lib/CSSConditionRule.js +7 -2
- package/lib/CSSContainerRule.js +20 -12
- package/lib/CSSImportRule.js +92 -19
- package/lib/CSSMediaRule.js +13 -5
- package/lib/CSSNamespaceRule.js +27 -11
- package/lib/CSSPageRule.js +275 -0
- package/lib/CSSRule.js +6 -0
- package/lib/CSSStyleRule.js +19 -4
- package/lib/CSSStyleSheet.js +31 -7
- package/lib/StyleSheet.js +16 -1
- package/lib/index.js +1 -0
- package/lib/parse.js +152 -24
- package/package.json +1 -1
package/build/CSSOM.js
CHANGED
|
@@ -326,6 +326,12 @@ Object.defineProperties(CSSOM.CSSRule.prototype, {
|
|
|
326
326
|
|
|
327
327
|
constructor: { value: CSSOM.CSSRule },
|
|
328
328
|
|
|
329
|
+
cssRule: {
|
|
330
|
+
value: "",
|
|
331
|
+
configurable: true,
|
|
332
|
+
enumerable: true
|
|
333
|
+
},
|
|
334
|
+
|
|
329
335
|
parentRule: {
|
|
330
336
|
get: function() {
|
|
331
337
|
return this.__parentRule
|
|
@@ -547,12 +553,17 @@ CSSOM.CSSCounterStyleRule.prototype.type = 11;
|
|
|
547
553
|
*/
|
|
548
554
|
CSSOM.CSSConditionRule = function CSSConditionRule() {
|
|
549
555
|
CSSOM.CSSGroupingRule.call(this);
|
|
556
|
+
this.__conditionText = '';
|
|
550
557
|
};
|
|
551
558
|
|
|
552
559
|
CSSOM.CSSConditionRule.prototype = new CSSOM.CSSGroupingRule();
|
|
553
560
|
CSSOM.CSSConditionRule.prototype.constructor = CSSOM.CSSConditionRule;
|
|
554
|
-
|
|
555
|
-
CSSOM.CSSConditionRule.prototype
|
|
561
|
+
|
|
562
|
+
Object.defineProperty(CSSOM.CSSConditionRule.prototype, "conditionText", {
|
|
563
|
+
get: function () {
|
|
564
|
+
return this.__conditionText;
|
|
565
|
+
}
|
|
566
|
+
});
|
|
556
567
|
|
|
557
568
|
|
|
558
569
|
|
|
@@ -583,7 +594,20 @@ Object.defineProperty(CSSOM.CSSStyleRule.prototype, "selectorText", {
|
|
|
583
594
|
return this.__selectorText;
|
|
584
595
|
},
|
|
585
596
|
set: function(value) {
|
|
586
|
-
|
|
597
|
+
if (typeof value === "string") {
|
|
598
|
+
var trimmedValue = value.trim();
|
|
599
|
+
|
|
600
|
+
if (trimmedValue === '') {
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// TODO: Setting invalid selectorText should be ignored
|
|
605
|
+
// There are some validations already on lib/parse.js
|
|
606
|
+
// but the same validations should be applied here.
|
|
607
|
+
// Check if we can move these validation logic to a shared function.
|
|
608
|
+
|
|
609
|
+
this.__selectorText = trimmedValue;
|
|
610
|
+
}
|
|
587
611
|
}
|
|
588
612
|
});
|
|
589
613
|
|
|
@@ -620,9 +644,11 @@ Object.defineProperty(CSSOM.CSSStyleRule.prototype, "cssText", {
|
|
|
620
644
|
return text;
|
|
621
645
|
},
|
|
622
646
|
set: function(cssText) {
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
647
|
+
if (typeof cssText === "string") {
|
|
648
|
+
var rule = CSSOM.CSSStyleRule.parse(cssText);
|
|
649
|
+
this.__style = rule.style;
|
|
650
|
+
this.selectorText = rule.selectorText;
|
|
651
|
+
}
|
|
626
652
|
}
|
|
627
653
|
});
|
|
628
654
|
|
|
@@ -840,7 +866,7 @@ CSSOM.MediaList.prototype = {
|
|
|
840
866
|
*/
|
|
841
867
|
CSSOM.CSSMediaRule = function CSSMediaRule() {
|
|
842
868
|
CSSOM.CSSConditionRule.call(this);
|
|
843
|
-
this.
|
|
869
|
+
this.__media = new CSSOM.MediaList();
|
|
844
870
|
};
|
|
845
871
|
|
|
846
872
|
CSSOM.CSSMediaRule.prototype = new CSSOM.CSSConditionRule();
|
|
@@ -849,16 +875,24 @@ CSSOM.CSSMediaRule.prototype.type = 4;
|
|
|
849
875
|
|
|
850
876
|
// https://opensource.apple.com/source/WebCore/WebCore-7611.1.21.161.3/css/CSSMediaRule.cpp
|
|
851
877
|
Object.defineProperties(CSSOM.CSSMediaRule.prototype, {
|
|
852
|
-
"
|
|
878
|
+
"media": {
|
|
853
879
|
get: function() {
|
|
854
|
-
return this.
|
|
880
|
+
return this.__media;
|
|
855
881
|
},
|
|
856
882
|
set: function(value) {
|
|
857
|
-
|
|
883
|
+
if (typeof value === "string") {
|
|
884
|
+
this.__media.mediaText = value;
|
|
885
|
+
} else {
|
|
886
|
+
this.__media = value;
|
|
887
|
+
}
|
|
858
888
|
},
|
|
859
|
-
configurable: true,
|
|
860
889
|
enumerable: true
|
|
861
890
|
},
|
|
891
|
+
"conditionText": {
|
|
892
|
+
get: function() {
|
|
893
|
+
return this.media.mediaText;
|
|
894
|
+
}
|
|
895
|
+
},
|
|
862
896
|
"cssText": {
|
|
863
897
|
get: function() {
|
|
864
898
|
var cssTexts = [];
|
|
@@ -891,27 +925,35 @@ CSSOM.CSSContainerRule.prototype.constructor = CSSOM.CSSContainerRule;
|
|
|
891
925
|
CSSOM.CSSContainerRule.prototype.type = 17;
|
|
892
926
|
|
|
893
927
|
Object.defineProperties(CSSOM.CSSContainerRule.prototype, {
|
|
894
|
-
"conditionText": {
|
|
895
|
-
get: function() {
|
|
896
|
-
return this.containerText;
|
|
897
|
-
},
|
|
898
|
-
set: function(value) {
|
|
899
|
-
this.containerText = value;
|
|
900
|
-
},
|
|
901
|
-
configurable: true,
|
|
902
|
-
enumerable: true
|
|
903
|
-
},
|
|
904
928
|
"cssText": {
|
|
905
929
|
get: function() {
|
|
906
930
|
var cssTexts = [];
|
|
907
931
|
for (var i=0, length=this.cssRules.length; i < length; i++) {
|
|
908
932
|
cssTexts.push(this.cssRules[i].cssText);
|
|
909
933
|
}
|
|
910
|
-
return "@container " + this.
|
|
934
|
+
return "@container " + this.conditionText + " {" + (cssTexts.length ? "\n " + cssTexts.join("\n ") : "") + "\n}";
|
|
911
935
|
},
|
|
912
936
|
configurable: true,
|
|
913
937
|
enumerable: true
|
|
914
|
-
}
|
|
938
|
+
},
|
|
939
|
+
"containerName": {
|
|
940
|
+
get: function() {
|
|
941
|
+
var parts = this.conditionText.trim().split(/\s+/);
|
|
942
|
+
if (parts.length > 1 && parts[0] !== '(' && !parts[0].startsWith('(')) {
|
|
943
|
+
return parts[0];
|
|
944
|
+
}
|
|
945
|
+
return "";
|
|
946
|
+
}
|
|
947
|
+
},
|
|
948
|
+
"containerQuery": {
|
|
949
|
+
get: function() {
|
|
950
|
+
var parts = this.conditionText.trim().split(/\s+/);
|
|
951
|
+
if (parts.length > 1 && parts[0] !== '(' && !parts[0].startsWith('(')) {
|
|
952
|
+
return parts.slice(1).join(' ');
|
|
953
|
+
}
|
|
954
|
+
return this.conditionText;
|
|
955
|
+
}
|
|
956
|
+
},
|
|
915
957
|
});
|
|
916
958
|
|
|
917
959
|
|
|
@@ -954,21 +996,25 @@ Object.defineProperty(CSSOM.CSSSupportsRule.prototype, "cssText", {
|
|
|
954
996
|
*/
|
|
955
997
|
CSSOM.CSSImportRule = function CSSImportRule() {
|
|
956
998
|
CSSOM.CSSRule.call(this);
|
|
957
|
-
this.
|
|
958
|
-
this.
|
|
959
|
-
this.
|
|
960
|
-
this.
|
|
961
|
-
this.
|
|
999
|
+
this.__href = "";
|
|
1000
|
+
this.__media = new CSSOM.MediaList();
|
|
1001
|
+
this.__layerName = null;
|
|
1002
|
+
this.__supportsText = null;
|
|
1003
|
+
this.__styleSheet = new CSSOM.CSSStyleSheet();
|
|
962
1004
|
};
|
|
963
1005
|
|
|
964
1006
|
CSSOM.CSSImportRule.prototype = new CSSOM.CSSRule();
|
|
965
1007
|
CSSOM.CSSImportRule.prototype.constructor = CSSOM.CSSImportRule;
|
|
966
|
-
|
|
1008
|
+
|
|
1009
|
+
Object.defineProperty(CSSOM.CSSImportRule.prototype, "type", {
|
|
1010
|
+
value: 3,
|
|
1011
|
+
writable: false
|
|
1012
|
+
});
|
|
967
1013
|
|
|
968
1014
|
Object.defineProperty(CSSOM.CSSImportRule.prototype, "cssText", {
|
|
969
1015
|
get: function() {
|
|
970
1016
|
var mediaText = this.media.mediaText;
|
|
971
|
-
return "@import url(\"" + this.href + "\")" + (this.layerName !== null ? " layer" + (this.layerName && "(" + this.layerName + ")") : "" ) + (this.supportsText ? " supports(" + this.supportsText + ")" : "" ) + (mediaText ? " " + mediaText : "") + ";";
|
|
1017
|
+
return "@import url(\"" + this.href.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + "\")" + (this.layerName !== null ? " layer" + (this.layerName && "(" + this.layerName + ")") : "" ) + (this.supportsText ? " supports(" + this.supportsText + ")" : "" ) + (mediaText ? " " + mediaText : "") + ";";
|
|
972
1018
|
},
|
|
973
1019
|
set: function(cssText) {
|
|
974
1020
|
var i = 0;
|
|
@@ -987,8 +1033,40 @@ Object.defineProperty(CSSOM.CSSImportRule.prototype, "cssText", {
|
|
|
987
1033
|
|
|
988
1034
|
var layerRegExp = /layer\(([^)]*)\)/;
|
|
989
1035
|
var layerRuleNameRegExp = /^(-?[_a-zA-Z]+(\.[_a-zA-Z]+)*[_a-zA-Z0-9-]*)$/;
|
|
990
|
-
var supportsRegExp = /supports\(([^)]+)\)/;
|
|
991
1036
|
var doubleOrMoreSpacesRegExp = /\s{2,}/g;
|
|
1037
|
+
|
|
1038
|
+
/**
|
|
1039
|
+
* Extracts the content inside supports() handling nested parentheses.
|
|
1040
|
+
* @param {string} text - The text to parse
|
|
1041
|
+
* @returns {object|null} - {content: string, endIndex: number} or null if not found
|
|
1042
|
+
*/
|
|
1043
|
+
function extractSupportsContent(text) {
|
|
1044
|
+
var supportsIndex = text.indexOf('supports(');
|
|
1045
|
+
if (supportsIndex !== 0) {
|
|
1046
|
+
return null;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
var depth = 0;
|
|
1050
|
+
var start = supportsIndex + 'supports('.length;
|
|
1051
|
+
var i = start;
|
|
1052
|
+
|
|
1053
|
+
for (; i < text.length; i++) {
|
|
1054
|
+
if (text[i] === '(') {
|
|
1055
|
+
depth++;
|
|
1056
|
+
} else if (text[i] === ')') {
|
|
1057
|
+
if (depth === 0) {
|
|
1058
|
+
// Found the closing parenthesis for supports()
|
|
1059
|
+
return {
|
|
1060
|
+
content: text.slice(start, i),
|
|
1061
|
+
endIndex: i
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
depth--;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
return null; // Unbalanced parentheses
|
|
1069
|
+
}
|
|
992
1070
|
|
|
993
1071
|
for (var character; (character = cssText.charAt(i)); i++) {
|
|
994
1072
|
|
|
@@ -1029,7 +1107,7 @@ Object.defineProperty(CSSOM.CSSImportRule.prototype, "cssText", {
|
|
|
1029
1107
|
url = url.slice(1, -1);
|
|
1030
1108
|
}
|
|
1031
1109
|
}
|
|
1032
|
-
this.
|
|
1110
|
+
this.__href = url;
|
|
1033
1111
|
i = index;
|
|
1034
1112
|
state = 'media';
|
|
1035
1113
|
}
|
|
@@ -1041,7 +1119,7 @@ Object.defineProperty(CSSOM.CSSImportRule.prototype, "cssText", {
|
|
|
1041
1119
|
if (!index) {
|
|
1042
1120
|
throw i + ": '\"' not found";
|
|
1043
1121
|
}
|
|
1044
|
-
this.
|
|
1122
|
+
this.__href = cssText.slice(i + 1, index);
|
|
1045
1123
|
i = index;
|
|
1046
1124
|
state = 'media';
|
|
1047
1125
|
}
|
|
@@ -1053,7 +1131,7 @@ Object.defineProperty(CSSOM.CSSImportRule.prototype, "cssText", {
|
|
|
1053
1131
|
if (!index) {
|
|
1054
1132
|
throw i + ': "\'" not found';
|
|
1055
1133
|
}
|
|
1056
|
-
this.
|
|
1134
|
+
this.__href = cssText.slice(i + 1, index);
|
|
1057
1135
|
i = index;
|
|
1058
1136
|
state = 'media';
|
|
1059
1137
|
}
|
|
@@ -1071,7 +1149,7 @@ Object.defineProperty(CSSOM.CSSImportRule.prototype, "cssText", {
|
|
|
1071
1149
|
var layerName = layerMatch[1].trim();
|
|
1072
1150
|
|
|
1073
1151
|
if (layerName.match(layerRuleNameRegExp) !== null) {
|
|
1074
|
-
this.
|
|
1152
|
+
this.__layerName = layerMatch[1].trim();
|
|
1075
1153
|
bufferTrimmed = bufferTrimmed.replace(layerRegExp, '')
|
|
1076
1154
|
.replace(doubleOrMoreSpacesRegExp, ' ') // Replace double or more spaces with single space
|
|
1077
1155
|
.trim();
|
|
@@ -1084,19 +1162,19 @@ Object.defineProperty(CSSOM.CSSImportRule.prototype, "cssText", {
|
|
|
1084
1162
|
}
|
|
1085
1163
|
}
|
|
1086
1164
|
} else {
|
|
1087
|
-
this.
|
|
1165
|
+
this.__layerName = "";
|
|
1088
1166
|
bufferTrimmed = bufferTrimmed.substring('layer'.length).trim()
|
|
1089
1167
|
}
|
|
1090
1168
|
}
|
|
1091
1169
|
|
|
1092
|
-
var
|
|
1170
|
+
var supportsResult = extractSupportsContent(bufferTrimmed);
|
|
1093
1171
|
|
|
1094
|
-
if (
|
|
1172
|
+
if (supportsResult) {
|
|
1095
1173
|
// REVIEW: In the browser, an empty supports() invalidates and ignores the entire @import rule
|
|
1096
|
-
this.
|
|
1097
|
-
|
|
1098
|
-
.
|
|
1099
|
-
.trim();
|
|
1174
|
+
this.__supportsText = supportsResult.content.trim();
|
|
1175
|
+
// Remove the entire supports(...) from the buffer
|
|
1176
|
+
bufferTrimmed = bufferTrimmed.slice(0, 0) + bufferTrimmed.slice(supportsResult.endIndex + 1);
|
|
1177
|
+
bufferTrimmed = bufferTrimmed.replace(doubleOrMoreSpacesRegExp, ' ').trim();
|
|
1100
1178
|
}
|
|
1101
1179
|
|
|
1102
1180
|
// REVIEW: In the browser, any invalid media is replaced with 'not all'
|
|
@@ -1117,6 +1195,43 @@ Object.defineProperty(CSSOM.CSSImportRule.prototype, "cssText", {
|
|
|
1117
1195
|
}
|
|
1118
1196
|
});
|
|
1119
1197
|
|
|
1198
|
+
Object.defineProperty(CSSOM.CSSImportRule.prototype, "href", {
|
|
1199
|
+
get: function() {
|
|
1200
|
+
return this.__href;
|
|
1201
|
+
}
|
|
1202
|
+
});
|
|
1203
|
+
|
|
1204
|
+
Object.defineProperty(CSSOM.CSSImportRule.prototype, "media", {
|
|
1205
|
+
get: function() {
|
|
1206
|
+
return this.__media;
|
|
1207
|
+
},
|
|
1208
|
+
set: function(value) {
|
|
1209
|
+
if (typeof value === "string") {
|
|
1210
|
+
this.__media.mediaText = value;
|
|
1211
|
+
} else {
|
|
1212
|
+
this.__media = value;
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
});
|
|
1216
|
+
|
|
1217
|
+
Object.defineProperty(CSSOM.CSSImportRule.prototype, "layerName", {
|
|
1218
|
+
get: function() {
|
|
1219
|
+
return this.__layerName;
|
|
1220
|
+
}
|
|
1221
|
+
});
|
|
1222
|
+
|
|
1223
|
+
Object.defineProperty(CSSOM.CSSImportRule.prototype, "supportsText", {
|
|
1224
|
+
get: function() {
|
|
1225
|
+
return this.__supportsText;
|
|
1226
|
+
}
|
|
1227
|
+
});
|
|
1228
|
+
|
|
1229
|
+
Object.defineProperty(CSSOM.CSSImportRule.prototype, "styleSheet", {
|
|
1230
|
+
get: function() {
|
|
1231
|
+
return this.__styleSheet;
|
|
1232
|
+
}
|
|
1233
|
+
});
|
|
1234
|
+
|
|
1120
1235
|
|
|
1121
1236
|
|
|
1122
1237
|
|
|
@@ -1128,23 +1243,25 @@ Object.defineProperty(CSSOM.CSSImportRule.prototype, "cssText", {
|
|
|
1128
1243
|
*/
|
|
1129
1244
|
CSSOM.CSSNamespaceRule = function CSSNamespaceRule() {
|
|
1130
1245
|
CSSOM.CSSRule.call(this);
|
|
1131
|
-
this.
|
|
1132
|
-
this.
|
|
1133
|
-
this.styleSheet = new CSSOM.CSSStyleSheet();
|
|
1246
|
+
this.__prefix = "";
|
|
1247
|
+
this.__namespaceURI = "";
|
|
1134
1248
|
};
|
|
1135
1249
|
|
|
1136
1250
|
CSSOM.CSSNamespaceRule.prototype = new CSSOM.CSSRule();
|
|
1137
1251
|
CSSOM.CSSNamespaceRule.prototype.constructor = CSSOM.CSSNamespaceRule;
|
|
1138
|
-
|
|
1252
|
+
|
|
1253
|
+
Object.defineProperty(CSSOM.CSSNamespaceRule.prototype, "type", {
|
|
1254
|
+
value: 10,
|
|
1255
|
+
writable: false
|
|
1256
|
+
});
|
|
1139
1257
|
|
|
1140
1258
|
Object.defineProperty(CSSOM.CSSNamespaceRule.prototype, "cssText", {
|
|
1141
1259
|
get: function() {
|
|
1142
1260
|
return "@namespace" + (this.prefix && " " + this.prefix) + " url(\"" + this.namespaceURI + "\");";
|
|
1143
1261
|
},
|
|
1144
1262
|
set: function(cssText) {
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
this.namespaceURI = "";
|
|
1263
|
+
var newPrefix = "";
|
|
1264
|
+
var newNamespaceURI = "";
|
|
1148
1265
|
|
|
1149
1266
|
// Remove @namespace and trim
|
|
1150
1267
|
var text = cssText.trim();
|
|
@@ -1167,26 +1284,40 @@ Object.defineProperty(CSSOM.CSSNamespaceRule.prototype, "cssText", {
|
|
|
1167
1284
|
if (match) {
|
|
1168
1285
|
// If prefix is present
|
|
1169
1286
|
if (match[1]) {
|
|
1170
|
-
|
|
1287
|
+
newPrefix = match[1];
|
|
1171
1288
|
}
|
|
1172
1289
|
// If url(...) form with quotes
|
|
1173
1290
|
if (typeof match[3] !== "undefined") {
|
|
1174
|
-
|
|
1291
|
+
newNamespaceURI = match[3];
|
|
1175
1292
|
}
|
|
1176
1293
|
// If url(...) form without quotes
|
|
1177
1294
|
else if (typeof match[4] !== "undefined") {
|
|
1178
|
-
|
|
1295
|
+
newNamespaceURI = match[4].trim();
|
|
1179
1296
|
}
|
|
1180
1297
|
// If quoted string form
|
|
1181
1298
|
else if (typeof match[6] !== "undefined") {
|
|
1182
|
-
|
|
1299
|
+
newNamespaceURI = match[6];
|
|
1183
1300
|
}
|
|
1301
|
+
|
|
1302
|
+
this.__prefix = newPrefix;
|
|
1303
|
+
this.__namespaceURI = newNamespaceURI;
|
|
1184
1304
|
} else {
|
|
1185
1305
|
throw new DOMException("Invalid @namespace rule", "InvalidStateError");
|
|
1186
1306
|
}
|
|
1187
1307
|
}
|
|
1188
1308
|
});
|
|
1189
1309
|
|
|
1310
|
+
Object.defineProperty(CSSOM.CSSNamespaceRule.prototype, "prefix", {
|
|
1311
|
+
get: function() {
|
|
1312
|
+
return this.__prefix;
|
|
1313
|
+
}
|
|
1314
|
+
});
|
|
1315
|
+
|
|
1316
|
+
Object.defineProperty(CSSOM.CSSNamespaceRule.prototype, "namespaceURI", {
|
|
1317
|
+
get: function() {
|
|
1318
|
+
return this.__namespaceURI;
|
|
1319
|
+
}
|
|
1320
|
+
});
|
|
1190
1321
|
|
|
1191
1322
|
|
|
1192
1323
|
|
|
@@ -1300,10 +1431,23 @@ Object.defineProperty(CSSOM.CSSStartingStyleRule.prototype, "cssText", {
|
|
|
1300
1431
|
* @see http://dev.w3.org/csswg/cssom/#the-stylesheet-interface
|
|
1301
1432
|
*/
|
|
1302
1433
|
CSSOM.StyleSheet = function StyleSheet() {
|
|
1434
|
+
this.__media = new CSSOM.MediaList();
|
|
1303
1435
|
this.__parentStyleSheet = null;
|
|
1304
1436
|
};
|
|
1305
1437
|
|
|
1306
1438
|
Object.defineProperties(CSSOM.StyleSheet.prototype, {
|
|
1439
|
+
media: {
|
|
1440
|
+
get: function() {
|
|
1441
|
+
return this.__media;
|
|
1442
|
+
},
|
|
1443
|
+
set: function(value) {
|
|
1444
|
+
if (typeof value === "string") {
|
|
1445
|
+
this.__media.mediaText = value;
|
|
1446
|
+
} else {
|
|
1447
|
+
this.__media = value;
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
},
|
|
1307
1451
|
parentStyleSheet: {
|
|
1308
1452
|
get: function() {
|
|
1309
1453
|
return this.__parentStyleSheet;
|
|
@@ -1366,7 +1510,10 @@ CSSOM.CSSStyleSheet.prototype.insertRule = function(rule, index) {
|
|
|
1366
1510
|
}
|
|
1367
1511
|
|
|
1368
1512
|
var ruleToParse = String(rule);
|
|
1369
|
-
var
|
|
1513
|
+
var parseErrors = [];
|
|
1514
|
+
var parsedSheet = CSSOM.parse(ruleToParse, undefined, function(err) {
|
|
1515
|
+
parseErrors.push(err);
|
|
1516
|
+
} );
|
|
1370
1517
|
if (parsedSheet.cssRules.length !== 1) {
|
|
1371
1518
|
errorUtils.throwParseError(this, 'insertRule', this.constructor.name, ruleToParse, 'SyntaxError');
|
|
1372
1519
|
}
|
|
@@ -1434,12 +1581,21 @@ CSSOM.CSSStyleSheet.prototype.insertRule = function(rule, index) {
|
|
|
1434
1581
|
'HierarchyRequestError');
|
|
1435
1582
|
}
|
|
1436
1583
|
|
|
1584
|
+
// Cannot insert if there are already non-special rules
|
|
1585
|
+
if (firstNonImportNamespaceIndex < this.cssRules.length) {
|
|
1586
|
+
errorUtils.throwError(this, 'DOMException',
|
|
1587
|
+
"Failed to execute 'insertRule' on '" + this.constructor.name + "': Failed to insert the rule.",
|
|
1588
|
+
'InvalidStateError');
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1437
1591
|
// Cannot insert after other types of rules
|
|
1438
1592
|
if (index > firstNonImportNamespaceIndex) {
|
|
1439
1593
|
errorUtils.throwError(this, 'DOMException',
|
|
1440
1594
|
"Failed to execute 'insertRule' on '" + this.constructor.name + "': Failed to insert the rule.",
|
|
1441
1595
|
'HierarchyRequestError');
|
|
1442
1596
|
}
|
|
1597
|
+
|
|
1598
|
+
|
|
1443
1599
|
} else if (cssRule.constructor.name === 'CSSLayerStatementRule') {
|
|
1444
1600
|
// @layer statement rules can be inserted anywhere before @import and @namespace
|
|
1445
1601
|
// No additional restrictions beyond what's already handled
|
|
@@ -1456,6 +1612,10 @@ CSSOM.CSSStyleSheet.prototype.insertRule = function(rule, index) {
|
|
|
1456
1612
|
"Failed to execute 'insertRule' on '" + this.constructor.name + "': Failed to insert the rule.",
|
|
1457
1613
|
'HierarchyRequestError');
|
|
1458
1614
|
}
|
|
1615
|
+
|
|
1616
|
+
if (parseErrors.length !== 0) {
|
|
1617
|
+
errorUtils.throwParseError(this, 'insertRule', this.constructor.name, ruleToParse, 'SyntaxError');
|
|
1618
|
+
}
|
|
1459
1619
|
}
|
|
1460
1620
|
|
|
1461
1621
|
cssRule.__parentStyleSheet = this;
|
|
@@ -1495,13 +1655,20 @@ CSSOM.CSSStyleSheet.prototype.deleteRule = function(index) {
|
|
|
1495
1655
|
if (index >= this.cssRules.length) {
|
|
1496
1656
|
errorUtils.throwIndexError(this, 'deleteRule', this.constructor.name, index, this.cssRules.length);
|
|
1497
1657
|
}
|
|
1498
|
-
if (this.cssRules[index]
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1658
|
+
if (this.cssRules[index]) {
|
|
1659
|
+
if (this.cssRules[index].constructor.name == "CSSNamespaceRule") {
|
|
1660
|
+
var shouldContinue = this.cssRules.every(function (rule) {
|
|
1661
|
+
return ['CSSImportRule','CSSLayerStatementRule','CSSNamespaceRule'].indexOf(rule.constructor.name) !== -1
|
|
1662
|
+
});
|
|
1663
|
+
if (!shouldContinue) {
|
|
1664
|
+
errorUtils.throwError(this, 'DOMException', "Failed to execute 'deleteRule' on '" + this.constructor.name + "': Failed to delete rule.", "InvalidStateError");
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
if (this.cssRules[index].constructor.name == "CSSImportRule") {
|
|
1668
|
+
this.cssRules[index].styleSheet.__parentStyleSheet = null;
|
|
1504
1669
|
}
|
|
1670
|
+
|
|
1671
|
+
this.cssRules[index].__parentStyleSheet = null;
|
|
1505
1672
|
}
|
|
1506
1673
|
this.cssRules.splice(index, 1);
|
|
1507
1674
|
};
|
|
@@ -2364,6 +2531,266 @@ Object.defineProperties(CSSOM.CSSLayerStatementRule.prototype, {
|
|
|
2364
2531
|
|
|
2365
2532
|
|
|
2366
2533
|
|
|
2534
|
+
/**
|
|
2535
|
+
* @constructor
|
|
2536
|
+
* @see https://drafts.csswg.org/cssom/#the-csspagerule-interface
|
|
2537
|
+
*/
|
|
2538
|
+
CSSOM.CSSPageRule = function CSSPageRule() {
|
|
2539
|
+
CSSOM.CSSGroupingRule.call(this);
|
|
2540
|
+
this.__style = new CSSOM.CSSStyleDeclaration();
|
|
2541
|
+
this.__style.parentRule = this;
|
|
2542
|
+
};
|
|
2543
|
+
|
|
2544
|
+
CSSOM.CSSPageRule.prototype = new CSSOM.CSSGroupingRule();
|
|
2545
|
+
CSSOM.CSSPageRule.prototype.constructor = CSSOM.CSSPageRule;
|
|
2546
|
+
|
|
2547
|
+
Object.defineProperty(CSSOM.CSSPageRule.prototype, "type", {
|
|
2548
|
+
value: 6,
|
|
2549
|
+
writable: false
|
|
2550
|
+
});
|
|
2551
|
+
|
|
2552
|
+
Object.defineProperty(CSSOM.CSSPageRule.prototype, "selectorText", {
|
|
2553
|
+
get: function() {
|
|
2554
|
+
return this.__selectorText;
|
|
2555
|
+
},
|
|
2556
|
+
set: function(value) {
|
|
2557
|
+
if (typeof value === "string") {
|
|
2558
|
+
var trimmedValue = value.trim();
|
|
2559
|
+
|
|
2560
|
+
// Empty selector is valid for @page
|
|
2561
|
+
if (trimmedValue === '') {
|
|
2562
|
+
this.__selectorText = '';
|
|
2563
|
+
return;
|
|
2564
|
+
}
|
|
2565
|
+
|
|
2566
|
+
// Parse @page selectorText for page name and pseudo-pages
|
|
2567
|
+
// Valid formats:
|
|
2568
|
+
// - (empty - no name, no pseudo-page)
|
|
2569
|
+
// - :left, :right, :first, :blank (pseudo-page only)
|
|
2570
|
+
// - named (named page only)
|
|
2571
|
+
// - named:first (named page with single pseudo-page)
|
|
2572
|
+
// - named:first:left (named page with multiple pseudo-pages)
|
|
2573
|
+
var atPageRuleSelectorRegExp = /^([^\s:]+)?((?::\w+)*)$/;
|
|
2574
|
+
var match = trimmedValue.match(atPageRuleSelectorRegExp);
|
|
2575
|
+
if (match) {
|
|
2576
|
+
var pageName = match[1] || '';
|
|
2577
|
+
var pseudoPages = match[2] || '';
|
|
2578
|
+
|
|
2579
|
+
// Validate page name if present
|
|
2580
|
+
if (pageName) {
|
|
2581
|
+
var cssCustomIdentifierRegExp = /^(-?[_a-zA-Z]+(\.[_a-zA-Z]+)*[_a-zA-Z0-9-]*)$/; // Validates a css custom identifier
|
|
2582
|
+
// Page name can be an identifier or a string
|
|
2583
|
+
if (!cssCustomIdentifierRegExp.test(pageName)) {
|
|
2584
|
+
return;
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
|
|
2588
|
+
// Validate pseudo-pages if present
|
|
2589
|
+
if (pseudoPages) {
|
|
2590
|
+
var pseudos = pseudoPages.split(':').filter(function(p) { return p; });
|
|
2591
|
+
var validPseudos = ['left', 'right', 'first', 'blank'];
|
|
2592
|
+
var allValid = true;
|
|
2593
|
+
for (var j = 0; j < pseudos.length; j++) {
|
|
2594
|
+
if (validPseudos.indexOf(pseudos[j].toLowerCase()) === -1) {
|
|
2595
|
+
allValid = false;
|
|
2596
|
+
break;
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
|
|
2600
|
+
if (!allValid) {
|
|
2601
|
+
return; // Invalid pseudo-page, do nothing
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
|
|
2605
|
+
this.__selectorText = pageName + pseudoPages.toLowerCase();
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
});
|
|
2610
|
+
|
|
2611
|
+
Object.defineProperty(CSSOM.CSSPageRule.prototype, "style", {
|
|
2612
|
+
get: function() {
|
|
2613
|
+
return this.__style;
|
|
2614
|
+
},
|
|
2615
|
+
set: function(value) {
|
|
2616
|
+
if (typeof value === "string") {
|
|
2617
|
+
this.__style.cssText = value;
|
|
2618
|
+
} else {
|
|
2619
|
+
this.__style = value;
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
});
|
|
2623
|
+
|
|
2624
|
+
Object.defineProperty(CSSOM.CSSPageRule.prototype, "cssText", {
|
|
2625
|
+
get: function() {
|
|
2626
|
+
var values = ""
|
|
2627
|
+
if (this.cssRules.length) {
|
|
2628
|
+
var valuesArr = [" {"];
|
|
2629
|
+
this.style.cssText && valuesArr.push(this.style.cssText);
|
|
2630
|
+
valuesArr.push(this.cssRules.map(function(rule){ return rule.cssText }).join("\n "));
|
|
2631
|
+
values = valuesArr.join("\n ") + "\n}"
|
|
2632
|
+
} else {
|
|
2633
|
+
values = " {" + (this.style.cssText ? " " + this.style.cssText : "") + " }";
|
|
2634
|
+
}
|
|
2635
|
+
return "@page" + (this.selectorText ? " " + this.selectorText : "") + values;
|
|
2636
|
+
},
|
|
2637
|
+
set: function(cssText) {
|
|
2638
|
+
if (typeof value === "string") {
|
|
2639
|
+
var rule = CSSOM.CSSPageRule.parse(cssText);
|
|
2640
|
+
this.__style = rule.style;
|
|
2641
|
+
this.selectorText = rule.selectorText;
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
});
|
|
2645
|
+
|
|
2646
|
+
/**
|
|
2647
|
+
* NON-STANDARD
|
|
2648
|
+
* lightweight version of parse.js.
|
|
2649
|
+
* @param {string} ruleText
|
|
2650
|
+
* @return CSSPageRule
|
|
2651
|
+
*/
|
|
2652
|
+
CSSOM.CSSPageRule.parse = function(ruleText) {
|
|
2653
|
+
var i = 0;
|
|
2654
|
+
var state = "selector";
|
|
2655
|
+
var index;
|
|
2656
|
+
var j = i;
|
|
2657
|
+
var buffer = "";
|
|
2658
|
+
|
|
2659
|
+
var SIGNIFICANT_WHITESPACE = {
|
|
2660
|
+
"selector": true,
|
|
2661
|
+
"value": true
|
|
2662
|
+
};
|
|
2663
|
+
|
|
2664
|
+
var pageRule = new CSSOM.CSSPageRule();
|
|
2665
|
+
var name, priority="";
|
|
2666
|
+
|
|
2667
|
+
for (var character; (character = ruleText.charAt(i)); i++) {
|
|
2668
|
+
|
|
2669
|
+
switch (character) {
|
|
2670
|
+
|
|
2671
|
+
case " ":
|
|
2672
|
+
case "\t":
|
|
2673
|
+
case "\r":
|
|
2674
|
+
case "\n":
|
|
2675
|
+
case "\f":
|
|
2676
|
+
if (SIGNIFICANT_WHITESPACE[state]) {
|
|
2677
|
+
// Squash 2 or more white-spaces in the row into 1
|
|
2678
|
+
switch (ruleText.charAt(i - 1)) {
|
|
2679
|
+
case " ":
|
|
2680
|
+
case "\t":
|
|
2681
|
+
case "\r":
|
|
2682
|
+
case "\n":
|
|
2683
|
+
case "\f":
|
|
2684
|
+
break;
|
|
2685
|
+
default:
|
|
2686
|
+
buffer += " ";
|
|
2687
|
+
break;
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
break;
|
|
2691
|
+
|
|
2692
|
+
// String
|
|
2693
|
+
case '"':
|
|
2694
|
+
j = i + 1;
|
|
2695
|
+
index = ruleText.indexOf('"', j) + 1;
|
|
2696
|
+
if (!index) {
|
|
2697
|
+
throw '" is missing';
|
|
2698
|
+
}
|
|
2699
|
+
buffer += ruleText.slice(i, index);
|
|
2700
|
+
i = index - 1;
|
|
2701
|
+
break;
|
|
2702
|
+
|
|
2703
|
+
case "'":
|
|
2704
|
+
j = i + 1;
|
|
2705
|
+
index = ruleText.indexOf("'", j) + 1;
|
|
2706
|
+
if (!index) {
|
|
2707
|
+
throw "' is missing";
|
|
2708
|
+
}
|
|
2709
|
+
buffer += ruleText.slice(i, index);
|
|
2710
|
+
i = index - 1;
|
|
2711
|
+
break;
|
|
2712
|
+
|
|
2713
|
+
// Comment
|
|
2714
|
+
case "/":
|
|
2715
|
+
if (ruleText.charAt(i + 1) === "*") {
|
|
2716
|
+
i += 2;
|
|
2717
|
+
index = ruleText.indexOf("*/", i);
|
|
2718
|
+
if (index === -1) {
|
|
2719
|
+
throw new SyntaxError("Missing */");
|
|
2720
|
+
} else {
|
|
2721
|
+
i = index + 1;
|
|
2722
|
+
}
|
|
2723
|
+
} else {
|
|
2724
|
+
buffer += character;
|
|
2725
|
+
}
|
|
2726
|
+
break;
|
|
2727
|
+
|
|
2728
|
+
case "{":
|
|
2729
|
+
if (state === "selector") {
|
|
2730
|
+
pageRule.selectorText = buffer.trim();
|
|
2731
|
+
buffer = "";
|
|
2732
|
+
state = "name";
|
|
2733
|
+
}
|
|
2734
|
+
break;
|
|
2735
|
+
|
|
2736
|
+
case ":":
|
|
2737
|
+
if (state === "name") {
|
|
2738
|
+
name = buffer.trim();
|
|
2739
|
+
buffer = "";
|
|
2740
|
+
state = "value";
|
|
2741
|
+
} else {
|
|
2742
|
+
buffer += character;
|
|
2743
|
+
}
|
|
2744
|
+
break;
|
|
2745
|
+
|
|
2746
|
+
case "!":
|
|
2747
|
+
if (state === "value" && ruleText.indexOf("!important", i) === i) {
|
|
2748
|
+
priority = "important";
|
|
2749
|
+
i += "important".length;
|
|
2750
|
+
} else {
|
|
2751
|
+
buffer += character;
|
|
2752
|
+
}
|
|
2753
|
+
break;
|
|
2754
|
+
|
|
2755
|
+
case ";":
|
|
2756
|
+
if (state === "value") {
|
|
2757
|
+
pageRule.style.setProperty(name, buffer.trim(), priority);
|
|
2758
|
+
priority = "";
|
|
2759
|
+
buffer = "";
|
|
2760
|
+
state = "name";
|
|
2761
|
+
} else {
|
|
2762
|
+
buffer += character;
|
|
2763
|
+
}
|
|
2764
|
+
break;
|
|
2765
|
+
|
|
2766
|
+
case "}":
|
|
2767
|
+
if (state === "value") {
|
|
2768
|
+
pageRule.style.setProperty(name, buffer.trim(), priority);
|
|
2769
|
+
priority = "";
|
|
2770
|
+
buffer = "";
|
|
2771
|
+
} else if (state === "name") {
|
|
2772
|
+
break;
|
|
2773
|
+
} else {
|
|
2774
|
+
buffer += character;
|
|
2775
|
+
}
|
|
2776
|
+
state = "selector";
|
|
2777
|
+
break;
|
|
2778
|
+
|
|
2779
|
+
default:
|
|
2780
|
+
buffer += character;
|
|
2781
|
+
break;
|
|
2782
|
+
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2786
|
+
return pageRule;
|
|
2787
|
+
|
|
2788
|
+
};
|
|
2789
|
+
|
|
2790
|
+
|
|
2791
|
+
|
|
2792
|
+
|
|
2793
|
+
|
|
2367
2794
|
/**
|
|
2368
2795
|
* Parses a CSS string and returns a CSSOM.CSSStyleSheet object representing the parsed stylesheet.
|
|
2369
2796
|
*
|
|
@@ -2412,7 +2839,8 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2412
2839
|
"counterStyleBlock": true,
|
|
2413
2840
|
'documentRule-begin': true,
|
|
2414
2841
|
"scopeBlock": true,
|
|
2415
|
-
"layerBlock": true
|
|
2842
|
+
"layerBlock": true,
|
|
2843
|
+
"pageBlock": true
|
|
2416
2844
|
};
|
|
2417
2845
|
|
|
2418
2846
|
var styleSheet = new CSSOM.CSSStyleSheet();
|
|
@@ -2430,7 +2858,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2430
2858
|
var ancestorRules = [];
|
|
2431
2859
|
var prevScope;
|
|
2432
2860
|
|
|
2433
|
-
var name, priority="", styleRule, mediaRule, containerRule, counterStyleRule, supportsRule, importRule, fontFaceRule, keyframesRule, documentRule, hostRule, startingStyleRule, scopeRule, layerBlockRule, layerStatementRule, nestedSelectorRule, namespaceRule;
|
|
2861
|
+
var name, priority="", styleRule, mediaRule, containerRule, counterStyleRule, supportsRule, importRule, fontFaceRule, keyframesRule, documentRule, hostRule, startingStyleRule, scopeRule, pageRule, layerBlockRule, layerStatementRule, nestedSelectorRule, namespaceRule;
|
|
2434
2862
|
|
|
2435
2863
|
// Track defined namespace prefixes for validation
|
|
2436
2864
|
var definedNamespacePrefixes = {};
|
|
@@ -2444,8 +2872,9 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2444
2872
|
var forwardImportRuleValidationRegExp = /(?:\s|\/\*|'|")/; // Match that the rule is followed by any whitespace, an opening comment, a single quote or double quote
|
|
2445
2873
|
var forwardRuleClosingBraceRegExp = /{[^{}]*}|}/; // Finds the next closing brace of a rule block
|
|
2446
2874
|
var forwardRuleSemicolonAndOpeningBraceRegExp = /^.*?({|;)/; // Finds the next semicolon or opening brace after the at-rule
|
|
2447
|
-
var
|
|
2875
|
+
var cssCustomIdentifierRegExp = /^(-?[_a-zA-Z]+(\.[_a-zA-Z]+)*[_a-zA-Z0-9-]*)$/; // Validates a css custom identifier
|
|
2448
2876
|
var startsWithCombinatorRegExp = /^\s*[>+~]/; // Checks if a selector starts with a CSS combinator (>, +, ~)
|
|
2877
|
+
var atPageRuleSelectorRegExp = /^([^\s:]+)?((?::\w+)*)$/;
|
|
2449
2878
|
|
|
2450
2879
|
/**
|
|
2451
2880
|
* Searches for the first occurrence of a CSS at-rule statement terminator (`;` or `}`)
|
|
@@ -2770,12 +3199,12 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2770
3199
|
if (isValid && atRuleKey === "@scope") {
|
|
2771
3200
|
var openBraceIndex = ruleSlice.indexOf('{');
|
|
2772
3201
|
if (openBraceIndex !== -1) {
|
|
2773
|
-
// Extract the
|
|
2774
|
-
var
|
|
2775
|
-
|
|
2776
|
-
// Skip past
|
|
2777
|
-
var preludeContent =
|
|
2778
|
-
|
|
3202
|
+
// Extract the rule prelude (everything between the at-rule and {)
|
|
3203
|
+
var rulePrelude = ruleSlice.slice(0, openBraceIndex).trim();
|
|
3204
|
+
|
|
3205
|
+
// Skip past at-rule keyword and whitespace
|
|
3206
|
+
var preludeContent = rulePrelude.slice("@scope".length).trim();
|
|
3207
|
+
|
|
2779
3208
|
if (preludeContent.length > 0) {
|
|
2780
3209
|
// Parse the scope prelude
|
|
2781
3210
|
var parsedScopePrelude = parseScopePrelude(preludeContent);
|
|
@@ -2814,6 +3243,64 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2814
3243
|
// Empty prelude (@scope {}) is valid
|
|
2815
3244
|
}
|
|
2816
3245
|
}
|
|
3246
|
+
|
|
3247
|
+
if (isValid && atRuleKey === "@page") {
|
|
3248
|
+
var openBraceIndex = ruleSlice.indexOf('{');
|
|
3249
|
+
if (openBraceIndex !== -1) {
|
|
3250
|
+
// Extract the rule prelude (everything between the at-rule and {)
|
|
3251
|
+
var rulePrelude = ruleSlice.slice(0, openBraceIndex).trim();
|
|
3252
|
+
|
|
3253
|
+
// Skip past at-rule keyword and whitespace
|
|
3254
|
+
var preludeContent = rulePrelude.slice("@page".length).trim();
|
|
3255
|
+
|
|
3256
|
+
if (preludeContent.length > 0) {
|
|
3257
|
+
var trimmedValue = preludeContent.trim();
|
|
3258
|
+
|
|
3259
|
+
// Empty selector is valid for @page
|
|
3260
|
+
if (trimmedValue !== '') {
|
|
3261
|
+
// Parse @page selectorText for page name and pseudo-pages
|
|
3262
|
+
// Valid formats:
|
|
3263
|
+
// - (empty - no name, no pseudo-page)
|
|
3264
|
+
// - :left, :right, :first, :blank (pseudo-page only)
|
|
3265
|
+
// - named (named page only)
|
|
3266
|
+
// - named:first (named page with single pseudo-page)
|
|
3267
|
+
// - named:first:left (named page with multiple pseudo-pages)
|
|
3268
|
+
var match = trimmedValue.match(atPageRuleSelectorRegExp);
|
|
3269
|
+
if (match) {
|
|
3270
|
+
var pageName = match[1] || '';
|
|
3271
|
+
var pseudoPages = match[2] || '';
|
|
3272
|
+
|
|
3273
|
+
// Validate page name if present
|
|
3274
|
+
if (pageName) {
|
|
3275
|
+
if (!cssCustomIdentifierRegExp.test(pageName)) {
|
|
3276
|
+
isValid = false;
|
|
3277
|
+
}
|
|
3278
|
+
}
|
|
3279
|
+
|
|
3280
|
+
// Validate pseudo-pages if present
|
|
3281
|
+
if (pseudoPages) {
|
|
3282
|
+
var pseudos = pseudoPages.split(':').filter(function(p) { return p; });
|
|
3283
|
+
var validPseudos = ['left', 'right', 'first', 'blank'];
|
|
3284
|
+
var allValid = true;
|
|
3285
|
+
for (var j = 0; j < pseudos.length; j++) {
|
|
3286
|
+
if (validPseudos.indexOf(pseudos[j].toLowerCase()) === -1) {
|
|
3287
|
+
allValid = false;
|
|
3288
|
+
break;
|
|
3289
|
+
}
|
|
3290
|
+
}
|
|
3291
|
+
|
|
3292
|
+
if (!allValid) {
|
|
3293
|
+
isValid = false;
|
|
3294
|
+
}
|
|
3295
|
+
}
|
|
3296
|
+
} else {
|
|
3297
|
+
isValid = false;
|
|
3298
|
+
}
|
|
3299
|
+
}
|
|
3300
|
+
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
3303
|
+
}
|
|
2817
3304
|
|
|
2818
3305
|
if (!isValid) {
|
|
2819
3306
|
// If it's invalid the browser will simply ignore the entire invalid block
|
|
@@ -2893,6 +3380,27 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2893
3380
|
return false;
|
|
2894
3381
|
}
|
|
2895
3382
|
|
|
3383
|
+
// Check for invalid pseudo-class usage with quoted strings
|
|
3384
|
+
// Pseudo-classes like :lang(), :dir(), :nth-*() should not accept quoted strings
|
|
3385
|
+
var pseudoPattern = /::?([a-zA-Z][\w-]*)\(([^)]+)\)/g;
|
|
3386
|
+
var pseudoMatch;
|
|
3387
|
+
while ((pseudoMatch = pseudoPattern.exec(selector)) !== null) {
|
|
3388
|
+
var pseudoName = pseudoMatch[1];
|
|
3389
|
+
var pseudoContent = pseudoMatch[2];
|
|
3390
|
+
|
|
3391
|
+
// List of pseudo-classes that should not accept quoted strings
|
|
3392
|
+
// :lang() - accepts language codes: en, fr-CA
|
|
3393
|
+
// :dir() - accepts direction: ltr, rtl
|
|
3394
|
+
// :nth-*() - accepts An+B notation: 2n+1, odd, even
|
|
3395
|
+
var noQuotesPseudos = ['lang', 'dir', 'nth-child', 'nth-last-child', 'nth-of-type', 'nth-last-of-type'];
|
|
3396
|
+
|
|
3397
|
+
for (var i = 0; i < noQuotesPseudos.length; i++) {
|
|
3398
|
+
if (pseudoName === noQuotesPseudos[i] && /['"]/.test(pseudoContent)) {
|
|
3399
|
+
return false;
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
|
|
2896
3404
|
// Fallback to a loose regexp for the overall selector structure (without deep paren matching)
|
|
2897
3405
|
// This is similar to the original, but without nested paren limitations
|
|
2898
3406
|
// Modified to support namespace selectors: *|element, prefix|element, |element
|
|
@@ -2997,7 +3505,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
2997
3505
|
* @returns {boolean} Returns `true` if the selector is valid, otherwise `false`.
|
|
2998
3506
|
*/
|
|
2999
3507
|
|
|
3000
|
-
// Cache to store validated selectors (ES5-compliant object)
|
|
3508
|
+
// Cache to store validated selectors (previously a ES6 Map, now an ES5-compliant object)
|
|
3001
3509
|
var validatedSelectorsCache = {};
|
|
3002
3510
|
|
|
3003
3511
|
// Only pseudo-classes that accept selector lists should recurse
|
|
@@ -3107,6 +3615,28 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
3107
3615
|
return definedNamespacePrefixes.hasOwnProperty(namespacePrefix);
|
|
3108
3616
|
}
|
|
3109
3617
|
|
|
3618
|
+
/**
|
|
3619
|
+
* Processes a CSS selector text
|
|
3620
|
+
*
|
|
3621
|
+
* @param {string} selectorText - The CSS selector text to process
|
|
3622
|
+
* @returns {string} The processed selector text with normalized whitespace
|
|
3623
|
+
*/
|
|
3624
|
+
function processSelectorText(selectorText) {
|
|
3625
|
+
// TODO: Remove invalid selectors that appears inside pseudo classes
|
|
3626
|
+
// TODO: The same processing here needs to be reused in CSSStyleRule.selectorText setter
|
|
3627
|
+
// TODO: Move these validation logic to a shared function to be reused in CSSStyleRule.selectorText setter
|
|
3628
|
+
|
|
3629
|
+
/**
|
|
3630
|
+
* Normalizes whitespace and preserving quoted strings.
|
|
3631
|
+
* Replaces all newline characters (CRLF, CR, or LF) with spaces while keeping quoted
|
|
3632
|
+
* strings (single or double quotes) intact, including any escaped characters within them.
|
|
3633
|
+
*/
|
|
3634
|
+
return selectorText.replace(/(['"])(?:\\.|[^\\])*?\1|(\r\n|\r|\n)/g, function(match, _, newline) {
|
|
3635
|
+
if (newline) return " ";
|
|
3636
|
+
return match;
|
|
3637
|
+
});
|
|
3638
|
+
}
|
|
3639
|
+
|
|
3110
3640
|
/**
|
|
3111
3641
|
* Checks if a given CSS selector text is valid by splitting it by commas
|
|
3112
3642
|
* and validating each individual selector using the `validateSelector` function.
|
|
@@ -3115,6 +3645,9 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
3115
3645
|
* @returns {boolean} Returns true if all selectors are valid, otherwise false.
|
|
3116
3646
|
*/
|
|
3117
3647
|
function isValidSelectorText(selectorText) {
|
|
3648
|
+
// TODO: The same validations here needs to be reused in CSSStyleRule.selectorText setter
|
|
3649
|
+
// TODO: Move these validation logic to a shared function to be reused in CSSStyleRule.selectorText setter
|
|
3650
|
+
|
|
3118
3651
|
// Check for newlines inside single or double quotes using regex
|
|
3119
3652
|
// This matches any quoted string (single or double) containing a newline
|
|
3120
3653
|
var quotedNewlineRegExp = /(['"])(?:\\.|[^\\])*?\1/g;
|
|
@@ -3241,7 +3774,8 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
3241
3774
|
i += 2;
|
|
3242
3775
|
index = token.indexOf("*/", i);
|
|
3243
3776
|
if (index === -1) {
|
|
3244
|
-
|
|
3777
|
+
i = token.length - 1;
|
|
3778
|
+
buffer = "";
|
|
3245
3779
|
} else {
|
|
3246
3780
|
i = index + 1;
|
|
3247
3781
|
}
|
|
@@ -3314,6 +3848,15 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
3314
3848
|
});
|
|
3315
3849
|
buffer = "";
|
|
3316
3850
|
break;
|
|
3851
|
+
} else if (token.indexOf("@page", i) === i) {
|
|
3852
|
+
validateAtRule("@page", function(){
|
|
3853
|
+
state = "pageBlock"
|
|
3854
|
+
pageRule = new CSSOM.CSSPageRule();
|
|
3855
|
+
pageRule.__starts = i;
|
|
3856
|
+
i += "page".length;
|
|
3857
|
+
});
|
|
3858
|
+
buffer = "";
|
|
3859
|
+
break;
|
|
3317
3860
|
} else if (token.indexOf("@supports", i) === i) {
|
|
3318
3861
|
validateAtRule("@supports", function(){
|
|
3319
3862
|
state = "conditionBlock";
|
|
@@ -3411,10 +3954,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
3411
3954
|
}
|
|
3412
3955
|
|
|
3413
3956
|
currentScope = parentRule = styleRule;
|
|
3414
|
-
styleRule.selectorText = buffer.trim()
|
|
3415
|
-
if (newline) return " ";
|
|
3416
|
-
return match;
|
|
3417
|
-
});
|
|
3957
|
+
styleRule.selectorText = processSelectorText(buffer.trim());
|
|
3418
3958
|
styleRule.style.__starts = i;
|
|
3419
3959
|
styleRule.__parentStyleSheet = styleSheet;
|
|
3420
3960
|
buffer = "";
|
|
@@ -3432,7 +3972,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
3432
3972
|
buffer = "";
|
|
3433
3973
|
state = "before-selector";
|
|
3434
3974
|
} else if (state === "containerBlock") {
|
|
3435
|
-
containerRule.
|
|
3975
|
+
containerRule.__conditionText = buffer.trim();
|
|
3436
3976
|
|
|
3437
3977
|
if (parentRule) {
|
|
3438
3978
|
containerRule.__parentRule = parentRule;
|
|
@@ -3449,7 +3989,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
3449
3989
|
counterStyleRule.__parentStyleSheet = styleSheet;
|
|
3450
3990
|
buffer = "";
|
|
3451
3991
|
} else if (state === "conditionBlock") {
|
|
3452
|
-
supportsRule.
|
|
3992
|
+
supportsRule.__conditionText = buffer.trim();
|
|
3453
3993
|
|
|
3454
3994
|
if (parentRule) {
|
|
3455
3995
|
supportsRule.__parentRule = parentRule;
|
|
@@ -3484,7 +4024,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
3484
4024
|
} else if (state === "layerBlock") {
|
|
3485
4025
|
layerBlockRule.name = buffer.trim();
|
|
3486
4026
|
|
|
3487
|
-
var isValidName = layerBlockRule.name.length === 0 || layerBlockRule.name.match(
|
|
4027
|
+
var isValidName = layerBlockRule.name.length === 0 || layerBlockRule.name.match(cssCustomIdentifierRegExp) !== null;
|
|
3488
4028
|
|
|
3489
4029
|
if (isValidName) {
|
|
3490
4030
|
if (parentRule) {
|
|
@@ -3497,6 +4037,19 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
3497
4037
|
}
|
|
3498
4038
|
buffer = "";
|
|
3499
4039
|
state = "before-selector";
|
|
4040
|
+
} else if (state === "pageBlock") {
|
|
4041
|
+
pageRule.selectorText = buffer.trim();
|
|
4042
|
+
|
|
4043
|
+
if (parentRule) {
|
|
4044
|
+
pageRule.__parentRule = parentRule;
|
|
4045
|
+
ancestorRules.push(parentRule);
|
|
4046
|
+
}
|
|
4047
|
+
|
|
4048
|
+
currentScope = parentRule = pageRule;
|
|
4049
|
+
pageRule.__parentStyleSheet = styleSheet;
|
|
4050
|
+
styleRule = pageRule;
|
|
4051
|
+
buffer = "";
|
|
4052
|
+
state = "before-name";
|
|
3500
4053
|
} else if (state === "hostRule-begin") {
|
|
3501
4054
|
if (parentRule) {
|
|
3502
4055
|
ancestorRules.push(parentRule);
|
|
@@ -3570,10 +4123,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
3570
4123
|
}
|
|
3571
4124
|
|
|
3572
4125
|
styleRule = new CSSOM.CSSStyleRule();
|
|
3573
|
-
var processedSelectorText = buffer.trim()
|
|
3574
|
-
if (newline) return " ";
|
|
3575
|
-
return match;
|
|
3576
|
-
});
|
|
4126
|
+
var processedSelectorText = processSelectorText(buffer.trim());
|
|
3577
4127
|
// In a nested selector, ensure each selector contains '&' at the beginning, except for selectors that already have '&' somewhere
|
|
3578
4128
|
if (parentRule.constructor.name !== "CSSStyleRule" && parentRule.parentRule === null) {
|
|
3579
4129
|
styleRule.selectorText = processedSelectorText;
|
|
@@ -3697,7 +4247,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
3697
4247
|
testNamespaceRule.cssText = buffer + character;
|
|
3698
4248
|
|
|
3699
4249
|
namespaceRule = testNamespaceRule;
|
|
3700
|
-
namespaceRule.__parentStyleSheet =
|
|
4250
|
+
namespaceRule.__parentStyleSheet = styleSheet;
|
|
3701
4251
|
styleSheet.cssRules.push(namespaceRule);
|
|
3702
4252
|
|
|
3703
4253
|
// Track the namespace prefix for validation
|
|
@@ -3716,7 +4266,7 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
3716
4266
|
return name.trim();
|
|
3717
4267
|
});
|
|
3718
4268
|
var isInvalid = parentRule !== undefined || nameListStr.some(function (name) {
|
|
3719
|
-
return name.trim().match(
|
|
4269
|
+
return name.trim().match(cssCustomIdentifierRegExp) === null;
|
|
3720
4270
|
});
|
|
3721
4271
|
|
|
3722
4272
|
if (!isInvalid) {
|
|
@@ -3943,6 +4493,10 @@ CSSOM.parse = function parse(token, opts, errorHandler) {
|
|
|
3943
4493
|
}
|
|
3944
4494
|
}
|
|
3945
4495
|
|
|
4496
|
+
if (buffer.trim() !== "") {
|
|
4497
|
+
parseError("Unexpected end of input");
|
|
4498
|
+
}
|
|
4499
|
+
|
|
3946
4500
|
return styleSheet;
|
|
3947
4501
|
};
|
|
3948
4502
|
|