From 9f7e335cfcbfa4c44b7088fd97d8e6677fe78c45 Mon Sep 17 00:00:00 2001 From: Alexey Kurnev Date: Mon, 14 Oct 2024 18:38:15 +0300 Subject: [PATCH] Support for @layer CSS-rule (#5) --- .changeset/simpleman383-layer-1.md | 5 +++ lib/CSSLayerBlockRule.js | 48 +++++++++++++++++++++++++ lib/CSSRule.js | 17 ++++----- lib/clone.js | 7 +++- lib/index.js | 1 + lib/parse.js | 27 ++++++++++++-- spec/parse.spec.js | 58 +++++++++++++++++++++++++++++- src/files.js | 1 + 8 files changed, 149 insertions(+), 15 deletions(-) create mode 100644 .changeset/simpleman383-layer-1.md create mode 100644 lib/CSSLayerBlockRule.js diff --git a/.changeset/simpleman383-layer-1.md b/.changeset/simpleman383-layer-1.md new file mode 100644 index 0000000..2ebff72 --- /dev/null +++ b/.changeset/simpleman383-layer-1.md @@ -0,0 +1,5 @@ +--- +"rrweb-cssom": minor +--- + +Added support for @layer CSS rule diff --git a/lib/CSSLayerBlockRule.js b/lib/CSSLayerBlockRule.js new file mode 100644 index 0000000..0095005 --- /dev/null +++ b/lib/CSSLayerBlockRule.js @@ -0,0 +1,48 @@ +//.CommonJS +var CSSOM = { + CSSRule: require("./CSSRule").CSSRule, + CSSGroupingRule: require("./CSSGroupingRule").CSSGroupingRule, +}; +///CommonJS + +/** + * @constructor + * @see https://drafts.csswg.org/css-cascade-5/#csslayerblockrule + */ +CSSOM.CSSLayerBlockRule = function CSSLayerBlockRule() { + CSSOM.CSSGroupingRule.call(this); + this.layerName = ""; + this.cssRules = []; +}; + +CSSOM.CSSLayerBlockRule.prototype = new CSSOM.CSSGroupingRule(); +CSSOM.CSSLayerBlockRule.prototype.constructor = CSSOM.CSSLayerBlockRule; +CSSOM.CSSLayerBlockRule.prototype.type = 18; + +Object.defineProperties(CSSOM.CSSLayerBlockRule.prototype, { + layerNameText: { + get: function () { + return this.layerName; + }, + set: function (value) { + this.layerName = value; + }, + configurable: true, + enumerable: true, + }, + cssText: { + get: function () { + var cssTexts = []; + for (var i = 0, length = this.cssRules.length; i < length; i++) { + cssTexts.push(this.cssRules[i].cssText); + } + return "@layer " + this.layerNameText + " {" + cssTexts.join("") + "}"; + }, + configurable: true, + enumerable: true, + }, +}); + +//.CommonJS +exports.CSSLayerBlockRule = CSSOM.CSSLayerBlockRule; +///CommonJS diff --git a/lib/CSSRule.js b/lib/CSSRule.js index 0c095c1..4f26398 100644 --- a/lib/CSSRule.js +++ b/lib/CSSRule.js @@ -2,20 +2,19 @@ var CSSOM = {}; ///CommonJS - /** * @constructor * @see http://dev.w3.org/csswg/cssom/#the-cssrule-interface * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule */ CSSOM.CSSRule = function CSSRule() { - this.parentRule = null; - this.parentStyleSheet = null; + this.parentRule = null; + this.parentStyleSheet = null; }; -CSSOM.CSSRule.UNKNOWN_RULE = 0; // obsolete +CSSOM.CSSRule.UNKNOWN_RULE = 0; // obsolete CSSOM.CSSRule.STYLE_RULE = 1; -CSSOM.CSSRule.CHARSET_RULE = 2; // obsolete +CSSOM.CSSRule.CHARSET_RULE = 2; // obsolete CSSOM.CSSRule.IMPORT_RULE = 3; CSSOM.CSSRule.MEDIA_RULE = 4; CSSOM.CSSRule.FONT_FACE_RULE = 5; @@ -31,15 +30,13 @@ CSSOM.CSSRule.FONT_FEATURE_VALUES_RULE = 14; CSSOM.CSSRule.VIEWPORT_RULE = 15; CSSOM.CSSRule.REGION_STYLE_RULE = 16; CSSOM.CSSRule.CONTAINER_RULE = 17; +CSSOM.CSSRule.LAYER_BLOCK_RULE = 18; CSSOM.CSSRule.STARTING_STYLE_RULE = 1002; - CSSOM.CSSRule.prototype = { - constructor: CSSOM.CSSRule - //FIXME + constructor: CSSOM.CSSRule, + //FIXME }; - -//.CommonJS exports.CSSRule = CSSOM.CSSRule; ///CommonJS diff --git a/lib/clone.js b/lib/clone.js index 9c5e6aa..c774b51 100644 --- a/lib/clone.js +++ b/lib/clone.js @@ -10,7 +10,8 @@ var CSSOM = { CSSSupportsRule: require("./CSSSupportsRule").CSSSupportsRule, CSSStyleDeclaration: require("./CSSStyleDeclaration").CSSStyleDeclaration, CSSKeyframeRule: require('./CSSKeyframeRule').CSSKeyframeRule, - CSSKeyframesRule: require('./CSSKeyframesRule').CSSKeyframesRule + CSSKeyframesRule: require('./CSSKeyframesRule').CSSKeyframesRule, + CSSLayerBlockRule: require('./CSSLayerBlockRule').CSSLayerBlockRule }; ///CommonJS @@ -61,6 +62,10 @@ CSSOM.clone = function clone(stylesheet) { ruleClone.conditionText = rule.conditionText; } + if (rule.hasOwnProperty('layerName')) { + ruleClone.layerName = rule.layerName; + } + if (rule.hasOwnProperty('cssRules')) { ruleClone.cssRules = clone(rule).cssRules; } diff --git a/lib/index.js b/lib/index.js index 5132203..0517e88 100644 --- a/lib/index.js +++ b/lib/index.js @@ -21,5 +21,6 @@ exports.MatcherList = require('./MatcherList').MatcherList; exports.CSSDocumentRule = require('./CSSDocumentRule').CSSDocumentRule; exports.CSSValue = require('./CSSValue').CSSValue; exports.CSSValueExpression = require('./CSSValueExpression').CSSValueExpression; +exports.CSSLayerBlockRule = require('./CSSLayerBlockRule').CSSLayerBlockRule; exports.parse = require('./parse').parse; exports.clone = require('./clone').clone; diff --git a/lib/parse.js b/lib/parse.js index dfade12..01db50d 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -37,7 +37,8 @@ CSSOM.parse = function parse(token) { "atBlock": true, "containerBlock": true, "conditionBlock": true, - 'documentRule-begin': true + 'documentRule-begin': true, + "layerBlock": true }; var styleSheet = new CSSOM.CSSStyleSheet(); @@ -52,7 +53,7 @@ CSSOM.parse = function parse(token) { var hasAncestors = false; var prevScope; - var name, priority="", styleRule, mediaRule, containerRule, supportsRule, importRule, fontFaceRule, keyframesRule, documentRule, hostRule, startingStyleRule; + var name, priority="", styleRule, mediaRule, containerRule, supportsRule, importRule, fontFaceRule, keyframesRule, documentRule, hostRule, startingStyleRule, layerBlockRule; var atKeyframesRegExp = /@(-(?:\w+-)+)?keyframes/g; @@ -165,7 +166,14 @@ CSSOM.parse = function parse(token) { i += "container".length; buffer = ""; break; - } else if (token.indexOf("@supports", i) === i) { + } else if (token.indexOf("@layer", i) === i) { + state = "layerBlock" + layerBlockRule = new CSSOM.CSSLayerBlockRule(); + layerBlockRule.__starts = i; + i += "layer".length; + buffer = ""; + break; + } else if (token.indexOf("@supports", i) === i) { state = "conditionBlock"; supportsRule = new CSSOM.CSSSupportsRule(); supportsRule.__starts = i; @@ -254,6 +262,17 @@ CSSOM.parse = function parse(token) { supportsRule.parentStyleSheet = styleSheet; buffer = ""; state = "before-selector"; + } else if (state === "layerBlock") { + layerBlockRule.layerNameText = buffer.trim(); + + if (parentRule) { + ancestorRules.push(parentRule); + } + + currentScope = parentRule = layerBlockRule; + layerBlockRule.parentStyleSheet = styleSheet; + buffer = ""; + state = "before-selector"; } else if (state === "hostRule-begin") { if (parentRule) { ancestorRules.push(parentRule); @@ -430,6 +449,7 @@ CSSOM.parse = function parse(token) { parentRule.constructor.name === "CSSMediaRule" || parentRule.constructor.name === "CSSSupportsRule" || parentRule.constructor.name === "CSSContainerRule" + || parentRule.constructor.name === "CSSLayerBlockRule" || parentRule.constructor.name === "CSSStartingStyleRule" ) { prevScope = currentScope; @@ -501,4 +521,5 @@ CSSOM.CSSKeyframeRule = require('./CSSKeyframeRule').CSSKeyframeRule; CSSOM.CSSKeyframesRule = require('./CSSKeyframesRule').CSSKeyframesRule; CSSOM.CSSValueExpression = require('./CSSValueExpression').CSSValueExpression; CSSOM.CSSDocumentRule = require('./CSSDocumentRule').CSSDocumentRule; +CSSOM.CSSLayerBlockRule = require("./CSSLayerBlockRule").CSSLayerBlockRule; ///CommonJS diff --git a/spec/parse.spec.js b/spec/parse.spec.js index 9691316..e9563b5 100644 --- a/spec/parse.spec.js +++ b/spec/parse.spec.js @@ -1402,7 +1402,63 @@ var TESTS = [ return result; })() - } + }, + { + input: "@layer custom-layer { div { display: block; } }", + result: (function() { + var result = { + cssRules: [ + { + layerName: "custom-layer", + cssRules: [ + { + selectorText: "div", + style: { + 0: "display", + display: "block", + length: 1 + }, + } + ], + parentRule: null, + } + ], + parentStyleSheet: null + }; + result.cssRules[0].parentStyleSheet = result.cssRules[0].cssRules[0].parentStyleSheet = result; + result.cssRules[0].cssRules[0].parentRule = result.cssRules[0]; + result.cssRules[0].cssRules[0].style.parentRule = result.cssRules[0].cssRules[0]; + return result; + })() + }, + { + input: "@layer { div { display: block; } }", + result: (function() { + var result = { + cssRules: [ + { + layerName: "", + cssRules: [ + { + selectorText: "div", + style: { + 0: "display", + display: "block", + length: 1 + }, + } + ], + parentRule: null, + } + ], + parentStyleSheet: null + }; + result.cssRules[0].parentStyleSheet = result.cssRules[0].cssRules[0].parentStyleSheet = result; + result.cssRules[0].cssRules[0].parentRule = result.cssRules[0]; + result.cssRules[0].cssRules[0].style.parentRule = result.cssRules[0].cssRules[0]; + return result; + })() + }, ]; diff --git a/src/files.js b/src/files.js index 5368f1d..fe2eea4 100644 --- a/src/files.js +++ b/src/files.js @@ -16,6 +16,7 @@ exports.files = [ "CSSStyleSheet", "CSSKeyframesRule", "CSSKeyframeRule", + "CSSLayerBlockRule", "MatcherList", "CSSDocumentRule", "CSSValue",