diff --git a/cjs/html/style-element.js b/cjs/html/style-element.js index 78cf8a0f..e4bc2b11 100644 --- a/cjs/html/style-element.js +++ b/cjs/html/style-element.js @@ -1,5 +1,5 @@ 'use strict'; -const {parse} = require('@acemir/cssom'); +const {parse} = require('cssomnom/parse'); const {registerHTMLClass} = require('../shared/register-html-class.js'); const {SHEET} = require('../shared/symbols.js'); diff --git a/esm/html/style-element.js b/esm/html/style-element.js index 9f23dde0..59b36160 100644 --- a/esm/html/style-element.js +++ b/esm/html/style-element.js @@ -1,4 +1,4 @@ -import {parse} from '@acemir/cssom'; +import {parse} from 'cssomnom/parse'; import {registerHTMLClass} from '../shared/register-html-class.js'; import {SHEET} from '../shared/symbols.js'; diff --git a/package-lock.json b/package-lock.json index 0c7a320b..43d2b641 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,8 @@ "version": "0.18.14", "license": "ISC", "dependencies": { - "@acemir/cssom": "^0.9.32", "css-select": "^7.0.0", + "cssomnom": "^0.1.11", "html-escaper": "^3.0.3", "htmlparser2": "^10.1.0", "uhyphen": "^0.2.0" @@ -37,12 +37,6 @@ } } }, - "node_modules/@acemir/cssom": { - "version": "0.9.32", - "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.32.tgz", - "integrity": "sha512-OijrAUu7rrJhSIoHNg6LMPS5e344nwUHA+1EFYYjrh64D+GmytUsfBQjci45TjT+rd5Spsp8B5xsZDT+TgyXXQ==", - "license": "MIT" - }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -1104,6 +1098,15 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/cssomnom": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/cssomnom/-/cssomnom-0.1.11.tgz", + "integrity": "sha512-P4RkO1Sq2Ual77xypEmAc4MkM5k2Ha4UwtBPCOpJmXculGCYPgzsU/xnOCy084esZaPePgGkkJg4Il/HE7GS5A==", + "license": "Apache-2.0", + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", diff --git a/package.json b/package.json index e370264d..7f20e5e8 100644 --- a/package.json +++ b/package.json @@ -70,8 +70,8 @@ } }, "dependencies": { - "@acemir/cssom": "^0.9.32", "css-select": "^7.0.0", + "cssomnom": "^0.1.11", "html-escaper": "^3.0.3", "htmlparser2": "^10.1.0", "uhyphen": "^0.2.0" diff --git a/worker.js b/worker.js index 31bb78a1..2c72ca4c 100644 --- a/worker.js +++ b/worker.js @@ -757,7 +757,7 @@ const Sequences = { ]), // ` { const end = self[END]; @@ -2276,7 +2276,7 @@ const parseFromString = (document, isHTML, markupLanguage) => { let ownerSVGElement = null; let parsingCData = false; - const content = new Parser({ + const content = new Parser$1({ // onprocessinginstruction(name, data) { if (name.toLowerCase() === '!doctype') @@ -8631,7432 +8631,9426 @@ class HTMLBodyElement extends HTMLElement { } } -var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; - -var lib = {}; - -var CSSOM_1; -var hasRequiredCSSOM; - -function requireCSSOM () { - if (hasRequiredCSSOM) return CSSOM_1; - hasRequiredCSSOM = 1; - var CSSOM = { - /** - * Creates and configures a new CSSOM instance with the specified options. - * - * @param {Object} opts - Configuration options for the CSSOM instance - * @param {Object} [opts.globalObject] - Optional global object to be assigned to CSSOM objects prototype - * @returns {Object} A new CSSOM instance with the applied configuration - * @description - * This method creates a new instance of CSSOM and optionally - * configures CSSStyleSheet with a global object reference. When a globalObject is provided - * and CSSStyleSheet exists on the instance, it creates a new CSSStyleSheet constructor - * using a factory function and assigns the globalObject to its prototype's __globalObject property. - */ - setup: function (opts) { - var instance = Object.create(this); - if (opts.globalObject) { - if (instance.CSSStyleSheet) { - var factoryCSSStyleSheet = createFunctionFactory(instance.CSSStyleSheet); - var CSSStyleSheet = factoryCSSStyleSheet(); - CSSStyleSheet.prototype.__globalObject = opts.globalObject; - - instance.CSSStyleSheet = CSSStyleSheet; - } - } - return instance; - } - }; - - function createFunctionFactory(fn) { - return function() { - // Create a new function that delegates to the original - var newFn = function() { - return fn.apply(this, arguments); - }; - - // Copy prototype chain - Object.setPrototypeOf(newFn, Object.getPrototypeOf(fn)); - - // Copy own properties - for (var key in fn) { - if (Object.prototype.hasOwnProperty.call(fn, key)) { - newFn[key] = fn[key]; - } - } - - // Clone the .prototype object for constructor-like behavior - if (fn.prototype) { - newFn.prototype = Object.create(fn.prototype); - } - - return newFn; - }; +// @ts-nocheck +// src/data/gen/property-list.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +const SUPPORTED_PROPERTIES = /* @__PURE__ */ new Set("-webkit-align-content,-webkit-align-items,-webkit-align-self,-webkit-animation,-webkit-animation-delay,-webkit-animation-direction,-webkit-animation-duration,-webkit-animation-fill-mode,-webkit-animation-iteration-count,-webkit-animation-name,-webkit-animation-play-state,-webkit-animation-timing-function,-webkit-appearance,-webkit-backface-visibility,-webkit-background-clip,-webkit-background-origin,-webkit-background-size,-webkit-border-bottom-left-radius,-webkit-border-bottom-right-radius,-webkit-border-radius,-webkit-border-top-left-radius,-webkit-border-top-right-radius,-webkit-box-align,-webkit-box-flex,-webkit-box-ordinal-group,-webkit-box-orient,-webkit-box-pack,-webkit-box-shadow,-webkit-box-sizing,-webkit-filter,-webkit-flex,-webkit-flex-basis,-webkit-flex-direction,-webkit-flex-flow,-webkit-flex-grow,-webkit-flex-shrink,-webkit-flex-wrap,-webkit-justify-content,-webkit-line-clamp,-webkit-mask,-webkit-mask-box-image,-webkit-mask-box-image-outset,-webkit-mask-box-image-repeat,-webkit-mask-box-image-slice,-webkit-mask-box-image-source,-webkit-mask-box-image-width,-webkit-mask-clip,-webkit-mask-composite,-webkit-mask-image,-webkit-mask-origin,-webkit-mask-position,-webkit-mask-repeat,-webkit-mask-size,-webkit-order,-webkit-perspective,-webkit-perspective-origin,-webkit-text-fill-color,-webkit-text-size-adjust,-webkit-text-stroke,-webkit-text-stroke-color,-webkit-text-stroke-width,-webkit-transform,-webkit-transform-origin,-webkit-transform-style,-webkit-transition,-webkit-transition-delay,-webkit-transition-duration,-webkit-transition-property,-webkit-transition-timing-function,-webkit-user-select,accent-color,align-content,align-items,align-self,alignment-baseline,all,anchor-name,anchor-scope,animation,animation-composition,animation-delay,animation-delay-end,animation-delay-start,animation-direction,animation-duration,animation-fill-mode,animation-iteration-count,animation-name,animation-play-state,animation-range,animation-range-center,animation-range-end,animation-range-start,animation-timeline,animation-timing-function,animation-trigger,appearance,aspect-ratio,backdrop-filter,backface-visibility,background,background-attachment,background-blend-mode,background-clip,background-color,background-image,background-origin,background-position,background-position-block,background-position-inline,background-position-x,background-position-y,background-repeat,background-repeat-block,background-repeat-inline,background-repeat-x,background-repeat-y,background-size,background-tbd,baseline-shift,baseline-source,block-ellipsis,block-size,block-step,block-step-align,block-step-insert,block-step-round,block-step-size,bookmark-label,bookmark-level,bookmark-state,border,border-block,border-block-clip,border-block-color,border-block-end,border-block-end-clip,border-block-end-color,border-block-end-radius,border-block-end-style,border-block-end-width,border-block-start,border-block-start-clip,border-block-start-color,border-block-start-radius,border-block-start-style,border-block-start-width,border-block-style,border-block-width,border-bottom,border-bottom-clip,border-bottom-color,border-bottom-left-radius,border-bottom-radius,border-bottom-right-radius,border-bottom-style,border-bottom-width,border-boundary,border-clip,border-collapse,border-color,border-end-end-radius,border-end-start-radius,border-image,border-image-outset,border-image-repeat,border-image-slice,border-image-source,border-image-width,border-inline,border-inline-clip,border-inline-color,border-inline-end,border-inline-end-clip,border-inline-end-color,border-inline-end-radius,border-inline-end-style,border-inline-end-width,border-inline-start,border-inline-start-clip,border-inline-start-color,border-inline-start-radius,border-inline-start-style,border-inline-start-width,border-inline-style,border-inline-width,border-left,border-left-clip,border-left-color,border-left-radius,border-left-style,border-left-width,border-limit,border-radius,border-right,border-right-clip,border-right-color,border-right-radius,border-right-style,border-right-width,border-shape,border-spacing,border-start-end-radius,border-start-start-radius,border-style,border-top,border-top-clip,border-top-color,border-top-left-radius,border-top-radius,border-top-right-radius,border-top-style,border-top-width,border-width,bottom,box-decoration-break,box-shadow,box-shadow-blur,box-shadow-color,box-shadow-offset,box-shadow-position,box-shadow-spread,box-sizing,box-snap,break-after,break-before,break-inside,caption-side,caret,caret-animation,caret-color,caret-shape,clear,clip,clip-path,clip-rule,color,color-adjust,color-interpolation,color-interpolation-filters,color-scheme,column-count,column-fill,column-gap,column-height,column-rule,column-rule-break,column-rule-color,column-rule-inset,column-rule-inset-cap,column-rule-inset-cap-end,column-rule-inset-cap-start,column-rule-inset-end,column-rule-inset-junction,column-rule-inset-junction-end,column-rule-inset-junction-start,column-rule-inset-start,column-rule-style,column-rule-visibility-items,column-rule-width,column-span,column-width,column-wrap,columns,contain,contain-intrinsic-block-size,contain-intrinsic-height,contain-intrinsic-inline-size,contain-intrinsic-size,contain-intrinsic-width,container,container-name,container-type,content,content-visibility,continue,copy-into,corner,corner-block-end,corner-block-end-shape,corner-block-start,corner-block-start-shape,corner-bottom,corner-bottom-left,corner-bottom-left-shape,corner-bottom-right,corner-bottom-right-shape,corner-bottom-shape,corner-end-end,corner-end-end-shape,corner-end-start,corner-end-start-shape,corner-inline-end,corner-inline-end-shape,corner-inline-start,corner-inline-start-shape,corner-left,corner-left-shape,corner-right,corner-right-shape,corner-shape,corner-start-end,corner-start-end-shape,corner-start-start,corner-start-start-shape,corner-top,corner-top-left,corner-top-left-shape,corner-top-right,corner-top-right-shape,corner-top-shape,counter-increment,counter-reset,counter-set,cue,cue-after,cue-before,cursor,cx,cy,d,direction,display,dominant-baseline,dynamic-range-limit,empty-cells,event-trigger,event-trigger-name,event-trigger-source,field-sizing,fill,fill-break,fill-color,fill-image,fill-opacity,fill-origin,fill-position,fill-repeat,fill-rule,fill-size,filter,fit-tolerance,flex,flex-basis,flex-direction,flex-flow,flex-grow,flex-line-count,flex-shrink,flex-wrap,float,float-defer,float-offset,float-reference,flood-color,flood-opacity,flow-from,flow-into,font,font-family,font-feature-settings,font-kerning,font-language-override,font-optical-sizing,font-palette,font-presentation,font-size,font-size-adjust,font-stretch,font-style,font-synthesis,font-synthesis-position,font-synthesis-small-caps,font-synthesis-style,font-synthesis-weight,font-variant,font-variant-alternates,font-variant-caps,font-variant-east-asian,font-variant-emoji,font-variant-ligatures,font-variant-numeric,font-variant-position,font-variation-settings,font-weight,font-width,footnote-display,footnote-policy,forced-color-adjust,frame-sizing,gap,glyph-orientation-vertical,grid,grid-area,grid-auto-columns,grid-auto-flow,grid-auto-rows,grid-column,grid-column-end,grid-column-gap,grid-column-start,grid-gap,grid-row,grid-row-end,grid-row-gap,grid-row-start,grid-template,grid-template-areas,grid-template-columns,grid-template-rows,hanging-punctuation,height,hyphenate-character,hyphenate-limit-chars,hyphenate-limit-last,hyphenate-limit-lines,hyphenate-limit-zone,hyphens,image-animation,image-orientation,image-rendering,image-resolution,initial-letter,initial-letter-align,initial-letter-wrap,inline-size,inline-sizing,input-security,inset,inset-block,inset-block-end,inset-block-start,inset-inline,inset-inline-end,inset-inline-start,interactivity,interest-delay,interest-delay-end,interest-delay-start,interpolate-size,isolation,justify-content,justify-items,justify-self,left,letter-spacing,lighting-color,line-break,line-clamp,line-fit-edge,line-grid,line-height,line-height-step,line-padding,line-snap,link-parameters,list-style,list-style-image,list-style-position,list-style-type,margin,margin-block,margin-block-end,margin-block-start,margin-bottom,margin-break,margin-inline,margin-inline-end,margin-inline-start,margin-left,margin-right,margin-top,margin-trim,marker,marker-end,marker-mid,marker-side,marker-start,mask,mask-border,mask-border-mode,mask-border-outset,mask-border-repeat,mask-border-slice,mask-border-source,mask-border-width,mask-clip,mask-composite,mask-image,mask-mode,mask-origin,mask-position,mask-repeat,mask-size,mask-type,math-depth,math-shift,math-style,max-block-size,max-height,max-inline-size,max-lines,max-size,max-width,min-block-size,min-height,min-inline-size,min-intrinsic-sizing,min-size,min-width,mix-blend-mode,nav-down,nav-left,nav-right,nav-up,object-fit,object-position,object-view-box,offset,offset-anchor,offset-distance,offset-path,offset-position,offset-rotate,opacity,order,orphans,outline,outline-color,outline-offset,outline-style,outline-width,overflow,overflow-anchor,overflow-block,overflow-clip-margin,overflow-clip-margin-block,overflow-clip-margin-block-end,overflow-clip-margin-block-start,overflow-clip-margin-bottom,overflow-clip-margin-inline,overflow-clip-margin-inline-end,overflow-clip-margin-inline-start,overflow-clip-margin-left,overflow-clip-margin-right,overflow-clip-margin-top,overflow-inline,overflow-wrap,overflow-x,overflow-y,overlay,overscroll-behavior,overscroll-behavior-block,overscroll-behavior-inline,overscroll-behavior-x,overscroll-behavior-y,padding,padding-block,padding-block-end,padding-block-start,padding-bottom,padding-inline,padding-inline-end,padding-inline-start,padding-left,padding-right,padding-top,page,page-break-after,page-break-before,page-break-inside,paint-order,path-length,pause,pause-after,pause-before,perspective,perspective-origin,place-content,place-items,place-self,pointer-events,pointer-timeline,pointer-timeline-axis,pointer-timeline-name,position,position-anchor,position-area,position-try,position-try-fallbacks,position-try-order,position-visibility,print-color-adjust,quotes,r,reading-flow,reading-order,region-fragment,resize,rest,rest-after,rest-before,right,rotate,row-gap,row-rule,row-rule-break,row-rule-color,row-rule-inset,row-rule-inset-cap,row-rule-inset-cap-end,row-rule-inset-cap-start,row-rule-inset-end,row-rule-inset-junction,row-rule-inset-junction-end,row-rule-inset-junction-start,row-rule-inset-start,row-rule-style,row-rule-visibility-items,row-rule-width,ruby-align,ruby-merge,ruby-overhang,ruby-position,rule,rule-break,rule-color,rule-inset,rule-inset-cap,rule-inset-end,rule-inset-junction,rule-inset-start,rule-overlap,rule-style,rule-visibility-items,rule-width,rx,ry,scale,scroll-axis-lock,scroll-behavior,scroll-initial-target,scroll-margin,scroll-margin-block,scroll-margin-block-end,scroll-margin-block-start,scroll-margin-bottom,scroll-margin-inline,scroll-margin-inline-end,scroll-margin-inline-start,scroll-margin-left,scroll-margin-right,scroll-margin-top,scroll-marker-group,scroll-padding,scroll-padding-block,scroll-padding-block-end,scroll-padding-block-start,scroll-padding-bottom,scroll-padding-inline,scroll-padding-inline-end,scroll-padding-inline-start,scroll-padding-left,scroll-padding-right,scroll-padding-top,scroll-snap-align,scroll-snap-stop,scroll-snap-type,scroll-target-group,scroll-timeline,scroll-timeline-axis,scroll-timeline-name,scrollbar-color,scrollbar-gutter,scrollbar-width,shape-image-threshold,shape-inside,shape-margin,shape-outside,shape-padding,shape-rendering,size,slider-orientation,spatial-navigation-action,spatial-navigation-contain,spatial-navigation-function,speak,speak-as,stop-color,stop-opacity,string-set,stroke,stroke-align,stroke-alignment,stroke-break,stroke-color,stroke-dash-corner,stroke-dash-justify,stroke-dashadjust,stroke-dasharray,stroke-dashcorner,stroke-dashoffset,stroke-image,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-origin,stroke-position,stroke-repeat,stroke-size,stroke-width,tab-size,table-layout,text-align,text-align-all,text-align-last,text-anchor,text-autospace,text-box,text-box-edge,text-box-trim,text-combine-upright,text-decoration,text-decoration-color,text-decoration-inset,text-decoration-line,text-decoration-skip,text-decoration-skip-box,text-decoration-skip-ink,text-decoration-skip-self,text-decoration-skip-spaces,text-decoration-style,text-decoration-thickness,text-emphasis,text-emphasis-color,text-emphasis-position,text-emphasis-skip,text-emphasis-style,text-fit,text-group-align,text-indent,text-justify,text-orientation,text-overflow,text-rendering,text-shadow,text-size-adjust,text-spacing,text-spacing-trim,text-transform,text-underline-offset,text-underline-position,text-wrap,text-wrap-mode,text-wrap-style,timeline-scope,timeline-trigger,timeline-trigger-activation-range,timeline-trigger-activation-range-end,timeline-trigger-activation-range-start,timeline-trigger-active-range,timeline-trigger-active-range-end,timeline-trigger-active-range-start,timeline-trigger-name,timeline-trigger-range,timeline-trigger-source,top,touch-action,transform,transform-box,transform-origin,transform-style,transition,transition-behavior,transition-delay,transition-duration,transition-property,transition-timing-function,translate,trigger-scope,unicode-bidi,user-select,vector-effect,vertical-align,view-timeline,view-timeline-axis,view-timeline-inset,view-timeline-name,view-transition-class,view-transition-group,view-transition-name,view-transition-scope,visibility,voice-balance,voice-duration,voice-family,voice-pitch,voice-range,voice-rate,voice-stress,voice-volume,white-space,white-space-collapse,white-space-trim,widows,width,will-change,window-drag,word-break,word-space-transform,word-spacing,word-wrap,wrap-after,wrap-before,wrap-flow,wrap-inside,wrap-through,writing-mode,x,y,z-index,zoom".split(",")); +const CUSTOM_IDENT_PROPERTIES = /* @__PURE__ */ new Set("-webkit-animation,-webkit-animation-name,-webkit-transition,-webkit-transition-property,animation,animation-name,color-scheme,container,container-name,copy-into,counter-increment,counter-reset,counter-set,flow-from,flow-into,font,font-family,grid,grid-area,grid-column,grid-column-end,grid-column-start,grid-row,grid-row-end,grid-row-start,grid-template,grid-template-columns,grid-template-rows,list-style,list-style-type,page,string-set,transition,transition-property,view-transition-class,view-transition-group,view-transition-name,will-change".split(",")); +const FONT_FACE_DESCRIPTORS = /* @__PURE__ */ new Set("ascent-override,descent-override,font-display,font-family,font-feature-settings,font-language-override,font-named-instance,font-size,font-stretch,font-style,font-variant,font-variation-settings,font-weight,font-width,line-gap-override,size-adjust,src,subscript-position-override,subscript-size-override,superscript-position-override,superscript-size-override,unicode-range".split(",")); +const PAGE_ALLOWED_PROPERTIES = /* @__PURE__ */ new Set("bleed,break-after,break-before,break-inside,counter-increment,counter-reset,counter-set,margin,margin-block,margin-block-end,margin-block-start,margin-bottom,margin-inline,margin-inline-end,margin-inline-start,margin-left,margin-right,margin-top,marks,orphans,page,page-break-after,page-break-before,page-break-inside,page-margin-safety,page-orientation,quotes,size,widows".split(",")); +const PAGE_DESCRIPTOR_PROPERTIES = [ + "margin", + "margin-top", + "margin-right", + "margin-bottom", + "margin-left", + "size", + "page-orientation", + "marks", + "bleed" +]; +const POSITION_TRY_ALLOWED_PROPERTIES = /* @__PURE__ */ new Set("align-self,block-size,bottom,height,inline-size,inset,inset-area,inset-block,inset-block-end,inset-block-start,inset-inline,inset-inline-end,inset-inline-start,justify-self,left,margin,margin-block,margin-block-end,margin-block-start,margin-bottom,margin-inline,margin-inline-end,margin-inline-start,margin-left,margin-right,margin-top,max-block-size,max-height,max-inline-size,max-width,min-block-size,min-height,min-inline-size,min-width,place-self,position-anchor,position-area,right,top,width".split(",")); +const COUNTER_STYLE_PROP_BY_DESCRIPTOR = /* @__PURE__ */ new Map([ + ["additive-symbols", "additiveSymbols"], + ["fallback", "fallback"], + ["negative", "negative"], + ["pad", "pad"], + ["prefix", "prefix"], + ["range", "range"], + ["speak-as", "speakAs"], + ["suffix", "suffix"], + ["symbols", "symbols"], + ["system", "system"] +]); +const FONT_FEATURE_VALUE_MAP_DEFS = [ + ["annotation", "annotation"], + ["ornaments", "ornaments"], + ["stylistic", "stylistic"], + ["swash", "swash"], + ["character-variant", "characterVariant"], + ["styleset", "styleset"], + ["historical-forms", "historicalForms"] +]; +const FONT_FEATURE_VALUE_BLOCKS = { + "annotation": "annotation", + "ornaments": "ornaments", + "stylistic": "stylistic", + "swash": "swash", + "character-variant": "characterVariant", + "charactervariant": "characterVariant", + "styleset": "styleset", + "historical-forms": "historicalForms", + "historicalforms": "historicalForms" +}; +const MARGIN_RULE_NAMES = /* @__PURE__ */ new Set("bottom-center,bottom-left,bottom-left-corner,bottom-right,bottom-right-corner,left-bottom,left-middle,left-top,right-bottom,right-middle,right-top,top-center,top-left,top-left-corner,top-right,top-right-corner".split(",")); +const GENERIC_FONT_FAMILIES = /* @__PURE__ */ new Set("cursive,emoji,fangsong,fantasy,math,monospace,sans-serif,serif,system-ui,ui-monospace,ui-rounded,ui-sans-serif,ui-serif".split(",")); +const FONT_VARIANT_LIGATURES_KEYWORDS = /* @__PURE__ */ new Set("common-ligatures,no-common-ligatures,discretionary-ligatures,no-discretionary-ligatures,historical-ligatures,no-historical-ligatures,contextual,no-contextual".split(",")); +const FONT_VARIANT_CAPS_KEYWORDS = /* @__PURE__ */ new Set("small-caps,all-small-caps,petite-caps,all-petite-caps,unicase,titling-caps".split(",")); +const FONT_VARIANT_NUMERIC_KEYWORDS = /* @__PURE__ */ new Set("lining-nums,oldstyle-nums,proportional-nums,tabular-nums,diagonal-fractions,stacked-fractions,ordinal,slashed-zero".split(",")); +const FONT_VARIANT_EAST_ASIAN_KEYWORDS = /* @__PURE__ */ new Set("jis78,jis83,jis90,jis04,simplified,traditional,full-width,proportional-width,ruby".split(",")); +const FONT_VARIANT_POSITION_KEYWORDS = /* @__PURE__ */ new Set("sub,super".split(",")); +const FONT_VARIANT_EMOJI_KEYWORDS = /* @__PURE__ */ new Set("text,emoji,unicode".split(",")); +const FONT_VARIANT_ALTERNATES_FUNCTIONS = /* @__PURE__ */ new Set("stylistic,styleset,character-variant,swash,ornaments,annotation".split(",")); +const SYSTEM_FONT_KEYWORDS = /* @__PURE__ */ new Set("initial,inherit,unset,revert,revert-layer,default,caption,icon,menu,message-box,small-caption,status-bar".split(",")); +const FONT_STRETCH_KEYWORDS = /* @__PURE__ */ new Set("ultra-condensed,extra-condensed,condensed,semi-condensed,semi-expanded,expanded,extra-expanded,ultra-expanded".split(",")); +const FONT_SIZE_KEYWORDS = /* @__PURE__ */ new Set("xx-small,x-small,small,medium,large,x-large,xx-large,xxx-large,smaller,larger".split(",")); +const FLEX_BASIS_KEYWORDS = /* @__PURE__ */ new Set("auto,content,max-content,min-content,fit-content".split(",")); +// src/webidl.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +function hideInternalFields(instance) { + for (const key of Object.getOwnPropertyNames(instance)) if (key.startsWith("_")) Object.defineProperty(instance, key, { enumerable: false }); +} +const appliedCtors = /* @__PURE__ */ new WeakSet(); +/** +* Aligns regular prototype properties of a WebIDL interface object with specification requirements: +* 1. Interface prototype members are { enumerable: true, configurable: true }. +* 2. Attribute getters/setters perform brand checks, throwing TypeError when invoked on the prototype +* itself or on an incompatible receiver (WebIDL § 3.6.1, § 3.6.2). +* 3. Operations are { writable: true, enumerable: true, configurable: true } and throw TypeError +* when invoked with fewer arguments than required (WebIDL § 3.7). +*/ +function applyWebIDLInterface(ctor, options = {}) { + const proto = ctor.prototype; + if (!proto) return; + if (appliedCtors.has(ctor)) return; + appliedCtors.add(ctor); + const brandCheck = options.brandCheck ?? true; + const arityCheck = options.arityCheck ?? true; + const interfaceName = options.interfaceName || ctor.name; + const excludeSet = new Set(options.exclude || []); + const rejectSet = new Set(options.rejectOperations || []); + Object.defineProperty(ctor, "length", { + value: 0, + writable: false, + enumerable: false, + configurable: true + }); + Object.defineProperty(proto, Symbol.toStringTag, { + value: interfaceName, + writable: false, + enumerable: false, + configurable: true + }); + const names = Object.getOwnPropertyNames(proto); + for (const name of names) { + if (name === "constructor" || name.startsWith("_") || excludeSet.has(name)) continue; + const desc = Object.getOwnPropertyDescriptor(proto, name); + if (!desc) continue; + if (desc.get || desc.set) { + const origGet = desc.get; + const origSet = desc.set; + let get = origGet; + if (origGet && brandCheck) { + get = function() { + if (!this || this === proto || !(this instanceof ctor)) throw new TypeError(`Failed to read the '${name}' property from '${interfaceName}': The provided value is not of type '${interfaceName}'.`); + return origGet.call(this); + }; + Object.defineProperty(get, "name", { + value: `get ${name}`, + configurable: true + }); + } + let set = origSet; + if (origSet && brandCheck) { + set = function(val) { + if (!this || this === proto || !(this instanceof ctor)) throw new TypeError(`Failed to set the '${name}' property on '${interfaceName}': The provided value is not of type '${interfaceName}'.`); + return origSet.call(this, val); + }; + Object.defineProperty(set, "name", { + value: `set ${name}`, + configurable: true + }); + } + Object.defineProperty(proto, name, { + get, + set: origSet ? set : void 0, + enumerable: true, + configurable: true + }); + continue; + } + if (typeof desc.value === "function") { + const origFn = desc.value; + const minArgs = origFn.length; + let fn = origFn; + if (brandCheck || arityCheck && minArgs > 0) { + fn = function(...args) { + if (brandCheck && (!this || this === proto || !(this instanceof ctor))) { + const err = /* @__PURE__ */ new TypeError(`Failed to execute '${name}' on '${interfaceName}': The provided value is not of type '${interfaceName}'.`); + if (rejectSet.has(name)) return Promise.reject(err); + throw err; + } + if (arityCheck && arguments.length < minArgs) { + const err = /* @__PURE__ */ new TypeError(`Failed to execute '${name}' on '${interfaceName}': ${minArgs} argument${minArgs === 1 ? "" : "s"} required, but only ${arguments.length} present.`); + if (rejectSet.has(name)) return Promise.reject(err); + throw err; + } + return origFn.apply(this, args); + }; + Object.defineProperty(fn, "name", { + value: origFn.name, + configurable: true + }); + Object.defineProperty(fn, "length", { + value: minArgs, + configurable: true + }); + } + Object.defineProperty(proto, name, { + value: fn, + writable: true, + enumerable: true, + configurable: true + }); + } } - - //.CommonJS - CSSOM_1 = CSSOM; - ///CommonJS - return CSSOM_1; } - -var errorUtils = {}; - -var hasRequiredErrorUtils; - -function requireErrorUtils () { - if (hasRequiredErrorUtils) return errorUtils; - hasRequiredErrorUtils = 1; - // Utility functions for CSSOM error handling - - /** - * Gets the appropriate error constructor from the global object context. - * Tries to find the error constructor from parentStyleSheet.__globalObject, - * then from __globalObject, then falls back to the native constructor. - * - * @param {Object} context - The CSSOM object (rule, stylesheet, etc.) - * @param {string} errorType - The error type ('TypeError', 'RangeError', 'DOMException', etc.) - * @return {Function} The error constructor - */ - function getErrorConstructor(context, errorType) { - // Try parentStyleSheet.__globalObject first - if (context.parentStyleSheet && context.parentStyleSheet.__globalObject && context.parentStyleSheet.__globalObject[errorType]) { - return context.parentStyleSheet.__globalObject[errorType]; +/** +* Defines constants on an interface object and its prototype per WebIDL: +* - WebIDL #es-constants: { [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: false } +* - WebIDL #constants-on-interface-prototype-object: same attributes on prototype +*/ +function applyWebIDLConstants(ctor, constants) { + for (const [name, val] of Object.entries(constants)) { + Object.defineProperty(ctor, name, { + value: val, + writable: false, + enumerable: true, + configurable: false + }); + if (ctor.prototype) Object.defineProperty(ctor.prototype, name, { + value: val, + writable: false, + enumerable: true, + configurable: false + }); + } +} +// src/utils.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +function camelToDashed(str) { + return str.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase()).replace(/^ms-/, "-ms-"); +} +function createIndexedProxy(target, getArray, mapValue) { + hideInternalFields(target); + return new Proxy(target, { + get(t, prop, receiver) { + if (typeof prop === "string") { + const index = Number(prop); + if (Number.isInteger(index) && index >= 0) { + const val = getArray(t)[index]; + return val !== void 0 ? mapValue ? mapValue(val) : val : void 0; + } + } + return Reflect.get(t, prop, receiver); + }, + set(t, prop, value) { + return Reflect.set(t, prop, value, t); } - - // Try __parentStyleSheet (alternative naming) - if (context.__parentStyleSheet && context.__parentStyleSheet.__globalObject && context.__parentStyleSheet.__globalObject[errorType]) { - return context.__parentStyleSheet.__globalObject[errorType]; + }); +} +function deleteRuleFromArray(rules, index) { + if (index < 0 || index >= rules.length) throw new DOMException("Index size error", "IndexSizeError"); + const oldRule = rules[index]; + rules.splice(index, 1); + if (oldRule && typeof oldRule === "object") { + if ("_parentRule" in oldRule) oldRule._parentRule = null; + if ("_parentStyleSheet" in oldRule) oldRule._parentStyleSheet = null; + } + return oldRule; +} +// src/parse-hooks.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +/** +* Upward optional feature hooks allowing `cssomnom/parse` (`./parse`) to tree-shake +* out Typed OM, `@property` registry, `MediaParser`, and `CSS.supports`. +* +* Downward parser functions are imported directly from `src/parser.ts`. +*/ +const ParseHooks = { + parseMediaQueryList: (text) => { + return text.split(",").map((s) => s.trim()).filter(Boolean).map((m) => ({ + type: "media-query", + mediaType: m, + tokens: [], + raw: m + })); + }, + serializeMediaQuery: (query) => { + return query.raw ?? query.mediaType ?? ""; + }, + validatePropertyValue: (_property, _value, _rawValue) => { + return true; + }, + supports: (_conditionText) => { + return false; + }, + createStylePropertyMap: (_style) => { + throw new Error("createStylePropertyMap not injected (import cssomnom/typed-om)"); + }, + validatePropertyRuleDescriptor: (_def) => { + return true; + }, + registerPropertyRule: (_def) => { + return false; + }, + unregisterPropertyRule: (_name) => {} +}; +// src/rules/collections.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +var StyleSheetList = class { + /** @internal */ + _sheets; + constructor(sheets) { + this._sheets = sheets; + return createIndexedProxy(this, (t) => t._sheets); + } + get length() { + return this._sheets.length; + } + item(index) { + return this._sheets[index] || null; + } + *[Symbol.iterator]() { + for (let i = 0; i < this.length; i++) yield this._sheets[i]; + } +}; +var MediaList = class { + /** @internal */ + _mediaQueries = []; + constructor(mediaText = "") { + this.mediaText = mediaText; + return createIndexedProxy(this, (t) => t._mediaQueries.map((q) => ParseHooks.serializeMediaQuery(q))); + } + get mediaText() { + return this._mediaQueries.map((q) => ParseHooks.serializeMediaQuery(q)).join(", "); + } + set mediaText(value) { + if (!value) { + this._mediaQueries = []; + return; } - - // Try __globalObject on the context itself - if (context.__globalObject && context.__globalObject[errorType]) { - return context.__globalObject[errorType]; + this._mediaQueries = ParseHooks.parseMediaQueryList(value); + } + get length() { + return this._mediaQueries.length; + } + item(index) { + const q = this._mediaQueries[index]; + return q ? ParseHooks.serializeMediaQuery(q) : null; + } + toString() { + return this.mediaText; + } + appendMedium(medium) { + const parsed = ParseHooks.parseMediaQueryList(medium); + if (parsed.length !== 1) return; + const m = parsed[0]; + const mText = ParseHooks.serializeMediaQuery(m); + if (this._mediaQueries.some((q) => ParseHooks.serializeMediaQuery(q) === mText)) return; + this._mediaQueries.push(m); + } + deleteMedium(medium) { + const parsed = ParseHooks.parseMediaQueryList(medium); + if (parsed.length !== 1) return; + const mText = ParseHooks.serializeMediaQuery(parsed[0]); + let i = this._mediaQueries.length; + let found = false; + while (i--) if (ParseHooks.serializeMediaQuery(this._mediaQueries[i]) === mText) { + this._mediaQueries.splice(i, 1); + found = true; } - - // Fall back to native constructor - return (typeof commonjsGlobal !== 'undefined' && commonjsGlobal[errorType]) || - (typeof window !== 'undefined' && window[errorType]) || - eval(errorType); + if (!found) throw new DOMException(`The medium '${medium}' does not exist in the MediaList.`, "NotFoundError"); } - - /** - * Creates an appropriate error with context-aware constructor. - * - * @param {Object} context - The CSSOM object (rule, stylesheet, etc.) - * @param {string} errorType - The error type ('TypeError', 'RangeError', 'DOMException', etc.) - * @param {string} message - The error message - * @param {string} [name] - Optional name for DOMException - */ - function createError(context, errorType, message, name) { - var ErrorConstructor = getErrorConstructor(context, errorType); - return new ErrorConstructor(message, name); + *[Symbol.iterator]() { + for (let i = 0; i < this.length; i++) yield ParseHooks.serializeMediaQuery(this._mediaQueries[i]); } - - /** - * Creates and throws an appropriate error with context-aware constructor. - * - * @param {Object} context - The CSSOM object (rule, stylesheet, etc.) - * @param {string} errorType - The error type ('TypeError', 'RangeError', 'DOMException', etc.) - * @param {string} message - The error message - * @param {string} [name] - Optional name for DOMException - */ - function throwError(context, errorType, message, name) { - throw createError(context, errorType, message, name); +}; +var CSSRuleList = class { + /** @internal */ + _getRules; + constructor(rulesOrGetter) { + this._getRules = typeof rulesOrGetter === "function" ? rulesOrGetter : () => rulesOrGetter; + return createIndexedProxy(this, (t) => t._getRules(), (v) => v); } - - /** - * Throws a TypeError for missing required arguments. - * - * @param {Object} context - The CSSOM object - * @param {string} methodName - The method name (e.g., 'appendRule') - * @param {string} objectName - The object name (e.g., 'CSSKeyframesRule') - * @param {number} [required=1] - Number of required arguments - * @param {number} [provided=0] - Number of provided arguments - */ - function throwMissingArguments(context, methodName, objectName, required, provided) { - required = required || 1; - provided = provided || 0; - var message = "Failed to execute '" + methodName + "' on '" + objectName + "': " + - required + " argument" + (required > 1 ? "s" : "") + " required, but only " + - provided + " present."; - throwError(context, 'TypeError', message); + get length() { + return this._getRules().length; } - - /** - * Throws a DOMException for parse errors. - * - * @param {Object} context - The CSSOM object - * @param {string} methodName - The method name - * @param {string} objectName - The object name - * @param {string} rule - The rule that failed to parse - * @param {string} [name='SyntaxError'] - The DOMException name - */ - function throwParseError(context, methodName, objectName, rule, name) { - var message = "Failed to execute '" + methodName + "' on '" + objectName + "': " + - "Failed to parse the rule '" + rule + "'."; - throwError(context, 'DOMException', message, name || 'SyntaxError'); + item(index) { + return this._getRules()[index] || null; } - - /** - * Throws a DOMException for index errors. - * - * @param {Object} context - The CSSOM object - * @param {string} methodName - The method name - * @param {string} objectName - The object name - * @param {number} index - The invalid index - * @param {number} maxIndex - The maximum valid index - * @param {string} [name='IndexSizeError'] - The DOMException name - */ - function throwIndexError(context, methodName, objectName, index, maxIndex, name) { - var message = "Failed to execute '" + methodName + "' on '" + objectName + "': " + - "The index provided (" + index + ") is larger than the maximum index (" + maxIndex + ")."; - throwError(context, 'DOMException', message, name || 'IndexSizeError'); - } - - var errorUtils$1 = { - createError: createError, - getErrorConstructor: getErrorConstructor, - throwError: throwError, - throwMissingArguments: throwMissingArguments, - throwParseError: throwParseError, - throwIndexError: throwIndexError - }; - - //.CommonJS - errorUtils.errorUtils = errorUtils$1; - ///CommonJS - return errorUtils; + *[Symbol.iterator]() { + const rules = this._getRules(); + for (let i = 0; i < rules.length; i++) yield rules[i]; + } +}; +// src/rules/utils.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +function isImportRule(r) { + return r.type === 3; } - -var regexPatterns = {}; - -var hasRequiredRegexPatterns; - -function requireRegexPatterns () { - if (hasRequiredRegexPatterns) return regexPatterns; - hasRequiredRegexPatterns = 1; - // Shared regex patterns for CSS parsing and validation - // These patterns are compiled once and reused across multiple files for better performance - - // Regex patterns for CSS parsing - var atKeyframesRegExp = /@(-(?:\w+-)+)?keyframes/g; // Match @keyframes and vendor-prefixed @keyframes - var beforeRulePortionRegExp = /{(?!.*{)|}(?!.*})|;(?!.*;)|\*\/(?!.*\*\/)/g; // Match the closest allowed character (a opening or closing brace, a semicolon or a comment ending) before the rule - var beforeRuleValidationRegExp = /^[\s{};]*(\*\/\s*)?$/; // Match that the portion before the rule is empty or contains only whitespace, semicolons, opening/closing braces, and optionally a comment ending (*/) followed by whitespace - var forwardRuleValidationRegExp = /(?:\s|\/\*|\{|\()/; // Match that the rule is followed by any whitespace, a opening comment, a condition opening parenthesis or a opening brace - var forwardImportRuleValidationRegExp = /(?:\s|\/\*|'|")/; // Match that the rule is followed by any whitespace, an opening comment, a single quote or double quote - var forwardRuleClosingBraceRegExp = /{[^{}]*}|}/; // Finds the next closing brace of a rule block - var forwardRuleSemicolonAndOpeningBraceRegExp = /^.*?({|;)/; // Finds the next semicolon or opening brace after the at-rule - - // Regex patterns for CSS selector validation and parsing - var cssCustomIdentifierRegExp = /^(-?[_a-zA-Z]+(\.[_a-zA-Z]+)*[_a-zA-Z0-9-]*)$/; // Validates a css custom identifier - var startsWithCombinatorRegExp = /^\s*[>+~]/; // Checks if a selector starts with a CSS combinator (>, +, ~) - - /** - * Parse `@page` selectorText for page name and pseudo-pages - * Valid formats: - * - (empty - no name, no pseudo-page) - * - `:left`, `:right`, `:first`, `:blank` (pseudo-page only) - * - `named` (named page only) - * - `named:first` (named page with single pseudo-page) - * - `named:first:left` (named page with multiple pseudo-pages) - */ - var atPageRuleSelectorRegExp = /^([^\s:]+)?((?::\w+)*)$/; // Validates @page rule selectors - - // Regex patterns for CSSImportRule parsing - var layerRegExp = /layer\(([^)]*)\)/; // Matches layer() function in @import - var layerRuleNameRegExp = /^(-?[_a-zA-Z]+(\.[_a-zA-Z]+)*[_a-zA-Z0-9-]*)$/; // Validates layer name (same as custom identifier) - var doubleOrMoreSpacesRegExp = /\s{2,}/g; // Matches two or more consecutive whitespace characters - - - // Regex patterns for CSS escape sequences and identifiers - var startsWithHexEscapeRegExp = /^\\[0-9a-fA-F]/; // Checks if escape sequence starts with hex escape - var identStartCharRegExp = /[a-zA-Z_\u00A0-\uFFFF]/; // Valid identifier start character - var identCharRegExp = /^[a-zA-Z0-9_\-\u00A0-\uFFFF\\]/; // Valid identifier character - var specialCharsNeedEscapeRegExp = /[!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~\s]/; // Characters that need escaping - var combinatorOrSeparatorRegExp = /[\s>+~,()]/; // Selector boundaries and combinators - var afterHexEscapeSeparatorRegExp = /[\s>+~,(){}\[\]]/; // Characters that separate after hex escape - var trailingSpaceSeparatorRegExp = /[\s>+~,(){}]/; // Characters that allow trailing space - var endsWithHexEscapeRegExp = /\\[0-9a-fA-F]{1,6}\s+$/; // Matches selector ending with hex escape + space(s) - - /** - * Regular expression to detect invalid characters in the value portion of a CSS style declaration. - * - * This regex matches a colon (:) that is not inside parentheses and not inside single or double quotes. - * It is used to ensure that the value part of a CSS property does not contain unexpected colons, - * which would indicate a malformed declaration (e.g., "color: foo:bar;" is invalid). - * - * The negative lookahead `(?![^(]*\))` ensures that the colon is not followed by a closing - * parenthesis without encountering an opening parenthesis, effectively ignoring colons inside - * function-like values (e.g., `url(data:image/png;base64,...)`). - * - * The lookahead `(?=(?:[^'"]|'[^']*'|"[^"]*")*$)` ensures that the colon is not inside single or double quotes, - * allowing colons within quoted strings (e.g., `content: ":";` or `background: url("foo:bar.png");`). - * - * Example: - * - `color: red;` // valid, does not match - * - `background: url(data:image/png;base64,...);` // valid, does not match - * - `content: ':';` // valid, does not match - * - `color: foo:bar;` // invalid, matches - */ - var basicStylePropertyValueValidationRegExp = /:(?![^(]*\))(?=(?:[^'"]|'[^']*'|"[^"]*")*$)/; - - // Attribute selector pattern: matches attribute-name operator value - // Operators: =, ~=, |=, ^=, $=, *= - // Rewritten to avoid ReDoS by using greedy match and trimming in JavaScript - var attributeSelectorContentRegExp = /^([^\s=~|^$*]+)\s*(~=|\|=|\^=|\$=|\*=|=)\s*(.+)$/; - - // Selector validation patterns - var pseudoElementRegExp = /::[a-zA-Z][\w-]*|:(before|after|first-line|first-letter)(?![a-zA-Z0-9_-])/; // Matches pseudo-elements - var invalidCombinatorLtGtRegExp = /<>/; // Invalid <> combinator - var invalidCombinatorDoubleGtRegExp = />>/; // Invalid >> combinator - var consecutiveCombinatorsRegExp = /[>+~]\s*[>+~]/; // Invalid consecutive combinators - var invalidSlottedRegExp = /(?:^|[\s>+~,\[])slotted\s*\(/i; // Invalid slotted() without :: - var invalidPartRegExp = /(?:^|[\s>+~,\[])part\s*\(/i; // Invalid part() without :: - var invalidCueRegExp = /(?:^|[\s>+~,\[])cue\s*\(/i; // Invalid cue() without :: - var invalidCueRegionRegExp = /(?:^|[\s>+~,\[])cue-region\s*\(/i; // Invalid cue-region() without :: - var invalidNestingPattern = /&(?![.\#\[:>\+~\s])[a-zA-Z]/; // Invalid & followed by type selector - var emptyPseudoClassRegExp = /:(?:is|not|where|has)\(\s*\)/; // Empty pseudo-class like :is() - var whitespaceNormalizationRegExp = /(['"])(?:\\.|[^\\])*?\1|(\r\n|\r|\n)/g; // Normalize newlines outside quotes - var newlineRemovalRegExp = /\n/g; // Remove all newlines - var whitespaceAndDotRegExp = /[\s.]/; // Matches whitespace or dot - var declarationOrOpenBraceRegExp = /[{;}]/; // Matches declaration separator or open brace - var ampersandRegExp = /&/; // Matches nesting selector - var hexEscapeSequenceRegExp = /^([0-9a-fA-F]{1,6})[ \t\r\n\f]?/; // Matches hex escape sequence (1-6 hex digits optionally followed by whitespace) - var attributeCaseFlagRegExp = /^(.+?)\s+([is])$/i; // Matches case-sensitivity flag at end of attribute value - var prependedAmpersandRegExp = /^&\s+[:\\.]/; // Matches prepended ampersand pattern (& followed by space and : or .) - var openBraceGlobalRegExp = /{/g; // Matches opening braces (global) - var closeBraceGlobalRegExp = /}/g; // Matches closing braces (global) - var scopePreludeSplitRegExp = /\s*\)\s*to\s+\(/; // Splits scope prelude by ") to (" - var leadingWhitespaceRegExp = /^\s+/; // Matches leading whitespace (used to implement a ES5-compliant alternative to trimStart()) - var doubleQuoteRegExp = /"/g; // Match all double quotes (for escaping in attribute values) - var backslashRegExp = /\\/g; // Match all backslashes (for escaping in attribute values) - - // Media query validation patterns - var invalidMediaOperatorWithoutSpaceRegExp = /\)(and|or)\(/i; // )and( or )or( - missing space after operator - var invalidMediaNotAfterOperatorRegExp = /\b(and|or)\s+not\b/i; // and not or or not - not after operator - var invalidMediaNotIdentifierRegExp = /\)\s+not\(/i; // ) not( - not() identifier after media feature - var invalidMediaNotAfterFeatureRegExp = /\)\s+not\s+/i; // ) not - not operator after media feature (only valid at start or after comma) - var invalidMediaOperatorBetweenParensRegExp = /\)\s+(?!and\b|or\b)[a-z]+\s+\(/i; // ) xx ( - invalid operator between ) and ( - var invalidMediaNotWithMediaTypeRegExp = /\bnot\s+[a-z][a-z0-9_-]*\s+\(/i; // not ( - missing 'and' between media type and feature - - // Media query normalization patterns - var mediaFeatureColonRegExp = /(\(\s*)([^():]+):(\s*)([^()]*\))/g; // Match colon in media features - var identifierBeforeParenRegExp = /[a-z0-9_-]/i; // Match identifier character before opening paren - var mediaAndOperatorRegExp = /\s*\band\b\s*/gi; // Match 'and' operator with word boundaries - var mediaOrOperatorRegExp = /\s*\bor\b\s*/gi; // Match 'or' operator with word boundaries - var mediaNotOperatorStartRegExp = /^\s*not\s+(?=\S)/i; // Match 'not' operator at start - var mediaNotOperatorRegExp = /\s+\bnot\s+(?=\S)/gi; // Match 'not' operator after space - - var regexPatterns$1 = { - // Parsing patterns - atKeyframesRegExp: atKeyframesRegExp, - beforeRulePortionRegExp: beforeRulePortionRegExp, - beforeRuleValidationRegExp: beforeRuleValidationRegExp, - forwardRuleValidationRegExp: forwardRuleValidationRegExp, - forwardImportRuleValidationRegExp: forwardImportRuleValidationRegExp, - forwardRuleClosingBraceRegExp: forwardRuleClosingBraceRegExp, - forwardRuleSemicolonAndOpeningBraceRegExp: forwardRuleSemicolonAndOpeningBraceRegExp, - - // Selector validation patterns - cssCustomIdentifierRegExp: cssCustomIdentifierRegExp, - startsWithCombinatorRegExp: startsWithCombinatorRegExp, - atPageRuleSelectorRegExp: atPageRuleSelectorRegExp, - - // Parsing patterns used in CSSImportRule - layerRegExp: layerRegExp, - layerRuleNameRegExp: layerRuleNameRegExp, - doubleOrMoreSpacesRegExp: doubleOrMoreSpacesRegExp, - - // Escape sequence and identifier patterns - startsWithHexEscapeRegExp: startsWithHexEscapeRegExp, - identStartCharRegExp: identStartCharRegExp, - identCharRegExp: identCharRegExp, - specialCharsNeedEscapeRegExp: specialCharsNeedEscapeRegExp, - combinatorOrSeparatorRegExp: combinatorOrSeparatorRegExp, - afterHexEscapeSeparatorRegExp: afterHexEscapeSeparatorRegExp, - trailingSpaceSeparatorRegExp: trailingSpaceSeparatorRegExp, - endsWithHexEscapeRegExp: endsWithHexEscapeRegExp, - - // Basic style property value validation - basicStylePropertyValueValidationRegExp: basicStylePropertyValueValidationRegExp, - - // Attribute selector patterns - attributeSelectorContentRegExp: attributeSelectorContentRegExp, - - // Selector validation patterns - pseudoElementRegExp: pseudoElementRegExp, - invalidCombinatorLtGtRegExp: invalidCombinatorLtGtRegExp, - invalidCombinatorDoubleGtRegExp: invalidCombinatorDoubleGtRegExp, - consecutiveCombinatorsRegExp: consecutiveCombinatorsRegExp, - invalidSlottedRegExp: invalidSlottedRegExp, - invalidPartRegExp: invalidPartRegExp, - invalidCueRegExp: invalidCueRegExp, - invalidCueRegionRegExp: invalidCueRegionRegExp, - invalidNestingPattern: invalidNestingPattern, - emptyPseudoClassRegExp: emptyPseudoClassRegExp, - whitespaceNormalizationRegExp: whitespaceNormalizationRegExp, - newlineRemovalRegExp: newlineRemovalRegExp, - whitespaceAndDotRegExp: whitespaceAndDotRegExp, - declarationOrOpenBraceRegExp: declarationOrOpenBraceRegExp, - ampersandRegExp: ampersandRegExp, - hexEscapeSequenceRegExp: hexEscapeSequenceRegExp, - attributeCaseFlagRegExp: attributeCaseFlagRegExp, - prependedAmpersandRegExp: prependedAmpersandRegExp, - openBraceGlobalRegExp: openBraceGlobalRegExp, - closeBraceGlobalRegExp: closeBraceGlobalRegExp, - scopePreludeSplitRegExp: scopePreludeSplitRegExp, - leadingWhitespaceRegExp: leadingWhitespaceRegExp, - doubleQuoteRegExp: doubleQuoteRegExp, - backslashRegExp: backslashRegExp, - - // Media query validation patterns - invalidMediaOperatorWithoutSpaceRegExp: invalidMediaOperatorWithoutSpaceRegExp, - invalidMediaNotAfterOperatorRegExp: invalidMediaNotAfterOperatorRegExp, - invalidMediaNotIdentifierRegExp: invalidMediaNotIdentifierRegExp, - invalidMediaNotAfterFeatureRegExp: invalidMediaNotAfterFeatureRegExp, - invalidMediaOperatorBetweenParensRegExp: invalidMediaOperatorBetweenParensRegExp, - invalidMediaNotWithMediaTypeRegExp: invalidMediaNotWithMediaTypeRegExp, - - // Media query normalization patterns - mediaFeatureColonRegExp: mediaFeatureColonRegExp, - identifierBeforeParenRegExp: identifierBeforeParenRegExp, - mediaAndOperatorRegExp: mediaAndOperatorRegExp, - mediaOrOperatorRegExp: mediaOrOperatorRegExp, - mediaNotOperatorStartRegExp: mediaNotOperatorStartRegExp, - mediaNotOperatorRegExp: mediaNotOperatorRegExp +function isNamespaceRule(r) { + return r.type === 10; +} +function isRegularRule(r) { + return !isImportRule(r) && !isNamespaceRule(r); +} +function serializeGroupingRule(atKeyword, condition, rules) { + const cond = condition ? " " + condition : ""; + const ruleTexts = rules.map((r) => r.cssText).filter((p) => p !== ""); + if (ruleTexts.length === 0) return atKeyword === "keyframes" ? `@${atKeyword}${cond} { }` : `@${atKeyword}${cond} {\n}`; + return `@${atKeyword}${cond} {\n${ruleTexts.map((t) => " " + t).join("\n")}\n}`; +} +function findParentStyleSheet(rule) { + let sheet = rule.parentStyleSheet; + let curr = rule.parentRule; + while (!sheet && curr) { + sheet = curr.parentStyleSheet; + curr = curr.parentRule; + } + return sheet; +} +/** +* Validates that component values match grammar. +* css-conditional-3 #at-supports +*/ +function isGrammaticallyValidSupportsCondition(values) { + const items = values.filter((v) => v.type !== "whitespace" && v.type !== "comment"); + if (items.length === 0) return false; + const isSupportsInParens = (item) => { + if (item.type === "function") return true; + if (item.type === "simple-block" && item.associatedToken.value === "(") { + const blockValues = item.value.filter((v) => v.type !== "whitespace" && v.type !== "comment"); + if (blockValues.length === 0) return true; + const hasTopOp = blockValues.some((v) => v.type === "ident" && [ + "and", + "or", + "not" + ].includes(String(v.value).toLowerCase())); + const isNestedParenBlock = blockValues.length === 1 && blockValues[0].type === "simple-block" && blockValues[0].associatedToken.value === "("; + if (hasTopOp || isNestedParenBlock) return isGrammaticallyValidSupportsCondition(item.value); + return true; + } + return false; }; - - //.CommonJS - regexPatterns.regexPatterns = regexPatterns$1; - ///CommonJS - return regexPatterns; + const operands = []; + const operators = []; + let i = 0; + while (i < items.length) { + if (items[i].type === "ident" && String(items[i].value).toLowerCase() === "not") { + if (i + 1 >= items.length) return false; + if (!isSupportsInParens(items[i + 1])) return false; + operands.push(true); + i += 2; + } else if (isSupportsInParens(items[i])) { + operands.push(true); + i += 1; + } else return false; + if (i < items.length) { + if (items[i].type !== "ident") return false; + const op = String(items[i].value).toLowerCase(); + if (op !== "and" && op !== "or") return false; + operators.push(op); + i += 1; + } + } + if (operands.length === 0) return false; + if (operands.length !== operators.length + 1) return false; + if (operators.length > 0) { + const firstOp = operators[0]; + if (!operators.every((op) => op === firstOp)) return false; + } + return true; } - -var CSSStyleDeclaration = {}; - -var parse$1 = {}; - -var CSSStyleSheet = {}; - -var MediaList = {}; - -var hasRequiredMediaList; - -function requireMediaList () { - if (hasRequiredMediaList) return MediaList; - hasRequiredMediaList = 1; - //.CommonJS - var CSSOM = {}; - ///CommonJS - - - /** - * @constructor - * @see http://dev.w3.org/csswg/cssom/#the-medialist-interface - */ - CSSOM.MediaList = function MediaList(){ - this.length = 0; +// src/rules/base.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +const RULE_CONSTANTS = { + STYLE_RULE: 1, + CHARSET_RULE: 2, + IMPORT_RULE: 3, + MEDIA_RULE: 4, + FONT_FACE_RULE: 5, + PAGE_RULE: 6, + KEYFRAMES_RULE: 7, + KEYFRAME_RULE: 8, + MARGIN_RULE: 9, + NAMESPACE_RULE: 10, + COUNTER_STYLE_RULE: 11, + SUPPORTS_RULE: 12, + FONT_FEATURE_VALUES_RULE: 14 +}; +var CSSRule = class { + /** @internal */ + _parentRule = null; + /** @internal */ + _parentStyleSheet = null; + /** @internal */ + _location; + /** @internal */ + _assignedLayerName = null; + constructor() { + hideInternalFields(this); + } + get location() { + return this._location; + } + get parentRule() { + return this._parentRule; + } + get parentStyleSheet() { + if (this._parentStyleSheet) return this._parentStyleSheet; + if (this._parentRule) return this._parentRule.parentStyleSheet; + return null; + } + get type() { + return 0; + } + get cssText() { + throw new Error("Not implemented"); + } + set cssText(_value) {} +}; +applyWebIDLConstants(CSSRule, RULE_CONSTANTS); +var CSSGroupingRule = class CSSGroupingRule extends CSSRule { + /** @internal */ + static _parser = null; + /** @internal */ + _cssRules; + /** @internal */ + _rules; + get cssRules() { + if (!this._cssRules) this._cssRules = new CSSRuleList(() => this._rules); + return this._cssRules; + } + constructor(rules, _parseRuleInBlock) { + super(); + this._rules = rules; + this._cssRules = new CSSRuleList(() => this._rules); + for (const rule of rules) if (rule instanceof CSSRule) rule._parentRule = this; + hideInternalFields(this); + } + insertRule(rule, index = 0) { + if (index < 0 || index > this._rules.length) throw new DOMException("Index size error", "IndexSizeError"); + const isScopeRule = (r) => r[Symbol.toStringTag] === "CSSScopeRule"; + const isStyleRule = (r) => r[Symbol.toStringTag] === "CSSStyleRule"; + const isNested = isStyleRule(this) || isScopeRule(this) || this.parentRule !== null; + let isInsideStyleRule = isStyleRule(this); + let isDirectlyInScope = isScopeRule(this); + for (let p = this.parentRule, seenStyle = isInsideStyleRule; p; p = p.parentRule) if (isStyleRule(p)) { + isInsideStyleRule = true; + seenStyle = true; + } else if (!seenStyle && isScopeRule(p)) isDirectlyInScope = true; + if (!CSSGroupingRule._parser) throw new DOMException("Parser not initialized", "SyntaxError"); + const { parseRule, parseRuleInBlock, parseRuleInScopeBlock } = CSSGroupingRule._parser; + let topRule = null; + try { + topRule = parseRule(rule); + } catch {} + if (topRule) { + if (isImportRule(topRule) || isNamespaceRule(topRule)) throw new DOMException("HierarchyRequestError: @import and @namespace rules are not allowed inside grouping rules", "HierarchyRequestError"); + if (isInsideStyleRule && !(topRule instanceof CSSGroupingRule)) throw new DOMException("HierarchyRequestError: This rule cannot be inserted inside a nested rule", "HierarchyRequestError"); + } + const parsedRule = isDirectlyInScope ? parseRuleInScopeBlock(rule) : parseRuleInBlock(rule, isNested); + if (!parsedRule) throw new DOMException("Syntax error", "SyntaxError"); + if (isImportRule(parsedRule) || isNamespaceRule(parsedRule)) throw new DOMException("HierarchyRequestError: @import and @namespace rules are not allowed inside grouping rules", "HierarchyRequestError"); + if (parsedRule[Symbol.toStringTag] === "CSSNestedDeclarations") { + if (!isNested) throw new DOMException("Syntax error: CSSNestedDeclarations cannot be inserted into top-level grouping rule", "SyntaxError"); + if (parsedRule.style._declarations.filter((d) => { + const name = d.name.toLowerCase(); + return name.startsWith("--") || SUPPORTED_PROPERTIES.has(name); + }).length === 0) throw new DOMException("Syntax error: CSSNestedDeclarations contains no valid declarations", "SyntaxError"); + } + if (parsedRule instanceof CSSRule) { + parsedRule._parentRule = this; + parsedRule._parentStyleSheet = null; + } + this._rules.splice(index, 0, parsedRule); + return index; + } + deleteRule(index) { + deleteRuleFromArray(this._rules, index); + } +}; +// src/data/gen/shorthands.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +const SHORTHANDS_DATA = { + "-webkit-animation": [ + "animation-name", + "animation-duration", + "animation-timing-function", + "animation-delay", + "animation-iteration-count", + "animation-direction", + "animation-fill-mode", + "animation-play-state", + "animation-timeline" + ], + "-webkit-border-radius": [ + "border-top-left-radius", + "border-top-right-radius", + "border-bottom-right-radius", + "border-bottom-left-radius" + ], + "-webkit-box-shadow": [ + "box-shadow-color", + "box-shadow-offset", + "box-shadow-blur", + "box-shadow-spread", + "box-shadow-position" + ], + "-webkit-flex": [ + "flex-grow", + "flex-shrink", + "flex-basis" + ], + "-webkit-flex-flow": ["flex-direction", "flex-wrap"], + "-webkit-line-clamp": [ + "max-lines", + "block-ellipsis", + "continue" + ], + "-webkit-mask": [ + "mask-image", + "mask-position", + "mask-size", + "mask-repeat", + "mask-origin", + "mask-clip", + "mask-composite", + "mask-mode" + ], + "-webkit-mask-box-image": [ + "mask-border-source", + "mask-border-slice", + "mask-border-width", + "mask-border-outset", + "mask-border-repeat", + "mask-border-mode" + ], + "-webkit-text-stroke": ["-webkit-text-stroke-width", "-webkit-text-stroke-color"], + "-webkit-transition": [ + "transition-property", + "transition-duration", + "transition-timing-function", + "transition-delay", + "transition-behavior" + ], + "animation": [ + "animation-name", + "animation-duration", + "animation-timing-function", + "animation-delay", + "animation-iteration-count", + "animation-direction", + "animation-fill-mode", + "animation-play-state", + "animation-timeline" + ], + "animation-delay": ["animation-delay-start", "animation-delay-end"], + "animation-range": ["animation-range-start", "animation-range-end"], + "background": [ + "background-image", + "background-position", + "background-size", + "background-repeat", + "background-attachment", + "background-origin", + "background-clip", + "background-color" + ], + "background-position": ["background-position-x", "background-position-y"], + "block-step": [ + "block-step-size", + "block-step-insert", + "block-step-align", + "block-step-round" + ], + "border": [ + "border-width", + "border-style", + "border-color" + ], + "border-block": ["border-block-start", "border-block-end"], + "border-block-clip": ["border-block-start-clip", "border-block-end-clip"], + "border-block-color": ["border-block-start-color", "border-block-end-color"], + "border-block-end": [ + "border-block-end-width", + "border-block-end-style", + "border-block-end-color" + ], + "border-block-end-radius": ["border-end-start-radius", "border-end-end-radius"], + "border-block-start": [ + "border-block-start-width", + "border-block-start-style", + "border-block-start-color" + ], + "border-block-start-radius": ["border-start-start-radius", "border-start-end-radius"], + "border-block-style": ["border-block-start-style", "border-block-end-style"], + "border-block-width": ["border-block-start-width", "border-block-end-width"], + "border-bottom": [ + "border-bottom-width", + "border-bottom-style", + "border-bottom-color" + ], + "border-bottom-radius": ["border-bottom-left-radius", "border-bottom-right-radius"], + "border-clip": [ + "border-top-clip", + "border-right-clip", + "border-bottom-clip", + "border-left-clip" + ], + "border-color": [ + "border-top-color", + "border-right-color", + "border-bottom-color", + "border-left-color" + ], + "border-image": [ + "border-image-source", + "border-image-slice", + "border-image-width", + "border-image-outset", + "border-image-repeat" + ], + "border-inline": ["border-inline-start", "border-inline-end"], + "border-inline-clip": ["border-inline-start-clip", "border-inline-end-clip"], + "border-inline-color": ["border-inline-start-color", "border-inline-end-color"], + "border-inline-end": [ + "border-inline-end-width", + "border-inline-end-style", + "border-inline-end-color" + ], + "border-inline-end-radius": ["border-start-end-radius", "border-end-end-radius"], + "border-inline-start": [ + "border-inline-start-width", + "border-inline-start-style", + "border-inline-start-color" + ], + "border-inline-start-radius": ["border-start-start-radius", "border-end-start-radius"], + "border-inline-style": ["border-inline-start-style", "border-inline-end-style"], + "border-inline-width": ["border-inline-start-width", "border-inline-end-width"], + "border-left": [ + "border-left-width", + "border-left-style", + "border-left-color" + ], + "border-left-radius": ["border-top-left-radius", "border-bottom-left-radius"], + "border-radius": [ + "border-top-left-radius", + "border-top-right-radius", + "border-bottom-right-radius", + "border-bottom-left-radius" + ], + "border-right": [ + "border-right-width", + "border-right-style", + "border-right-color" + ], + "border-right-radius": ["border-top-right-radius", "border-bottom-right-radius"], + "border-style": [ + "border-top-style", + "border-right-style", + "border-bottom-style", + "border-left-style" + ], + "border-top": [ + "border-top-width", + "border-top-style", + "border-top-color" + ], + "border-top-radius": ["border-top-left-radius", "border-top-right-radius"], + "border-width": [ + "border-top-width", + "border-right-width", + "border-bottom-width", + "border-left-width" + ], + "box-shadow": [ + "box-shadow-color", + "box-shadow-offset", + "box-shadow-blur", + "box-shadow-spread", + "box-shadow-position" + ], + "caret": [ + "caret-color", + "caret-animation", + "caret-shape" + ], + "color-adjust": ["print-color-adjust"], + "column-rule": [ + "column-rule-width", + "column-rule-style", + "column-rule-color" + ], + "column-rule-inset": [ + "column-rule-inset-cap-start", + "column-rule-inset-cap-end", + "column-rule-inset-junction-start", + "column-rule-inset-junction-end" + ], + "column-rule-inset-cap": ["column-rule-inset-cap-start", "column-rule-inset-cap-end"], + "column-rule-inset-end": ["column-rule-inset-cap-end", "column-rule-inset-junction-end"], + "column-rule-inset-junction": ["column-rule-inset-junction-start", "column-rule-inset-junction-end"], + "column-rule-inset-start": ["column-rule-inset-cap-start", "column-rule-inset-junction-start"], + "columns": [ + "column-width", + "column-count", + "column-height" + ], + "contain-intrinsic-size": ["contain-intrinsic-width", "contain-intrinsic-height"], + "container": ["container-name", "container-type"], + "corner": ["corner-shape", "border-radius"], + "corner-block-end": ["border-block-end-radius", "corner-block-end-shape"], + "corner-block-end-shape": ["corner-end-start-shape", "corner-end-end-shape"], + "corner-block-start": ["border-block-start-radius", "corner-block-start-shape"], + "corner-block-start-shape": ["corner-start-start-shape", "corner-start-end-shape"], + "corner-bottom": ["border-bottom-radius", "corner-bottom-shape"], + "corner-bottom-left": ["border-bottom-left-radius", "corner-bottom-left-shape"], + "corner-bottom-right": ["border-bottom-right-radius", "corner-bottom-right-shape"], + "corner-bottom-shape": ["corner-bottom-left-shape", "corner-bottom-right-shape"], + "corner-end-end": ["border-end-end-radius", "corner-end-end-shape"], + "corner-end-start": ["border-end-start-radius", "corner-end-start-shape"], + "corner-inline-end": ["border-inline-end-radius", "corner-inline-end-shape"], + "corner-inline-end-shape": ["corner-start-end-shape", "corner-end-end-shape"], + "corner-inline-start": ["border-inline-start-radius", "corner-inline-start-shape"], + "corner-inline-start-shape": ["corner-start-start-shape", "corner-end-start-shape"], + "corner-left": ["border-left-radius", "corner-left-shape"], + "corner-left-shape": ["corner-top-left-shape", "corner-bottom-left-shape"], + "corner-right": ["border-right-radius", "corner-right-shape"], + "corner-right-shape": ["corner-top-right-shape", "corner-bottom-right-shape"], + "corner-shape": [ + "corner-top-left-shape", + "corner-top-right-shape", + "corner-bottom-right-shape", + "corner-bottom-left-shape" + ], + "corner-start-end": ["border-start-end-radius", "corner-start-end-shape"], + "corner-start-start": ["border-start-start-radius", "corner-start-start-shape"], + "corner-top": ["border-top-radius", "corner-top-shape"], + "corner-top-left": ["border-top-left-radius", "corner-top-left-shape"], + "corner-top-right": ["border-top-right-radius", "corner-top-right-shape"], + "corner-top-shape": ["corner-top-left-shape", "corner-top-right-shape"], + "cue": ["cue-before", "cue-after"], + "flex": [ + "flex-grow", + "flex-shrink", + "flex-basis" + ], + "flex-flow": ["flex-direction", "flex-wrap"], + "font": [ + "font-style", + "font-variant", + "font-weight", + "font-stretch", + "font-size", + "line-height", + "font-family" + ], + "font-synthesis": [ + "font-synthesis-weight", + "font-synthesis-style", + "font-synthesis-small-caps" + ], + "font-variant": [ + "font-variant-ligatures", + "font-variant-caps", + "font-variant-alternates", + "font-variant-numeric", + "font-variant-east-asian", + "font-variant-position", + "font-variant-emoji" + ], + "gap": ["row-gap", "column-gap"], + "grid": [ + "grid-template-rows", + "grid-template-columns", + "grid-template-areas", + "grid-auto-rows", + "grid-auto-columns", + "grid-auto-flow" + ], + "grid-area": [ + "grid-row-start", + "grid-column-start", + "grid-row-end", + "grid-column-end" + ], + "grid-column": ["grid-column-start", "grid-column-end"], + "grid-gap": ["row-gap", "column-gap"], + "grid-row": ["grid-row-start", "grid-row-end"], + "grid-template": [ + "grid-template-rows", + "grid-template-columns", + "grid-template-areas" + ], + "inset": [ + "top", + "right", + "bottom", + "left" + ], + "inset-block": ["inset-block-start", "inset-block-end"], + "inset-inline": ["inset-inline-start", "inset-inline-end"], + "interest-delay": ["interest-delay-start", "interest-delay-end"], + "line-clamp": [ + "max-lines", + "block-ellipsis", + "continue" + ], + "list-style": [ + "list-style-type", + "list-style-position", + "list-style-image" + ], + "margin": [ + "margin-top", + "margin-right", + "margin-bottom", + "margin-left" + ], + "margin-block": ["margin-block-start", "margin-block-end"], + "margin-inline": ["margin-inline-start", "margin-inline-end"], + "marker": [ + "marker-start", + "marker-mid", + "marker-end" + ], + "mask": [ + "mask-image", + "mask-position", + "mask-size", + "mask-repeat", + "mask-origin", + "mask-clip", + "mask-composite", + "mask-mode" + ], + "mask-border": [ + "mask-border-source", + "mask-border-slice", + "mask-border-width", + "mask-border-outset", + "mask-border-repeat", + "mask-border-mode" + ], + "max-size": ["max-width", "max-height"], + "min-size": ["min-width", "min-height"], + "offset": [ + "offset-path", + "offset-distance", + "offset-rotate", + "offset-anchor", + "offset-position" + ], + "outline": [ + "outline-width", + "outline-style", + "outline-color" + ], + "overflow": ["overflow-x", "overflow-y"], + "overflow-clip-margin": [ + "overflow-clip-margin-top", + "overflow-clip-margin-right", + "overflow-clip-margin-bottom", + "overflow-clip-margin-left" + ], + "overflow-clip-margin-block": ["overflow-clip-margin-block-start", "overflow-clip-margin-block-end"], + "overflow-clip-margin-inline": ["overflow-clip-margin-inline-start", "overflow-clip-margin-inline-end"], + "overscroll-behavior": ["overscroll-behavior-x", "overscroll-behavior-y"], + "padding": [ + "padding-top", + "padding-right", + "padding-bottom", + "padding-left" + ], + "padding-block": ["padding-block-start", "padding-block-end"], + "padding-inline": ["padding-inline-start", "padding-inline-end"], + "pause": ["pause-before", "pause-after"], + "place-content": ["align-content", "justify-content"], + "place-items": ["align-items", "justify-items"], + "place-self": ["align-self", "justify-self"], + "pointer-timeline": ["pointer-timeline-name", "pointer-timeline-axis"], + "position-try": ["position-try-order", "position-try-fallbacks"], + "rest": ["rest-before", "rest-after"], + "row-rule": [ + "row-rule-width", + "row-rule-style", + "row-rule-color" + ], + "row-rule-inset": [ + "row-rule-inset-cap-start", + "row-rule-inset-cap-end", + "row-rule-inset-junction-start", + "row-rule-inset-junction-end" + ], + "row-rule-inset-cap": ["row-rule-inset-cap-start", "row-rule-inset-cap-end"], + "row-rule-inset-end": ["row-rule-inset-cap-end", "row-rule-inset-junction-end"], + "row-rule-inset-junction": ["row-rule-inset-junction-start", "row-rule-inset-junction-end"], + "row-rule-inset-start": ["row-rule-inset-cap-start", "row-rule-inset-junction-start"], + "rule": ["column-rule", "row-rule"], + "rule-break": ["column-rule-break", "row-rule-break"], + "rule-color": ["column-rule-color", "row-rule-color"], + "rule-inset": ["column-rule-inset", "row-rule-inset"], + "rule-inset-cap": ["column-rule-inset-cap"], + "rule-inset-end": ["column-rule-inset-end", "row-rule-inset-end"], + "rule-inset-junction": ["column-rule-inset-junction"], + "rule-inset-start": ["column-rule-inset-start", "row-rule-inset-start"], + "rule-style": ["column-rule-style", "row-rule-style"], + "rule-visibility-items": ["column-rule-visibility-items"], + "rule-width": ["column-rule-width", "row-rule-width"], + "scroll-margin": [ + "scroll-margin-top", + "scroll-margin-right", + "scroll-margin-bottom", + "scroll-margin-left" + ], + "scroll-margin-block": ["scroll-margin-block-start", "scroll-margin-block-end"], + "scroll-margin-inline": ["scroll-margin-inline-start", "scroll-margin-inline-end"], + "scroll-padding": [ + "scroll-padding-top", + "scroll-padding-right", + "scroll-padding-bottom", + "scroll-padding-left" + ], + "scroll-padding-block": ["scroll-padding-block-start", "scroll-padding-block-end"], + "scroll-padding-inline": ["scroll-padding-inline-start", "scroll-padding-inline-end"], + "scroll-timeline": ["scroll-timeline-name", "scroll-timeline-axis"], + "size": ["width", "height"], + "text-align": ["text-align-all", "text-align-last"], + "text-decoration": [ + "text-decoration-line", + "text-decoration-thickness", + "text-decoration-style", + "text-decoration-color" + ], + "text-decoration-skip": [ + "text-decoration-skip-self", + "text-decoration-skip-box", + "text-decoration-skip-spaces", + "text-decoration-skip-ink" + ], + "text-emphasis": ["text-emphasis-style", "text-emphasis-color"], + "text-spacing": ["text-spacing-trim", "text-autospace"], + "text-wrap": ["text-wrap-mode", "text-wrap-style"], + "timeline-trigger": [ + "timeline-trigger-name", + "timeline-trigger-source", + "timeline-trigger-range" + ], + "timeline-trigger-activation-range": ["timeline-trigger-activation-range-start", "timeline-trigger-activation-range-end"], + "timeline-trigger-active-range": ["timeline-trigger-active-range-start", "timeline-trigger-active-range-end"], + "timeline-trigger-range": ["timeline-trigger-activation-range", "timeline-trigger-active-range"], + "transition": [ + "transition-property", + "transition-duration", + "transition-timing-function", + "transition-delay", + "transition-behavior" + ], + "vertical-align": [ + "alignment-baseline", + "baseline-shift", + "baseline-source" + ], + "view-timeline": ["view-timeline-name", "view-timeline-axis"], + "white-space": ["white-space-collapse", "text-wrap-mode"] +}; +// src/data/gen/LogicalMapping.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +/** +* Logical property to physical property mapping for default horizontal-tb, ltr. +* css-logical-1 #box +*/ +const LOGICAL_MAPPING = { + "background-position-block": "background-position-y", + "background-position-inline": "background-position-x", + "background-repeat-block": "background-repeat-y", + "background-repeat-inline": "background-repeat-x", + "block-size": "height", + "border-block-end-clip": "border-bottom-clip", + "border-block-end-color": "border-bottom-color", + "border-block-end-style": "border-bottom-style", + "border-block-end-width": "border-bottom-width", + "border-block-start-clip": "border-top-clip", + "border-block-start-color": "border-top-color", + "border-block-start-style": "border-top-style", + "border-block-start-width": "border-top-width", + "border-end-end-radius": "border-bottom-right-radius", + "border-end-start-radius": "border-bottom-left-radius", + "border-inline-end-clip": "border-right-clip", + "border-inline-end-color": "border-right-color", + "border-inline-end-style": "border-right-style", + "border-inline-end-width": "border-right-width", + "border-inline-start-clip": "border-left-clip", + "border-inline-start-color": "border-left-color", + "border-inline-start-style": "border-left-style", + "border-inline-start-width": "border-left-width", + "border-start-end-radius": "border-top-right-radius", + "border-start-start-radius": "border-top-left-radius", + "contain-intrinsic-block-size": "contain-intrinsic-height", + "contain-intrinsic-inline-size": "contain-intrinsic-width", + "corner-end-end-shape": "corner-bottom-right-shape", + "corner-end-start-shape": "corner-bottom-left-shape", + "corner-start-end-shape": "corner-top-right-shape", + "corner-start-start-shape": "corner-top-left-shape", + "inline-size": "width", + "inset-block-end": "bottom", + "inset-block-start": "top", + "inset-inline-end": "right", + "inset-inline-start": "left", + "margin-block-end": "margin-bottom", + "margin-block-start": "margin-top", + "margin-inline-end": "margin-right", + "margin-inline-start": "margin-left", + "max-block-size": "max-height", + "max-inline-size": "max-width", + "min-block-size": "min-height", + "min-inline-size": "min-width", + "overflow-block": "overflow-y", + "overflow-clip-margin-block-end": "overflow-clip-margin-bottom", + "overflow-clip-margin-block-start": "overflow-clip-margin-top", + "overflow-clip-margin-inline-end": "overflow-clip-margin-right", + "overflow-clip-margin-inline-start": "overflow-clip-margin-left", + "overflow-inline": "overflow-x", + "overscroll-behavior-block": "overscroll-behavior-y", + "overscroll-behavior-inline": "overscroll-behavior-x", + "padding-block-end": "padding-bottom", + "padding-block-start": "padding-top", + "padding-inline-end": "padding-right", + "padding-inline-start": "padding-left", + "scroll-margin-block-end": "scroll-margin-bottom", + "scroll-margin-block-start": "scroll-margin-top", + "scroll-margin-inline-end": "scroll-margin-right", + "scroll-margin-inline-start": "scroll-margin-left", + "scroll-padding-block-end": "scroll-padding-bottom", + "scroll-padding-block-start": "scroll-padding-top", + "scroll-padding-inline-end": "scroll-padding-right", + "scroll-padding-inline-start": "scroll-padding-left" +}; +const PHYSICAL_TO_LOGICAL = {}; +for (const [logical, physical] of Object.entries(LOGICAL_MAPPING)) { + if (!PHYSICAL_TO_LOGICAL[physical]) PHYSICAL_TO_LOGICAL[physical] = []; + PHYSICAL_TO_LOGICAL[physical].push(logical); +} +/** +* Logical property groups and mapping logic. +* css-logical-1 #logical-property-group +*/ +const LOGICAL_PROPERTY_INFO = { + "background-position-block": { + group: "background-position", + logic: "logical" + }, + "background-position-inline": { + group: "background-position", + logic: "logical" + }, + "background-position-x": { + group: "background-position", + logic: "physical" + }, + "background-position-y": { + group: "background-position", + logic: "physical" + }, + "background-repeat-block": { + group: "background-repeat", + logic: "logical" + }, + "background-repeat-inline": { + group: "background-repeat", + logic: "logical" + }, + "background-repeat-x": { + group: "background-repeat", + logic: "physical" + }, + "background-repeat-y": { + group: "background-repeat", + logic: "physical" + }, + "block-size": { + group: "size", + logic: "logical" + }, + "border-block-end-clip": { + group: "border-clip", + logic: "logical" + }, + "border-block-end-color": { + group: "border-color", + logic: "logical" + }, + "border-block-end-style": { + group: "border-style", + logic: "logical" + }, + "border-block-end-width": { + group: "border-width", + logic: "logical" + }, + "border-block-start-clip": { + group: "border-clip", + logic: "logical" + }, + "border-block-start-color": { + group: "border-color", + logic: "logical" + }, + "border-block-start-style": { + group: "border-style", + logic: "logical" + }, + "border-block-start-width": { + group: "border-width", + logic: "logical" + }, + "border-bottom-clip": { + group: "border-clip", + logic: "physical" + }, + "border-bottom-color": { + group: "border-color", + logic: "physical" + }, + "border-bottom-left-radius": { + group: "border-radius", + logic: "physical" + }, + "border-bottom-right-radius": { + group: "border-radius", + logic: "physical" + }, + "border-bottom-style": { + group: "border-style", + logic: "physical" + }, + "border-bottom-width": { + group: "border-width", + logic: "physical" + }, + "border-end-end-radius": { + group: "border-radius", + logic: "logical" + }, + "border-end-start-radius": { + group: "border-radius", + logic: "logical" + }, + "border-inline-end-clip": { + group: "border-clip", + logic: "logical" + }, + "border-inline-end-color": { + group: "border-color", + logic: "logical" + }, + "border-inline-end-style": { + group: "border-style", + logic: "logical" + }, + "border-inline-end-width": { + group: "border-width", + logic: "logical" + }, + "border-inline-start-clip": { + group: "border-clip", + logic: "logical" + }, + "border-inline-start-color": { + group: "border-color", + logic: "logical" + }, + "border-inline-start-style": { + group: "border-style", + logic: "logical" + }, + "border-inline-start-width": { + group: "border-width", + logic: "logical" + }, + "border-left-clip": { + group: "border-clip", + logic: "physical" + }, + "border-left-color": { + group: "border-color", + logic: "physical" + }, + "border-left-style": { + group: "border-style", + logic: "physical" + }, + "border-left-width": { + group: "border-width", + logic: "physical" + }, + "border-right-clip": { + group: "border-clip", + logic: "physical" + }, + "border-right-color": { + group: "border-color", + logic: "physical" + }, + "border-right-style": { + group: "border-style", + logic: "physical" + }, + "border-right-width": { + group: "border-width", + logic: "physical" + }, + "border-start-end-radius": { + group: "border-radius", + logic: "logical" + }, + "border-start-start-radius": { + group: "border-radius", + logic: "logical" + }, + "border-top-clip": { + group: "border-clip", + logic: "physical" + }, + "border-top-color": { + group: "border-color", + logic: "physical" + }, + "border-top-left-radius": { + group: "border-radius", + logic: "physical" + }, + "border-top-right-radius": { + group: "border-radius", + logic: "physical" + }, + "border-top-style": { + group: "border-style", + logic: "physical" + }, + "border-top-width": { + group: "border-width", + logic: "physical" + }, + "bottom": { + group: "inset", + logic: "physical" + }, + "contain-intrinsic-block-size": { + group: "contain-intrinsic-size", + logic: "logical" + }, + "contain-intrinsic-height": { + group: "contain-intrinsic-size", + logic: "physical" + }, + "contain-intrinsic-inline-size": { + group: "contain-intrinsic-size", + logic: "logical" + }, + "contain-intrinsic-width": { + group: "contain-intrinsic-size", + logic: "physical" + }, + "corner-bottom-left-shape": { + group: "corner-shape", + logic: "physical" + }, + "corner-bottom-right-shape": { + group: "corner-shape", + logic: "physical" + }, + "corner-end-end-shape": { + group: "corner-shape", + logic: "logical" + }, + "corner-end-start-shape": { + group: "corner-shape", + logic: "logical" + }, + "corner-start-end-shape": { + group: "corner-shape", + logic: "logical" + }, + "corner-start-start-shape": { + group: "corner-shape", + logic: "logical" + }, + "corner-top-left-shape": { + group: "corner-shape", + logic: "physical" + }, + "corner-top-right-shape": { + group: "corner-shape", + logic: "physical" + }, + "height": { + group: "size", + logic: "physical" + }, + "inline-size": { + group: "size", + logic: "logical" + }, + "inset-block-end": { + group: "inset", + logic: "logical" + }, + "inset-block-start": { + group: "inset", + logic: "logical" + }, + "inset-inline-end": { + group: "inset", + logic: "logical" + }, + "inset-inline-start": { + group: "inset", + logic: "logical" + }, + "left": { + group: "inset", + logic: "physical" + }, + "margin-block-end": { + group: "margin", + logic: "logical" + }, + "margin-block-start": { + group: "margin", + logic: "logical" + }, + "margin-bottom": { + group: "margin", + logic: "physical" + }, + "margin-inline-end": { + group: "margin", + logic: "logical" + }, + "margin-inline-start": { + group: "margin", + logic: "logical" + }, + "margin-left": { + group: "margin", + logic: "physical" + }, + "margin-right": { + group: "margin", + logic: "physical" + }, + "margin-top": { + group: "margin", + logic: "physical" + }, + "max-block-size": { + group: "max-size", + logic: "logical" + }, + "max-height": { + group: "max-size", + logic: "physical" + }, + "max-inline-size": { + group: "max-size", + logic: "logical" + }, + "max-width": { + group: "max-size", + logic: "physical" + }, + "min-block-size": { + group: "min-size", + logic: "logical" + }, + "min-height": { + group: "min-size", + logic: "physical" + }, + "min-inline-size": { + group: "min-size", + logic: "logical" + }, + "min-width": { + group: "min-size", + logic: "physical" + }, + "overflow-block": { + group: "overflow", + logic: "logical" + }, + "overflow-clip-margin-block-end": { + group: "overflow-clip-margin", + logic: "logical" + }, + "overflow-clip-margin-block-start": { + group: "overflow-clip-margin", + logic: "logical" + }, + "overflow-clip-margin-bottom": { + group: "overflow-clip-margin", + logic: "physical" + }, + "overflow-clip-margin-inline-end": { + group: "overflow-clip-margin", + logic: "logical" + }, + "overflow-clip-margin-inline-start": { + group: "overflow-clip-margin", + logic: "logical" + }, + "overflow-clip-margin-left": { + group: "overflow-clip-margin", + logic: "physical" + }, + "overflow-clip-margin-right": { + group: "overflow-clip-margin", + logic: "physical" + }, + "overflow-clip-margin-top": { + group: "overflow-clip-margin", + logic: "physical" + }, + "overflow-inline": { + group: "overflow", + logic: "logical" + }, + "overflow-x": { + group: "overflow", + logic: "physical" + }, + "overflow-y": { + group: "overflow", + logic: "physical" + }, + "overscroll-behavior-block": { + group: "overscroll-behavior", + logic: "logical" + }, + "overscroll-behavior-inline": { + group: "overscroll-behavior", + logic: "logical" + }, + "overscroll-behavior-x": { + group: "overscroll-behavior", + logic: "physical" + }, + "overscroll-behavior-y": { + group: "overscroll-behavior", + logic: "physical" + }, + "padding-block-end": { + group: "padding", + logic: "logical" + }, + "padding-block-start": { + group: "padding", + logic: "logical" + }, + "padding-bottom": { + group: "padding", + logic: "physical" + }, + "padding-inline-end": { + group: "padding", + logic: "logical" + }, + "padding-inline-start": { + group: "padding", + logic: "logical" + }, + "padding-left": { + group: "padding", + logic: "physical" + }, + "padding-right": { + group: "padding", + logic: "physical" + }, + "padding-top": { + group: "padding", + logic: "physical" + }, + "right": { + group: "inset", + logic: "physical" + }, + "scroll-margin-block-end": { + group: "scroll-margin", + logic: "logical" + }, + "scroll-margin-block-start": { + group: "scroll-margin", + logic: "logical" + }, + "scroll-margin-bottom": { + group: "scroll-margin", + logic: "physical" + }, + "scroll-margin-inline-end": { + group: "scroll-margin", + logic: "logical" + }, + "scroll-margin-inline-start": { + group: "scroll-margin", + logic: "logical" + }, + "scroll-margin-left": { + group: "scroll-margin", + logic: "physical" + }, + "scroll-margin-right": { + group: "scroll-margin", + logic: "physical" + }, + "scroll-margin-top": { + group: "scroll-margin", + logic: "physical" + }, + "scroll-padding-block-end": { + group: "scroll-padding", + logic: "logical" + }, + "scroll-padding-block-start": { + group: "scroll-padding", + logic: "logical" + }, + "scroll-padding-bottom": { + group: "scroll-padding", + logic: "physical" + }, + "scroll-padding-inline-end": { + group: "scroll-padding", + logic: "logical" + }, + "scroll-padding-inline-start": { + group: "scroll-padding", + logic: "logical" + }, + "scroll-padding-left": { + group: "scroll-padding", + logic: "physical" + }, + "scroll-padding-right": { + group: "scroll-padding", + logic: "physical" + }, + "scroll-padding-top": { + group: "scroll-padding", + logic: "physical" + }, + "top": { + group: "inset", + logic: "physical" + }, + "width": { + group: "size", + logic: "physical" + } +}; +/** +* Resolves a logical property to its physical counterpart based on writing mode and direction. +*/ +function resolveLogicalProperty(name, writingMode = "horizontal-tb", direction = "ltr") { + if (!(name in LOGICAL_MAPPING)) return name; + let blockStart = "top"; + let blockEnd = "bottom"; + let inlineStart = "left"; + let inlineEnd = "right"; + let blockAxis = "y"; + let inlineAxis = "x"; + if (writingMode === "vertical-rl" || writingMode === "sideways-rl") { + blockStart = "right"; + blockEnd = "left"; + inlineStart = "top"; + inlineEnd = "bottom"; + blockAxis = "x"; + inlineAxis = "y"; + } else if (writingMode === "vertical-lr") { + blockStart = "left"; + blockEnd = "right"; + inlineStart = "top"; + inlineEnd = "bottom"; + blockAxis = "x"; + inlineAxis = "y"; + } else if (writingMode === "sideways-lr") { + blockStart = "left"; + blockEnd = "right"; + inlineStart = "bottom"; + inlineEnd = "top"; + blockAxis = "x"; + inlineAxis = "y"; + } + if (direction === "rtl") { + const tmp = inlineStart; + inlineStart = inlineEnd; + inlineEnd = tmp; + } + if (name.startsWith("inset-block-start")) return blockStart; + if (name.startsWith("inset-block-end")) return blockEnd; + if (name.startsWith("inset-inline-start")) return inlineStart; + if (name.startsWith("inset-inline-end")) return inlineEnd; + const radius = (s1, s2) => { + return `-${s1 === "top" || s1 === "bottom" ? s1 : s2}-${s1 === "left" || s1 === "right" ? s1 : s2}-radius`; }; - - CSSOM.MediaList.prototype = { - - constructor: CSSOM.MediaList, - - /** - * @return {string} - */ - get mediaText() { - return Array.prototype.join.call(this, ", "); + const shape = (s1, s2) => { + return `-${s1 === "top" || s1 === "bottom" ? s1 : s2}-${s1 === "left" || s1 === "right" ? s1 : s2}-shape`; + }; + const replacements = [ + { + logical: "-block-start", + physical: `-${blockStart}` }, - - /** - * @param {string} value - */ - set mediaText(value) { - if (typeof value === "string") { - var values = value.split(",").filter(function(text){ - return !!text; - }); - var length = this.length = values.length; - for (var i=0; i v.type !== "whitespace" && v.type !== "comment" && v.type !== "EOF"); } - -var CSSStyleRule = {}; - -var CSSRule = {}; - -var hasRequiredCSSRule; - -function requireCSSRule () { - if (hasRequiredCSSRule) return CSSRule; - hasRequiredCSSRule = 1; - //.CommonJS - 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; - }; - - CSSOM.CSSRule.UNKNOWN_RULE = 0; // obsolete - CSSOM.CSSRule.STYLE_RULE = 1; - CSSOM.CSSRule.CHARSET_RULE = 2; // obsolete - CSSOM.CSSRule.IMPORT_RULE = 3; - CSSOM.CSSRule.MEDIA_RULE = 4; - CSSOM.CSSRule.FONT_FACE_RULE = 5; - CSSOM.CSSRule.PAGE_RULE = 6; - CSSOM.CSSRule.KEYFRAMES_RULE = 7; - CSSOM.CSSRule.KEYFRAME_RULE = 8; - CSSOM.CSSRule.MARGIN_RULE = 9; - CSSOM.CSSRule.NAMESPACE_RULE = 10; - CSSOM.CSSRule.COUNTER_STYLE_RULE = 11; - CSSOM.CSSRule.SUPPORTS_RULE = 12; - CSSOM.CSSRule.DOCUMENT_RULE = 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; - - Object.defineProperties(CSSOM.CSSRule.prototype, { - - constructor: { value: CSSOM.CSSRule }, - - cssRule: { - value: "", - configurable: true, - enumerable: true - }, - - cssText: { - get: function() { - // Default getter: subclasses should override this - return ""; - }, - set: function(cssText) { - return cssText; - } - }, - - parentRule: { - get: function() { - return this.__parentRule - } - }, - - parentStyleSheet: { - get: function() { - return this.__parentStyleSheet - } - }, - - UNKNOWN_RULE: { value: 0, enumerable: true }, // obsolet - STYLE_RULE: { value: 1, enumerable: true }, - CHARSET_RULE: { value: 2, enumerable: true }, // obsolet - IMPORT_RULE: { value: 3, enumerable: true }, - MEDIA_RULE: { value: 4, enumerable: true }, - FONT_FACE_RULE: { value: 5, enumerable: true }, - PAGE_RULE: { value: 6, enumerable: true }, - KEYFRAMES_RULE: { value: 7, enumerable: true }, - KEYFRAME_RULE: { value: 8, enumerable: true }, - MARGIN_RULE: { value: 9, enumerable: true }, - NAMESPACE_RULE: { value: 10, enumerable: true }, - COUNTER_STYLE_RULE: { value: 11, enumerable: true }, - SUPPORTS_RULE: { value: 12, enumerable: true }, - DOCUMENT_RULE: { value: 13, enumerable: true }, - FONT_FEATURE_VALUES_RULE: { value: 14, enumerable: true }, - VIEWPORT_RULE: { value: 15, enumerable: true }, - REGION_STYLE_RULE: { value: 16, enumerable: true }, - CONTAINER_RULE: { value: 17, enumerable: true }, - LAYER_BLOCK_RULE: { value: 18, enumerable: true }, - STARTING_STYLE_RULE: { value: 1002, enumerable: true }, - }); - - //.CommonJS - CSSRule.CSSRule = CSSOM.CSSRule; - ///CommonJS - return CSSRule; +function splitTokensByComma(tokens) { + const res = [[]]; + for (const t of tokens) if (t.type === "comma") res.push([]); + else res[res.length - 1].push(t); + return res; } - -var CSSGroupingRule = {}; - -var hasRequiredCSSGroupingRule; - -function requireCSSGroupingRule () { - if (hasRequiredCSSGroupingRule) return CSSGroupingRule; - hasRequiredCSSGroupingRule = 1; - //.CommonJS - var CSSOM = { - CSSRule: requireCSSRule().CSSRule, - CSSRuleList: requireCSSRuleList().CSSRuleList, - parse: requireParse().parse +function expandUniformLonghands(longhands, val) { + const res = {}; + for (const lh of longhands) res[lh] = val; + return res; +} +function tryExpandCssWide(filtered, longhands, extraKeywords) { + if (filtered.length === 1 && filtered[0].type === "ident") { + const v = filtered[0].value.toLowerCase(); + if (CSS_WIDE_KEYWORDS$2.has(v) || extraKeywords?.has(v)) return expandUniformLonghands(longhands, [filtered[0]]); + } + return null; +} +function checkUniformCssWide(serializedVals, checkVar = false) { + if (serializedVals.some((v) => CSS_WIDE_KEYWORDS$2.has(v.toLowerCase()) || checkVar && v.toLowerCase().startsWith("var("))) return serializedVals.every((v) => v.toLowerCase() === serializedVals[0].toLowerCase()) ? serializedVals[0] : null; +} +function expandFourSides(data) { + const s0 = [data[0]]; + const s1 = data.length > 1 ? [data[1]] : s0; + return [ + s0, + s1, + data.length > 2 ? [data[2]] : s0, + data.length > 3 ? [data[3]] : s1 + ]; +} +function formatFourSides(a, b, c, d) { + if (a === b && a === c && a === d) return a; + if (a === c && b === d) return `${a} ${b}`; + if (b === d) return `${a} ${b} ${c}`; + return `${a} ${b} ${c} ${d}`; +} +function getFunctionName(token) { + if (token.type === "function") { + if ("name" in token && typeof token.name === "string") return token.name.toLowerCase(); + if ("value" in token && typeof token.value === "string") return token.value.toLowerCase(); + } + return ""; +} +function isRepeatKeyword(token) { + return token.type === "ident" && REPEAT_KEYWORDS.has(token.value.toLowerCase()); +} +function isAttachmentKeyword(token) { + return token.type === "ident" && ATTACHMENT_KEYWORDS.has(token.value.toLowerCase()); +} +function isBoxKeyword(token) { + return token.type === "ident" && BOX_KEYWORDS.has(token.value.toLowerCase()); +} +function isClipOnlyBoxKeyword(keyword) { + return CLIP_ONLY_BOX_KEYWORDS.has(keyword.toLowerCase()); +} +function isColorToken(token) { + if (token.type === "hash") return true; + if (token.type === "ident") return NAMED_COLORS.has(token.value.toLowerCase()); + if (token.type === "function") return COLOR_FUNCTIONS.has(getFunctionName(token)); + return false; +} +function isImageToken(token) { + if (token.type === "ident" && token.value.toLowerCase() === "none") return true; + if (token.type === "url") return true; + if (token.type === "function") return IMAGE_FUNCTIONS.has(getFunctionName(token)); + return false; +} +function isPositionOrSizeValue(token) { + if (token.type === "ident") return POSITION_OR_SIZE_KEYWORDS.has(token.value.toLowerCase()); + if (token.type === "percentage" || token.type === "dimension") return true; + if (token.type === "number" && token.value === 0) return true; + if (token.type === "function") return MATH_FUNCTIONS.has(getFunctionName(token)); + return false; +} +function extractSizeTokens(tokens, slashIdx) { + if (slashIdx + 1 >= tokens.length) return null; + const first = tokens[slashIdx + 1]; + if (first.type === "ident" && ["cover", "contain"].includes(first.value.toLowerCase())) return { + size: [first], + consumed: 1 }; - var errorUtils = requireErrorUtils().errorUtils; - ///CommonJS - - - /** - * @constructor - * @see https://drafts.csswg.org/cssom/#the-cssgroupingrule-interface - */ - CSSOM.CSSGroupingRule = function CSSGroupingRule() { - CSSOM.CSSRule.call(this); - this.__cssRules = new CSSOM.CSSRuleList(); + const isSizeVal = (t) => t.type === "ident" && t.value.toLowerCase() === "auto" || t.type === "percentage" || t.type === "dimension" || t.type === "number" && t.value === 0 || t.type === "function" && MATH_FUNCTIONS.has(getFunctionName(t)); + if (!isSizeVal(first)) return null; + if (slashIdx + 2 < tokens.length && isSizeVal(tokens[slashIdx + 2])) return { + size: [first, tokens[slashIdx + 2]], + consumed: 2 }; - - CSSOM.CSSGroupingRule.prototype = Object.create(CSSOM.CSSRule.prototype); - CSSOM.CSSGroupingRule.prototype.constructor = CSSOM.CSSGroupingRule; - - Object.setPrototypeOf(CSSOM.CSSGroupingRule, CSSOM.CSSRule); - - Object.defineProperty(CSSOM.CSSGroupingRule.prototype, "cssRules", { - get: function() { - return this.__cssRules; - } - }); - - /** - * Used to insert a new CSS rule to a list of CSS rules. - * - * @example - * cssGroupingRule.cssText - * -> "body{margin:0;}" - * cssGroupingRule.insertRule("img{border:none;}", 1) - * -> 1 - * cssGroupingRule.cssText - * -> "body{margin:0;}img{border:none;}" - * - * @param {string} rule - * @param {number} [index] - * @see https://www.w3.org/TR/cssom-1/#dom-cssgroupingrule-insertrule - * @return {number} The index within the grouping rule's collection of the newly inserted rule. - */ - CSSOM.CSSGroupingRule.prototype.insertRule = function insertRule(rule, index) { - if (rule === undefined && index === undefined) { - errorUtils.throwMissingArguments(this, 'insertRule', this.constructor.name); - } - if (index === void 0) { - index = 0; - } - index = Number(index); - if (index < 0) { - index = 4294967296 + index; - } - if (index > this.cssRules.length) { - errorUtils.throwIndexError(this, 'insertRule', this.constructor.name, index, this.cssRules.length); - } - var ruleToParse = processedRuleToParse = String(rule); - ruleToParse = ruleToParse.trim().replace(/^\/\*[\s\S]*?\*\/\s*/, ""); - var isNestedSelector = this.constructor.name === "CSSStyleRule"; - if (isNestedSelector === false) { - var currentRule = this; - while (currentRule.parentRule) { - currentRule = currentRule.parentRule; - if (currentRule.constructor.name === "CSSStyleRule") { - isNestedSelector = true; - break; - } - } - } - if (isNestedSelector) { - processedRuleToParse = 's { n { } ' + ruleToParse + '}'; - } - var isScopeRule = this.constructor.name === "CSSScopeRule"; - if (isScopeRule) { - if (isNestedSelector) { - processedRuleToParse = 's { ' + '@scope {' + ruleToParse + '}}'; - } else { - processedRuleToParse = '@scope {' + ruleToParse + '}'; - } - } - var parsedRules = new CSSOM.CSSRuleList(); - CSSOM.parse(processedRuleToParse, { - styleSheet: this.parentStyleSheet, - cssRules: parsedRules + return { + size: [first], + consumed: 1 + }; +} +function mapBoxKeywords(keywords) { + if (keywords.length === 0) return { + origin: "padding-box", + clip: "border-box" + }; + if (keywords.length === 1) { + const a = keywords[0].toLowerCase(); + return isClipOnlyBoxKeyword(a) ? { + origin: "border-box", + clip: a + } : { + origin: a, + clip: a + }; + } + if (keywords.length === 2) { + const a = keywords[0].toLowerCase(); + const b = keywords[1].toLowerCase(); + const aClip = isClipOnlyBoxKeyword(a); + const bClip = isClipOnlyBoxKeyword(b); + if (aClip && bClip) return { + origin: "border-box", + clip: `${a} ${b}` + }; + if (aClip) return { + origin: b, + clip: a + }; + if (bClip) return { + origin: a, + clip: b + }; + return { + origin: a, + clip: b + }; + } + if (keywords.length === 3) { + const clips = keywords.filter(isClipOnlyBoxKeyword); + const origins = keywords.filter((k) => !isClipOnlyBoxKeyword(k)); + return clips.length === 2 && origins.length === 1 ? { + origin: origins[0], + clip: clips.join(" ") + } : null; + } + return null; +} +function parseRepeatTokens(tokens) { + if (tokens.length === 1) { + const val = tokens[0].value?.toString().toLowerCase(); + if (val === "repeat-x") return [{ + type: "ident", + value: "repeat" + }, { + type: "ident", + value: "no-repeat" + }]; + if (val === "repeat-y") return [{ + type: "ident", + value: "no-repeat" + }, { + type: "ident", + value: "repeat" + }]; + return [tokens[0]]; + } + return tokens.length === 2 ? tokens : null; +} +function normalizePositionTokens(tokens) { + if (tokens.length === 1) { + const t0 = tokens[0]; + if (t0.type === "ident") { + const v = t0.value.toLowerCase(); + if (v === "left" || v === "right") return [t0, { + type: "ident", + value: "center" + }]; + if (v === "top" || v === "bottom") return [{ + type: "ident", + value: "center" + }, t0]; + if (v === "center") return [t0, t0]; + } else return [t0, { + type: "percentage", + value: 50, + sign: null + }]; + } + if (tokens.length === 2 && tokens[0].type === "ident" && tokens[1].type === "ident") { + const [t0, t1] = tokens; + const v0 = t0.value.toLowerCase(); + const v1 = t1.value.toLowerCase(); + const isHoriz = (v) => v === "left" || v === "right"; + const isVert = (v) => v === "top" || v === "bottom"; + if (isVert(v0) && isHoriz(v1) || isVert(v0) && v1 === "center") return [t1, t0]; + } + return tokens; +} +function normalizeSizeTokens(tokens) { + if (tokens.length === 1) { + const t0 = tokens[0]; + if (t0.type === "ident" && ["cover", "contain"].includes(t0.value.toLowerCase())) return [t0]; + return [t0, { + type: "ident", + value: "auto" + }]; + } + return tokens; +} +function joinWithWhitespace(tokens) { + const res = []; + for (let i = 0; i < tokens.length; i++) { + if (i > 0) res.push({ + type: "whitespace", + value: " " }); - if (isScopeRule) { - if (isNestedSelector) { - parsedRules = parsedRules[0].cssRules[0].cssRules; - } else { - parsedRules = parsedRules[0].cssRules; - } + res.push(tokens[i]); + } + return res; +} +function expandBackground(values) { + const cssWide = tryExpandCssWide(filterSignificantTokens(values), SHORTHANDS_DATA["background"]); + if (cssWide) return cssWide; + const layers = splitTokensByComma(values); + const numLayers = layers.length; + if (numLayers === 0) return null; + const imageLayers = []; + const positionLayers = []; + const sizeLayers = []; + const repeatLayers = []; + const attachmentLayers = []; + const originLayers = []; + const clipLayers = []; + let parsedColor = null; + for (let i = 0; i < numLayers; i++) { + const layerClean = filterSignificantTokens(layers[i]); + if (layerClean.length === 0) return null; + const slashIdx = layerClean.findIndex((t) => t.type === "delim" && t.value === "/"); + let sizeTokens = null; + if (slashIdx !== -1) { + const sizeResult = extractSizeTokens(layerClean, slashIdx); + if (!sizeResult) return null; + sizeTokens = sizeResult.size; + layerClean.splice(slashIdx, 1 + sizeResult.consumed); } - if (isNestedSelector) { - parsedRules = parsedRules[0].cssRules.slice(1); + const repeatTokens = []; + const attachmentTokens = []; + const boxKeywords = []; + let colorTokens = null; + let imageTokens = null; + const positionTokens = []; + for (const token of layerClean) if (isRepeatKeyword(token)) repeatTokens.push(token); + else if (isAttachmentKeyword(token)) attachmentTokens.push(token); + else if (isBoxKeyword(token)) boxKeywords.push(token.value); + else if (isColorToken(token)) { + if (i !== numLayers - 1 || colorTokens !== null) return null; + colorTokens = [token]; + } else if (isImageToken(token)) { + if (imageTokens !== null) return null; + imageTokens = [token]; + } else if (isPositionOrSizeValue(token)) positionTokens.push(token); + else return null; + if (sizeTokens !== null && positionTokens.length === 0 || positionTokens.length > 4) return null; + const boxMapped = mapBoxKeywords(boxKeywords); + if (!boxMapped) return null; + const repeatMapped = parseRepeatTokens(repeatTokens); + imageLayers.push(imageTokens || [{ + type: "ident", + value: "none" + }]); + positionLayers.push(positionTokens.length > 0 ? joinWithWhitespace(normalizePositionTokens(positionTokens)) : joinWithWhitespace([{ + type: "percentage", + value: 0, + sign: null + }, { + type: "percentage", + value: 0, + sign: null + }])); + sizeLayers.push(sizeTokens !== null ? joinWithWhitespace(normalizeSizeTokens(sizeTokens)) : [{ + type: "ident", + value: "auto" + }]); + repeatLayers.push(repeatMapped ? joinWithWhitespace(repeatMapped) : [{ + type: "ident", + value: "repeat" + }]); + attachmentLayers.push(attachmentTokens.length > 0 ? joinWithWhitespace(attachmentTokens) : [{ + type: "ident", + value: "scroll" + }]); + originLayers.push([{ + type: "ident", + value: boxMapped.origin + }]); + clipLayers.push(joinWithWhitespace(boxMapped.clip.split(" ").map((c) => ({ + type: "ident", + value: c + })))); + if (colorTokens !== null) parsedColor = colorTokens; + } + const joinLayers = (layerList) => { + const res = []; + for (let i = 0; i < layerList.length; i++) { + if (i > 0) res.push({ + type: "comma", + value: "," + }, { + type: "whitespace", + value: " " + }); + res.push(...layerList[i]); } - if (parsedRules.length !== 1) { - if (isNestedSelector && parsedRules.length === 0 && ruleToParse.indexOf('@font-face') === 0) { - errorUtils.throwError(this, 'DOMException', - "Failed to execute 'insertRule' on '" + this.constructor.name + "': " + - "Only conditional nested group rules, style rules, @scope rules, @apply rules, and nested declaration rules may be nested.", - 'HierarchyRequestError'); - } else { - errorUtils.throwParseError(this, 'insertRule', this.constructor.name, ruleToParse, 'SyntaxError'); - } + return res; + }; + return { + "background-image": joinLayers(imageLayers), + "background-position": joinLayers(positionLayers), + "background-size": joinLayers(sizeLayers), + "background-repeat": joinLayers(repeatLayers), + "background-attachment": joinLayers(attachmentLayers), + "background-origin": joinLayers(originLayers), + "background-clip": joinLayers(clipLayers), + "background-color": parsedColor || [{ + type: "ident", + value: "transparent" + }] + }; +} +function contractBackground(longhands) { + const image = longhands["background-image"]; + const position = longhands["background-position"]; + const size = longhands["background-size"]; + const repeat = longhands["background-repeat"]; + const attachment = longhands["background-attachment"]; + const origin = longhands["background-origin"]; + const clip = longhands["background-clip"]; + const color = longhands["background-color"]; + if (!image || !position || !size || !repeat || !attachment || !origin || !clip || !color) return null; + const imageLayers = splitTokensByComma(image); + const positionLayers = splitTokensByComma(position); + const sizeLayers = splitTokensByComma(size); + const repeatLayers = splitTokensByComma(repeat); + const attachmentLayers = splitTokensByComma(attachment); + const originLayers = splitTokensByComma(origin); + const clipLayers = splitTokensByComma(clip); + const numLayers = imageLayers.length; + if (positionLayers.length !== numLayers || sizeLayers.length !== numLayers || repeatLayers.length !== numLayers || attachmentLayers.length !== numLayers || originLayers.length !== numLayers || clipLayers.length !== numLayers) return null; + const layerStrings = []; + for (let i = 0; i < numLayers; i++) { + const imgVal = serialize(imageLayers[i]).trim(); + const posVal = serialize(positionLayers[i]).trim(); + const sizeVal = serialize(sizeLayers[i]).trim(); + const repVal = serialize(repeatLayers[i]).trim(); + const attVal = serialize(attachmentLayers[i]).trim(); + const origVal = serialize(originLayers[i]).trim(); + const clipVal = serialize(clipLayers[i]).trim(); + const parts = []; + if (imgVal !== "none" && imgVal !== "") parts.push(imgVal); + const isInitialPosition = posVal !== "" && [ + "0% 0%", + "left top", + "0% center", + "center left", + "left center" + ].includes(posVal.toLowerCase()); + const isInitialSize = sizeVal !== "" && ["auto", "auto auto"].includes(sizeVal.toLowerCase()); + if (posVal !== "" && sizeVal !== "") { + if (!isInitialSize) parts.push(`${posVal} / ${sizeVal}`); + else if (!isInitialPosition) parts.push(posVal); } - var cssRule = parsedRules[0]; - - if (cssRule.constructor.name === 'CSSNestedDeclarations' && cssRule.style.length === 0) { - errorUtils.throwParseError(this, 'insertRule', this.constructor.name, ruleToParse, 'SyntaxError'); + const isInitialRepeat = repVal !== "" && ["repeat", "repeat repeat"].includes(repVal.toLowerCase()); + if (repVal !== "" && !isInitialRepeat) { + const tokens = repeatLayers[i].filter((t) => t.type !== "whitespace" && t.type !== "EOF"); + if (tokens.length === 2) { + const v0 = tokens[0].value?.toString().toLowerCase(); + const v1 = tokens[1].value?.toString().toLowerCase(); + if (v0 === "repeat" && v1 === "no-repeat") parts.push("repeat-x"); + else if (v0 === "no-repeat" && v1 === "repeat") parts.push("repeat-y"); + else if (v0 === v1 && v0) parts.push(v0); + else parts.push(`${v0} ${v1}`); + } else parts.push(repVal); } - - // Check for rules that cannot be inserted inside a CSSGroupingRule - if (cssRule.constructor.name === 'CSSImportRule' || cssRule.constructor.name === 'CSSNamespaceRule') { - var ruleKeyword = cssRule.constructor.name === 'CSSImportRule' ? '@import' : '@namespace'; - errorUtils.throwError(this, 'DOMException', - "Failed to execute 'insertRule' on '" + this.constructor.name + "': " + - "'" + ruleKeyword + "' rules cannot be inserted inside a group rule.", - 'HierarchyRequestError'); + if (attVal !== "" && attVal.toLowerCase() !== "scroll") parts.push(attVal); + if (origVal !== "" && clipVal !== "") { + const isClipOnly = ["text", "border-area"].includes(clipVal.toLowerCase()) || clipVal.toLowerCase().includes("text") || clipVal.toLowerCase().includes("border-area"); + const defaultOrigin = isClipOnly ? "border-box" : "padding-box"; + if (origVal.toLowerCase() !== "padding-box" || clipVal.toLowerCase() !== "border-box") { + if (isClipOnly) parts.push(origVal.toLowerCase() === defaultOrigin ? clipVal : `${origVal} ${clipVal}`); + else parts.push(origVal.toLowerCase() === clipVal.toLowerCase() ? origVal : `${origVal} ${clipVal}`); + } } - - // Check for CSSLayerStatementRule (@layer statement rules) - if (cssRule.constructor.name === 'CSSLayerStatementRule') { - errorUtils.throwParseError(this, 'insertRule', this.constructor.name, ruleToParse, 'SyntaxError'); + if (i === numLayers - 1) { + const colVal = serialize(color).trim(); + if (colVal !== "" && colVal.toLowerCase() !== "transparent") parts.push(colVal); } - - cssRule.__parentRule = this; - this.cssRules.splice(index, 0, cssRule); - return index; + if (parts.length === 0) parts.push("none"); + layerStrings.push(parts.join(" ")); + } + return layerStrings.join(", "); +} +function isValidLengthOrPercentage(val, allowNegative = true) { + if (val.type === "ident") { + const kw = (val.value ?? "").toString().toLowerCase(); + return kw === "auto" || CSS_WIDE_KEYWORDS$2.has(kw); + } + if (val.type === "dimension") { + if (!allowNegative && typeof val.value === "number" && val.value < 0) return false; + return LENGTH_UNITS.has((val.unit ?? "").toLowerCase()); + } + if (val.type === "percentage") return allowNegative || typeof val.value !== "number" || val.value >= 0; + if (val.type === "number" && val.value === 0) return true; + if (val.type === "function") { + const fnName = getFunctionName(val); + return MATH_FUNCTIONS.has(fnName) || fnName === "env"; + } + return false; +} +const expandBox = (physical, logical) => (values) => { + const filtered = filterSignificantTokens(values); + if (filtered.length === 0) return null; + const isLogical = filtered[0].type === "ident" && filtered[0].value.toLowerCase() === "logical"; + const data = isLogical ? filtered.slice(1) : filtered; + if (data.length < 1 || data.length > 4) return null; + const isLengthBox = physical[0].startsWith("margin") || physical[0].startsWith("padding") || physical[0] === "top" || physical[0].startsWith("scroll-") || physical[0].startsWith("border-") && physical[0].endsWith("-width"); + const allowNegative = !physical[0].startsWith("padding") && !physical[0].startsWith("scroll-padding") && !(physical[0].startsWith("border-") && physical[0].endsWith("-width")); + if (isLengthBox && !data.every((v) => isValidLengthOrPercentage(v, allowNegative))) return null; + const target = isLogical ? logical : physical; + const [s0, s1, s2, s3] = expandFourSides(data); + return { + [target[0]]: s0, + [target[1]]: s1, + [target[2]]: s2, + [target[3]]: s3 }; - - /** - * Used to delete a rule from the grouping rule. - * - * cssGroupingRule.cssText - * -> "img{border:none;}body{margin:0;}" - * cssGroupingRule.deleteRule(0) - * cssGroupingRule.cssText - * -> "body{margin:0;}" - * - * @param {number} index within the grouping rule's rule list of the rule to remove. - * @see https://www.w3.org/TR/cssom-1/#dom-cssgroupingrule-deleterule - */ - CSSOM.CSSGroupingRule.prototype.deleteRule = function deleteRule(index) { - if (index === undefined) { - errorUtils.throwMissingArguments(this, 'deleteRule', this.constructor.name); - } - index = Number(index); - if (index < 0) { - index = 4294967296 + index; - } - if (index >= this.cssRules.length) { - errorUtils.throwIndexError(this, 'deleteRule', this.constructor.name, index, this.cssRules.length); - } - this.cssRules[index].__parentRule = null; - this.cssRules[index].__parentStyleSheet = null; - this.cssRules.splice(index, 1); +}; +const contractBox = (physical, logical) => (values) => { + const t = values[physical[0]]; + const r = values[physical[1]]; + const b = values[physical[2]]; + const l = values[physical[3]]; + if (t && r && b && l) { + const serialized = [ + t, + r, + b, + l + ].map((v) => serialize(v).trim()); + const cssWide = checkUniformCssWide(serialized); + if (cssWide !== void 0) return cssWide; + return formatFourSides(serialized[0], serialized[1], serialized[2], serialized[3]); + } + const lbs = values[logical[0]]; + const lbe = values[logical[2]]; + const lis = values[logical[1]]; + const lie = values[logical[3]]; + if (lbs && lbe && lis && lie) { + const [sbs, sis, sbe, sie] = [ + lbs, + lis, + lbe, + lie + ].map((v) => serialize(v).trim()); + const cssWide = checkUniformCssWide([ + sbs, + sis, + sbe, + sie + ]); + if (cssWide !== void 0) return cssWide; + return `logical ${formatFourSides(sbs, sis, sbe, sie)}`; + } + return null; +}; +const expandTwoValue = (longhands) => (values) => { + const filtered = filterSignificantTokens(values); + if (filtered.length < 1 || filtered.length > 2) return null; + if (longhands[0].startsWith("padding") || longhands[0].endsWith("-width")) { + for (const v of filtered) if ((v.type === "dimension" || v.type === "percentage" || v.type === "number") && typeof v.value === "number" && v.value < 0) return null; + } + return { + [longhands[0]]: [filtered[0]], + [longhands[1]]: filtered.length > 1 ? [filtered[1]] : [filtered[0]] }; - - //.CommonJS - CSSGroupingRule.CSSGroupingRule = CSSOM.CSSGroupingRule; - ///CommonJS - return CSSGroupingRule; +}; +const contractTwoValue = (longhands) => (values) => { + const v1 = values[longhands[0]]; + const v2 = values[longhands[1]]; + if (!v1 || !v2) return null; + const s1 = serialize(v1).trim(); + const s2 = serialize(v2).trim(); + const cssWide = checkUniformCssWide([s1, s2]); + if (cssWide !== void 0) return cssWide; + return s1 === s2 ? s1 : `${s1} ${s2}`; +}; +function formatBorderSideValue(widthVal, styleVal, colorVal) { + const w = widthVal.trim(); + const s = styleVal.trim(); + const c = colorVal.trim(); + const cssWide = checkUniformCssWide([ + w, + s, + c + ]); + if (cssWide !== void 0) return cssWide; + const isInitialWidth = w.toLowerCase() === "medium"; + const isInitialStyle = s.toLowerCase() === "none"; + const isInitialColor = c.toLowerCase() === "currentcolor"; + if (isInitialWidth && isInitialStyle && isInitialColor) return "none"; + const parts = []; + if (!isInitialWidth) parts.push(w); + if (!isInitialStyle) parts.push(s); + if (!isInitialColor) parts.push(c); + return parts.length === 0 ? "none" : parts.join(" "); } - -var hasRequiredCSSStyleRule; - -function requireCSSStyleRule () { - if (hasRequiredCSSStyleRule) return CSSStyleRule; - hasRequiredCSSStyleRule = 1; - //.CommonJS - var CSSOM = { - CSSStyleDeclaration: requireCSSStyleDeclaration().CSSStyleDeclaration, - CSSRule: requireCSSRule().CSSRule, - CSSRuleList: requireCSSRuleList().CSSRuleList, - CSSGroupingRule: requireCSSGroupingRule().CSSGroupingRule, +function parseBorderTriplet(filtered, allowAutoStyle = false) { + let widthVal = [{ + type: "ident", + value: "medium" + }]; + let styleVal = [{ + type: "ident", + value: "none" + }]; + let colorVal = [{ + type: "ident", + value: "currentcolor" + }]; + for (const val of filtered) if (val.type === "ident") { + const v = val.value.toLowerCase(); + if (BORDER_WIDTH_KEYWORDS.has(v)) widthVal = [val]; + else if (BORDER_STYLE_KEYWORDS.has(v) || allowAutoStyle && v === "auto") styleVal = [val]; + else colorVal = [val]; + } else if (val.type === "dimension" || val.type === "percentage" || val.type === "number") widthVal = [val]; + else colorVal = [val]; + return { + widthVal, + styleVal, + colorVal }; - var regexPatterns = requireRegexPatterns().regexPatterns; - // Use cssstyle if available - try { - CSSOM.CSSStyleDeclaration = require("cssstyle").CSSStyleDeclaration; - } catch (e) { - // ignore - } - ///CommonJS - - - /** - * @constructor - * @see http://dev.w3.org/csswg/cssom/#cssstylerule - * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleRule - */ - CSSOM.CSSStyleRule = function CSSStyleRule() { - CSSOM.CSSGroupingRule.call(this); - this.__selectorText = ""; - this.__style = new CSSOM.CSSStyleDeclaration(); - this.__style.parentRule = this; +} +const BORDER_IMAGE_LONGHANDS = [ + "border-image-source", + "border-image-slice", + "border-image-width", + "border-image-outset", + "border-image-repeat" +]; +const BORDER_ALL_LONGHANDS = [ + "border-top-width", + "border-right-width", + "border-bottom-width", + "border-left-width", + "border-top-style", + "border-right-style", + "border-bottom-style", + "border-left-style", + "border-top-color", + "border-right-color", + "border-bottom-color", + "border-left-color", + ...BORDER_IMAGE_LONGHANDS +]; +function createInitialBorderImageValues(source = [{ + type: "ident", + value: "none" +}]) { + return { + "border-image-source": source, + "border-image-slice": [{ + type: "percentage", + value: 100, + sign: null + }], + "border-image-width": [{ + type: "number", + value: 1, + sign: null, + numberType: "integer" + }], + "border-image-outset": [{ + type: "number", + value: 0, + sign: null, + numberType: "integer" + }], + "border-image-repeat": [{ + type: "ident", + value: "stretch" + }] }; - - CSSOM.CSSStyleRule.prototype = Object.create(CSSOM.CSSGroupingRule.prototype); - CSSOM.CSSStyleRule.prototype.constructor = CSSOM.CSSStyleRule; - - Object.setPrototypeOf(CSSOM.CSSStyleRule, CSSOM.CSSGroupingRule); - - Object.defineProperty(CSSOM.CSSStyleRule.prototype, "type", { - value: 1, - writable: false - }); - - Object.defineProperty(CSSOM.CSSStyleRule.prototype, "selectorText", { - get: function() { - return this.__selectorText; - }, - set: function(value) { - if (typeof value === "string") { - // Don't trim if the value ends with a hex escape sequence followed by space - // (e.g., ".\31 " where the space is part of the escape terminator) - var endsWithHexEscapeRegExp = regexPatterns.endsWithHexEscapeRegExp; - var endsWithEscape = endsWithHexEscapeRegExp.test(value); - var trimmedValue = endsWithEscape ? value.replace(/\s+$/, ' ').trimStart() : value.trim(); - - if (trimmedValue === '') { - return; - } - - // TODO: Setting invalid selectorText should be ignored - // There are some validations already on lib/parse.js - // but the same validations should be applied here. - // Check if we can move these validation logic to a shared function. - - this.__selectorText = trimmedValue; - } - }, - configurable: true - }); - - Object.defineProperty(CSSOM.CSSStyleRule.prototype, "style", { - get: function() { - return this.__style; - }, - set: function(value) { - if (typeof value === "string") { - this.__style.cssText = value; - } else { - this.__style = value; - } - }, - configurable: true - }); - - Object.defineProperty(CSSOM.CSSStyleRule.prototype, "cssText", { - get: function() { - var text; - if (this.selectorText) { - var values = ""; - if (this.cssRules.length) { - var valuesArr = [" {"]; - this.style.cssText && valuesArr.push(this.style.cssText); - valuesArr.push(this.cssRules.reduce(function(acc, rule){ - if (rule.cssText !== "") { - acc.push(rule.cssText); - } - return acc; - }, []).join("\n ")); - values = valuesArr.join("\n ") + "\n}"; - } else { - values = " {" + (this.style.cssText ? " " + this.style.cssText : "") + " }"; - } - text = this.selectorText + values; - } else { - text = ""; - } - return text; - } - }); - - //.CommonJS - CSSStyleRule.CSSStyleRule = CSSOM.CSSStyleRule; - ///CommonJS - return CSSStyleRule; } - -var hasRequiredCSSStyleSheet; - -function requireCSSStyleSheet () { - if (hasRequiredCSSStyleSheet) return CSSStyleSheet; - hasRequiredCSSStyleSheet = 1; - //.CommonJS - var CSSOM = { - MediaList: requireMediaList().MediaList, - StyleSheet: requireStyleSheet().StyleSheet, - CSSRuleList: requireCSSRuleList().CSSRuleList, - CSSStyleRule: requireCSSStyleRule().CSSStyleRule, +function isInitialBorderImage(values) { + const src = values["border-image-source"]; + const slice = values["border-image-slice"]; + const width = values["border-image-width"]; + const outset = values["border-image-outset"]; + const repeat = values["border-image-repeat"]; + if (!src || !slice || !width || !outset || !repeat) return false; + const sSrc = serialize(src).trim().toLowerCase(); + const sSlice = serialize(slice).trim().toLowerCase(); + const sWidth = serialize(width).trim().toLowerCase(); + const sOutset = serialize(outset).trim().toLowerCase(); + const sRepeat = serialize(repeat).trim().toLowerCase(); + return (sSrc === "none" || sSrc === "") && (sSlice === "100%" || sSlice === "100% 100% 100% 100%" || sSlice === "") && (sWidth === "1" || sWidth === "1 1 1 1" || sWidth === "") && (sOutset === "0" || sOutset === "0px" || sOutset === "0s" || sOutset === "0 0 0 0" || sOutset === "") && (sRepeat === "stretch" || sRepeat === "stretch stretch" || sRepeat === ""); +} +const expandBorderSide = (prefix) => (values) => { + const filtered = filterSignificantTokens(values); + if (filtered.length === 0 || filtered.length > 3) return null; + const longhands = [ + `${prefix}-width`, + `${prefix}-style`, + `${prefix}-color` + ]; + const cssWide = tryExpandCssWide(filtered, longhands); + if (cssWide) return cssWide; + const { widthVal, styleVal, colorVal } = parseBorderTriplet(filtered); + return { + [longhands[0]]: widthVal, + [longhands[1]]: styleVal, + [longhands[2]]: colorVal }; - var errorUtils = requireErrorUtils().errorUtils; - ///CommonJS - - - /** - * @constructor - * @param {CSSStyleSheetInit} [opts] - CSSStyleSheetInit options. - * @param {string} [opts.baseURL] - The base URL of the stylesheet. - * @param {boolean} [opts.disabled] - The disabled attribute of the stylesheet. - * @param {MediaList | string} [opts.media] - The media attribute of the stylesheet. - * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet - */ - CSSOM.CSSStyleSheet = function CSSStyleSheet(opts) { - CSSOM.StyleSheet.call(this); - this.__constructed = true; - this.__cssRules = new CSSOM.CSSRuleList(); - this.__ownerRule = null; - - if (opts && typeof opts === "object") { - if (opts.baseURL && typeof opts.baseURL === "string") { - this.__baseURL = opts.baseURL; - } - if (opts.media && typeof opts.media === "string") { - this.media.mediaText = opts.media; - } - if (typeof opts.disabled === "boolean") { - this.disabled = opts.disabled; - } - } +}; +const contractBorderSide = (prefix) => (values) => { + const w = values[`${prefix}-width`]; + const s = values[`${prefix}-style`]; + const c = values[`${prefix}-color`]; + if (!w || !s || !c) return null; + return formatBorderSideValue(serialize(w), serialize(s), serialize(c)); +}; +function expandBorder(values) { + const filtered = filterSignificantTokens(values); + if (filtered.length === 0 || filtered.length > 3) return null; + const cssWide = tryExpandCssWide(filtered, BORDER_ALL_LONGHANDS); + if (cssWide) return cssWide; + const { widthVal, styleVal, colorVal } = parseBorderTriplet(filtered); + return { + "border-top-width": widthVal, + "border-right-width": widthVal, + "border-bottom-width": widthVal, + "border-left-width": widthVal, + "border-top-style": styleVal, + "border-right-style": styleVal, + "border-bottom-style": styleVal, + "border-left-style": styleVal, + "border-top-color": colorVal, + "border-right-color": colorVal, + "border-bottom-color": colorVal, + "border-left-color": colorVal, + ...createInitialBorderImageValues() }; - - - CSSOM.CSSStyleSheet.prototype = Object.create(CSSOM.StyleSheet.prototype); - CSSOM.CSSStyleSheet.prototype.constructor = CSSOM.CSSStyleSheet; - - Object.setPrototypeOf(CSSOM.CSSStyleSheet, CSSOM.StyleSheet); - - Object.defineProperty(CSSOM.CSSStyleSheet.prototype, "cssRules", { - get: function() { - return this.__cssRules; - } - }); - - Object.defineProperty(CSSOM.CSSStyleSheet.prototype, "rules", { - get: function() { - return this.__cssRules; - } - }); - - Object.defineProperty(CSSOM.CSSStyleSheet.prototype, "ownerRule", { - get: function() { - return this.__ownerRule; - } - }); - - /** - * Used to insert a new rule into the style sheet. The new rule now becomes part of the cascade. - * - * sheet = new Sheet("body {margin: 0}") - * sheet.toString() - * -> "body{margin:0;}" - * sheet.insertRule("img {border: none}", 0) - * -> 0 - * sheet.toString() - * -> "img{border:none;}body{margin:0;}" - * - * @param {string} rule - * @param {number} [index=0] - * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet-insertRule - * @return {number} The index within the style sheet's rule collection of the newly inserted rule. - */ - CSSOM.CSSStyleSheet.prototype.insertRule = function(rule, index) { - if (rule === undefined && index === undefined) { - errorUtils.throwMissingArguments(this, 'insertRule', this.constructor.name); - } - if (index === void 0) { - index = 0; - } - index = Number(index); - if (index < 0) { - index = 4294967296 + index; - } - if (index > this.cssRules.length) { - errorUtils.throwIndexError(this, 'insertRule', this.constructor.name, index, this.cssRules.length); - } - - var ruleToParse = String(rule); - var parseErrors = []; - var parsedSheet = CSSOM.parse(ruleToParse, undefined, function(err) { - parseErrors.push(err); - } ); - if (parsedSheet.cssRules.length !== 1) { - errorUtils.throwParseError(this, 'insertRule', this.constructor.name, ruleToParse, 'SyntaxError'); - } - var cssRule = parsedSheet.cssRules[0]; - - // Helper function to find the first index of a rule that's NOT of specified constructors - function findFirstNonConstructorIndex(rules, constructorNames) { - for (var i = 0; i < rules.length; i++) { - if (constructorNames.indexOf(rules[i].constructor.name) === -1) { - return i; - } - } - return rules.length; - } - - // Validate rule ordering based on CSS specification - if (cssRule.constructor.name === 'CSSImportRule') { - if (this.__constructed === true) { - errorUtils.throwError(this, 'DOMException', - "Failed to execute 'insertRule' on '" + this.constructor.name + "': Can't insert @import rules into a constructed stylesheet.", - 'SyntaxError'); - } - // @import rules cannot be inserted after @layer rules that already exist - // They can only be inserted at the beginning or after other @import rules - var firstLayerIndex = findFirstNonConstructorIndex(this.cssRules, ['CSSImportRule']); - if (firstLayerIndex < this.cssRules.length && this.cssRules[firstLayerIndex].constructor.name === 'CSSLayerStatementRule' && index > firstLayerIndex) { - errorUtils.throwError(this, 'DOMException', - "Failed to execute 'insertRule' on '" + this.constructor.name + "': Failed to insert the rule.", - 'HierarchyRequestError'); - } - - // Also cannot insert after @namespace or other rules - var firstNonImportIndex = findFirstNonConstructorIndex(this.cssRules, ['CSSImportRule']); - if (index > firstNonImportIndex && firstNonImportIndex < this.cssRules.length && - this.cssRules[firstNonImportIndex].constructor.name !== 'CSSLayerStatementRule') { - errorUtils.throwError(this, 'DOMException', - "Failed to execute 'insertRule' on '" + this.constructor.name + "': Failed to insert the rule.", - 'HierarchyRequestError'); - } - } else if (cssRule.constructor.name === 'CSSNamespaceRule') { - // @namespace rules can come after @layer and @import, but before any other rules - // They cannot come before @import rules - var firstImportIndex = -1; - for (var i = 0; i < this.cssRules.length; i++) { - if (this.cssRules[i].constructor.name === 'CSSImportRule') { - firstImportIndex = i; - break; - } - } - var firstNonImportNamespaceIndex = findFirstNonConstructorIndex(this.cssRules, [ - 'CSSLayerStatementRule', - 'CSSImportRule', - 'CSSNamespaceRule' - ]); - - // Cannot insert before @import rules - if (firstImportIndex !== -1 && index <= firstImportIndex) { - errorUtils.throwError(this, 'DOMException', - "Failed to execute 'insertRule' on '" + this.constructor.name + "': Failed to insert the rule.", - 'HierarchyRequestError'); - } - - // Cannot insert if there are already non-special rules - if (firstNonImportNamespaceIndex < this.cssRules.length) { - errorUtils.throwError(this, 'DOMException', - "Failed to execute 'insertRule' on '" + this.constructor.name + "': Failed to insert the rule.", - 'InvalidStateError'); - } - - // Cannot insert after other types of rules - if (index > firstNonImportNamespaceIndex) { - errorUtils.throwError(this, 'DOMException', - "Failed to execute 'insertRule' on '" + this.constructor.name + "': Failed to insert the rule.", - 'HierarchyRequestError'); - } - - - } else if (cssRule.constructor.name === 'CSSLayerStatementRule') ; else { - // Any other rule cannot be inserted before @import and @namespace - var firstNonSpecialRuleIndex = findFirstNonConstructorIndex(this.cssRules, [ - 'CSSLayerStatementRule', - 'CSSImportRule', - 'CSSNamespaceRule' - ]); - - if (index < firstNonSpecialRuleIndex) { - errorUtils.throwError(this, 'DOMException', - "Failed to execute 'insertRule' on '" + this.constructor.name + "': Failed to insert the rule.", - 'HierarchyRequestError'); - } - - if (parseErrors.filter(function(error) { return !error.isNested; }).length !== 0) { - errorUtils.throwParseError(this, 'insertRule', this.constructor.name, ruleToParse, 'SyntaxError'); - } - } - - cssRule.__parentStyleSheet = this; - this.cssRules.splice(index, 0, cssRule); - return index; +} +function contractBorder(values) { + for (const lh of BORDER_ALL_LONGHANDS) if (!values[lh]) return null; + const allSerialized = BORDER_ALL_LONGHANDS.map((lh) => serialize(values[lh]).trim()); + const cssWide = checkUniformCssWide(allSerialized); + if (cssWide !== void 0) return cssWide; + if (!isInitialBorderImage(values)) return null; + const [w0, w1, w2, w3, s0, s1, s2, s3, c0, c1, c2, c3] = allSerialized; + if (w0 !== w1 || w0 !== w2 || w0 !== w3) return null; + if (s0 !== s1 || s0 !== s2 || s0 !== s3) return null; + if (c0 !== c1 || c0 !== c2 || c0 !== c3) return null; + return formatBorderSideValue(w0, s0, c0); +} +function expandBorderImage(values) { + const filtered = filterSignificantTokens(values); + if (filtered.length === 0) return null; + const cssWide = tryExpandCssWide(filtered, BORDER_IMAGE_LONGHANDS); + if (cssWide) return cssWide; + if (filtered.length === 1 && filtered[0].type === "ident" && filtered[0].value.toLowerCase() === "none") return createInitialBorderImageValues(); + if (filtered.some((t) => t.type === "function" && getFunctionName(t) === "var")) return expandUniformLonghands(BORDER_IMAGE_LONGHANDS, values); + let source = [{ + type: "ident", + value: "none" + }]; + for (const token of filtered) if (token.type === "url" || token.type === "function" && [ + "linear-gradient", + "radial-gradient", + "conic-gradient", + "image", + "image-set" + ].includes(getFunctionName(token))) source = [token]; + return createInitialBorderImageValues(source); +} +function contractBorderImage(values) { + for (const lh of BORDER_IMAGE_LONGHANDS) if (!values[lh]) return null; + const allVals = BORDER_IMAGE_LONGHANDS.map((lh) => serialize(values[lh]).trim()); + const cssWide = checkUniformCssWide(allVals, true); + if (cssWide !== void 0) return cssWide; + if (isInitialBorderImage(values)) return "none"; + const [sSrc, sSlice, sWidth, sOutset, sRepeat] = allVals; + return (sSlice === "100%" || sSlice === "100% 100% 100% 100%") && (sWidth === "1" || sWidth === "1 1 1 1") && (sOutset === "0" || sOutset === "0px" || sOutset === "0s" || sOutset === "0 0 0 0") && (sRepeat === "stretch" || sRepeat === "stretch stretch") ? sSrc : null; +} +function expandOutline(values) { + const filtered = filterSignificantTokens(values); + if (filtered.length === 0 || filtered.length > 3) return null; + const cssWide = tryExpandCssWide(filtered, [ + "outline-color", + "outline-style", + "outline-width" + ]); + if (cssWide) return cssWide; + const { widthVal, styleVal, colorVal } = parseBorderTriplet(filtered, true); + return { + "outline-color": colorVal, + "outline-style": styleVal, + "outline-width": widthVal }; - - CSSOM.CSSStyleSheet.prototype.addRule = function(selector, styleBlock, index) { - if (index === void 0) { - index = this.cssRules.length; +} +function contractOutline(values) { + const c = values["outline-color"]; + const s = values["outline-style"]; + const w = values["outline-width"]; + if (!c || !s || !w) return null; + const sc = serialize(c).trim(); + const ss = serialize(s).trim(); + const sw = serialize(w).trim(); + const cssWide = checkUniformCssWide([ + sc, + ss, + sw + ]); + if (cssWide !== void 0) return cssWide; + const isInitialColor = sc.toLowerCase() === "currentcolor"; + const isInitialStyle = ss.toLowerCase() === "none"; + const isInitialWidth = sw.toLowerCase() === "medium"; + if (isInitialColor && isInitialStyle && isInitialWidth) return "none"; + const parts = []; + if (!isInitialColor) parts.push(sc); + if (!isInitialStyle) parts.push(ss); + if (!isInitialWidth) parts.push(sw); + return parts.length === 0 ? "none" : parts.join(" "); +} +const FONT_VARIANT_LONGHANDS = [ + "font-variant-ligatures", + "font-variant-caps", + "font-variant-alternates", + "font-variant-numeric", + "font-variant-east-asian", + "font-variant-position", + "font-variant-emoji" +]; +function expandFontVariant(values) { + const filtered = filterSignificantTokens(values); + if (filtered.length === 0) return null; + const wide = tryExpandCssWide(filtered, FONT_VARIANT_LONGHANDS); + if (wide) return wide; + if (filtered.length === 1 && filtered[0].type === "ident") { + const v = filtered[0].value.toLowerCase(); + if (v === "normal") return expandUniformLonghands(FONT_VARIANT_LONGHANDS, [{ + type: "ident", + value: "normal" + }]); + if (v === "none") { + const res = expandUniformLonghands(FONT_VARIANT_LONGHANDS, [{ + type: "ident", + value: "normal" + }]); + res["font-variant-ligatures"] = [{ + type: "ident", + value: "none" + }]; + return res; } - this.insertRule(selector + "{" + styleBlock + "}", index); - return -1; + } + const buckets = { + "font-variant-ligatures": [], + "font-variant-caps": [], + "font-variant-alternates": [], + "font-variant-numeric": [], + "font-variant-east-asian": [], + "font-variant-position": [], + "font-variant-emoji": [] }; - - /** - * Used to delete a rule from the style sheet. - * - * sheet = new Sheet("img{border:none} body{margin:0}") - * sheet.toString() - * -> "img{border:none;}body{margin:0;}" - * sheet.deleteRule(0) - * sheet.toString() - * -> "body{margin:0;}" - * - * @param {number} index within the style sheet's rule list of the rule to remove. - * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet-deleteRule - */ - CSSOM.CSSStyleSheet.prototype.deleteRule = function(index) { - if (index === undefined) { - errorUtils.throwMissingArguments(this, 'deleteRule', this.constructor.name); - } - index = Number(index); - if (index < 0) { - index = 4294967296 + index; - } - if (index >= this.cssRules.length) { - errorUtils.throwIndexError(this, 'deleteRule', this.constructor.name, index, this.cssRules.length); - } - if (this.cssRules[index]) { - if (this.cssRules[index].constructor.name == "CSSNamespaceRule") { - var shouldContinue = this.cssRules.every(function (rule) { - return ['CSSImportRule','CSSLayerStatementRule','CSSNamespaceRule'].indexOf(rule.constructor.name) !== -1 - }); - if (!shouldContinue) { - errorUtils.throwError(this, 'DOMException', "Failed to execute 'deleteRule' on '" + this.constructor.name + "': Failed to delete rule.", "InvalidStateError"); - } + for (const token of filtered) if (token.type === "ident") { + const val = token.value.toLowerCase(); + if (val === "normal") continue; + if (val === "none" || FONT_VARIANT_LIGATURES_KEYWORDS.has(val)) buckets["font-variant-ligatures"].push(token); + else if (FONT_VARIANT_CAPS_KEYWORDS.has(val)) buckets["font-variant-caps"].push(token); + else if (val === "historical-forms") buckets["font-variant-alternates"].push(token); + else if (FONT_VARIANT_NUMERIC_KEYWORDS.has(val)) buckets["font-variant-numeric"].push(token); + else if (FONT_VARIANT_EAST_ASIAN_KEYWORDS.has(val)) buckets["font-variant-east-asian"].push(token); + else if (FONT_VARIANT_POSITION_KEYWORDS.has(val)) buckets["font-variant-position"].push(token); + else if (FONT_VARIANT_EMOJI_KEYWORDS.has(val)) buckets["font-variant-emoji"].push(token); + else return null; + } else if (token.type === "function" && FONT_VARIANT_ALTERNATES_FUNCTIONS.has(getFunctionName(token))) buckets["font-variant-alternates"].push(token); + else return null; + const norm = [{ + type: "ident", + value: "normal" + }]; + const res = {}; + for (const lh of FONT_VARIANT_LONGHANDS) res[lh] = buckets[lh].length > 0 ? joinWithWhitespace(buckets[lh]) : norm; + return res; +} +function contractFontVariant(values) { + for (const lh of FONT_VARIANT_LONGHANDS) if (!values[lh]) return null; + const allVals = FONT_VARIANT_LONGHANDS.map((lh) => serialize(values[lh]).trim()); + const wide = checkUniformCssWide(allVals); + if (wide !== void 0) return wide; + if (allVals.every((v) => v.toLowerCase() === "normal")) return "normal"; + if (allVals[0].toLowerCase() === "none") return allVals.slice(1).every((v) => v.toLowerCase() === "normal") ? "none" : null; + const nonNormal = allVals.filter((v) => v.toLowerCase() !== "normal"); + return nonNormal.length === 0 ? "normal" : nonNormal.join(" "); +} +const FONT_LONGHANDS = [ + "font-style", + "font-variant-caps", + "font-variant-ligatures", + "font-variant-alternates", + "font-variant-numeric", + "font-variant-east-asian", + "font-variant-position", + "font-variant-emoji", + "font-weight", + "font-stretch", + "font-size", + "line-height", + "font-family" +]; +function expandFont(values) { + const filtered = filterSignificantTokens(values); + if (filtered.length === 0) return null; + if (filtered.length === 1 && filtered[0].type === "ident" && SYSTEM_FONT_KEYWORDS.has(filtered[0].value.toLowerCase())) return expandUniformLonghands(FONT_LONGHANDS, [filtered[0]]); + const norm = [{ + type: "ident", + value: "normal" + }]; + let styleVal = norm; + let capsVal = norm; + let weightVal = norm; + let stretchVal = norm; + let lineHeightVal = norm; + let i = 0; + while (i < filtered.length) { + const token = filtered[i]; + if (token.type === "ident") { + const v = token.value.toLowerCase(); + if (v === "italic" || v === "oblique") { + styleVal = [token]; + i++; + continue; } - if (this.cssRules[index].constructor.name == "CSSImportRule") { - this.cssRules[index].styleSheet.__parentStyleSheet = null; + if (v === "small-caps") { + capsVal = [token]; + i++; + continue; } - - this.cssRules[index].__parentStyleSheet = null; - } - this.cssRules.splice(index, 1); - }; - - CSSOM.CSSStyleSheet.prototype.removeRule = function(index) { - if (index === void 0) { - index = 0; - } - this.deleteRule(index); - }; - - - /** - * Replaces the rules of a {@link CSSStyleSheet} - * - * @returns a promise - * @see https://www.w3.org/TR/cssom-1/#dom-cssstylesheet-replace - */ - CSSOM.CSSStyleSheet.prototype.replace = function(text) { - var _Promise; - if (this.__globalObject && this.__globalObject['Promise']) { - _Promise = this.__globalObject['Promise']; - } else { - _Promise = Promise; - } - var _setTimeout; - if (this.__globalObject && this.__globalObject['setTimeout']) { - _setTimeout = this.__globalObject['setTimeout']; - } else { - _setTimeout = setTimeout; - } - var sheet = this; - return new _Promise(function (resolve, reject) { - // If the constructed flag is not set, or the disallow modification flag is set, throw a NotAllowedError DOMException. - if (!sheet.__constructed || sheet.__disallowModification) { - reject(errorUtils.createError(sheet, 'DOMException', - "Failed to execute 'replaceSync' on '" + sheet.constructor.name + "': Not allowed.", - 'NotAllowedError')); + if (v === "bold" || v === "bolder" || v === "lighter") { + weightVal = [token]; + i++; + continue; } - // Set the disallow modification flag. - sheet.__disallowModification = true; - - // In parallel, do these steps: - _setTimeout(function() { - // Let rules be the result of running parse a stylesheet's contents from text. - var rules = new CSSOM.CSSRuleList(); - CSSOM.parse(text, { styleSheet: sheet, cssRules: rules }); - // If rules contains one or more @import rules, remove those rules from rules. - var i = 0; - while (i < rules.length) { - if (rules[i].constructor.name === 'CSSImportRule') { - rules.splice(i, 1); - } else { - i++; - } - } - // Set sheet's CSS rules to rules. - sheet.__cssRules.splice.apply(sheet.__cssRules, [0, sheet.__cssRules.length].concat(rules)); - // Unset sheet’s disallow modification flag. - delete sheet.__disallowModification; - // Resolve promise with sheet. - resolve(sheet); - }); - }); - }; - - /** - * Synchronously replaces the rules of a {@link CSSStyleSheet} - * - * @see https://www.w3.org/TR/cssom-1/#dom-cssstylesheet-replacesync - */ - CSSOM.CSSStyleSheet.prototype.replaceSync = function(text) { - var sheet = this; - // If the constructed flag is not set, or the disallow modification flag is set, throw a NotAllowedError DOMException. - if (!sheet.__constructed || sheet.__disallowModification) { - errorUtils.throwError(sheet, 'DOMException', - "Failed to execute 'replaceSync' on '" + sheet.constructor.name + "': Not allowed.", - 'NotAllowedError'); - } - // Let rules be the result of running parse a stylesheet's contents from text. - var rules = new CSSOM.CSSRuleList(); - CSSOM.parse(text, { styleSheet: sheet, cssRules: rules }); - // If rules contains one or more @import rules, remove those rules from rules. - var i = 0; - while (i < rules.length) { - if (rules[i].constructor.name === 'CSSImportRule') { - rules.splice(i, 1); - } else { + if (FONT_STRETCH_KEYWORDS.has(v)) { + stretchVal = [token]; i++; + continue; } + if (v === "normal") { + i++; + continue; + } + } else if (token.type === "number" && typeof token.value === "number" && token.value >= 1 && token.value <= 1e3) { + weightVal = [token]; + i++; + continue; } - // Set sheet's CSS rules to rules. - sheet.__cssRules.splice.apply(sheet.__cssRules, [0, sheet.__cssRules.length].concat(rules)); - }; - - /** - * NON-STANDARD - * @return {string} serialize stylesheet - */ - CSSOM.CSSStyleSheet.prototype.toString = function() { - var result = ""; - var rules = this.cssRules; - for (var i=0; i= filtered.length) return null; + const sizeToken = filtered[i]; + if (!(sizeToken.type === "dimension" || sizeToken.type === "percentage" || sizeToken.type === "number" && sizeToken.value === 0 || sizeToken.type === "ident" && FONT_SIZE_KEYWORDS.has(sizeToken.value.toLowerCase()) || sizeToken.type === "function" && MATH_FUNCTIONS.has(getFunctionName(sizeToken)))) return null; + const sizeVal = [sizeToken]; + i++; + if (i < filtered.length && filtered[i].type === "delim" && filtered[i].value === "/") { + i++; + if (i >= filtered.length) return null; + const lhToken = filtered[i]; + if (!(lhToken.type === "number" || lhToken.type === "dimension" || lhToken.type === "percentage" || lhToken.type === "ident" && lhToken.value.toLowerCase() === "normal" || lhToken.type === "function" && MATH_FUNCTIONS.has(getFunctionName(lhToken)))) return null; + lineHeightVal = [lhToken]; + i++; + } + if (i >= filtered.length) return null; + const lastConsumed = lineHeightVal !== norm ? lineHeightVal[0] : sizeToken; + const lastIdx = values.indexOf(lastConsumed); + let familyVal; + if (lastIdx !== -1) { + familyVal = values.slice(lastIdx + 1).filter((t) => t.type !== "EOF"); + while (familyVal.length > 0 && (familyVal[0].type === "whitespace" || familyVal[0].type === "comment")) familyVal.shift(); + } else familyVal = filtered.slice(i); + const res = expandUniformLonghands(FONT_LONGHANDS, norm); + res["font-style"] = styleVal; + res["font-variant-caps"] = capsVal; + res["font-weight"] = weightVal; + res["font-stretch"] = stretchVal; + res["font-size"] = sizeVal; + res["line-height"] = lineHeightVal; + res["font-family"] = familyVal; + return res; +} +const FONT_PRIMARY_LONGHANDS = [ + "font-style", + "font-variant-caps", + "font-weight", + "font-stretch", + "font-size", + "line-height", + "font-family" +]; +const FONT_OTHER_VARIANTS = [ + "font-variant-ligatures", + "font-variant-alternates", + "font-variant-numeric", + "font-variant-east-asian", + "font-variant-position", + "font-variant-emoji" +]; +function contractFont(values) { + for (const lh of FONT_PRIMARY_LONGHANDS) if (!values[lh]) return null; + for (const lh of FONT_OTHER_VARIANTS) if (values[lh] && serialize(values[lh]).trim().toLowerCase() !== "normal") return null; + const sStyle = serialize(values["font-style"]).trim(); + const sCaps = serialize(values["font-variant-caps"]).trim(); + const sWeight = serialize(values["font-weight"]).trim(); + const sStretch = serialize(values["font-stretch"]).trim(); + const sSize = serialize(values["font-size"]).trim(); + const sLineHeight = serialize(values["line-height"]).trim(); + const sFamily = serialize(values["font-family"], false, "font-family").trim(); + const wide = checkUniformCssWide([ + sStyle, + sCaps, + sWeight, + sStretch, + sSize, + sLineHeight, + sFamily + ]); + if (wide !== void 0) return wide; + if (!sSize || !sFamily) return null; + const parts = []; + if (sStyle.toLowerCase() !== "normal") parts.push(sStyle); + if (sCaps.toLowerCase() !== "normal") parts.push(sCaps); + if (sWeight.toLowerCase() !== "normal" && sWeight !== "400") parts.push(sWeight); + if (sStretch.toLowerCase() !== "normal") parts.push(sStretch); + parts.push(sLineHeight.toLowerCase() !== "normal" ? `${sSize} / ${sLineHeight}` : sSize); + parts.push(sFamily); + return parts.join(" "); +} +const LIST_STYLE_LONGHANDS = [ + "list-style-type", + "list-style-position", + "list-style-image" +]; +function expandListStyle(values) { + const filtered = filterSignificantTokens(values); + if (filtered.length === 0 || filtered.length > 3) return null; + const wide = tryExpandCssWide(filtered, LIST_STYLE_LONGHANDS); + if (wide) return wide; + let typeVal = [{ + type: "ident", + value: "disc" + }]; + let posVal = [{ + type: "ident", + value: "outside" + }]; + let imgVal = [{ + type: "ident", + value: "none" + }]; + let hasType = false; + let hasPos = false; + let hasImg = false; + for (const token of filtered) if (token.type === "ident") { + const v = token.value.toLowerCase(); + if ((v === "inside" || v === "outside") && !hasPos) { + posVal = [token]; + hasPos = true; + } else if (v === "none") { + if (!hasImg && !hasType) { + imgVal = [token]; + typeVal = [token]; + hasImg = true; + hasType = true; + } else if (!hasImg) { + imgVal = [token]; + hasImg = true; + } else if (!hasType) { + typeVal = [token]; + hasType = true; + } + } else if (!hasType) { + typeVal = [token]; + hasType = true; + } else return null; + } else if ((token.type === "url" || token.type === "function" && IMAGE_FUNCTIONS.has(getFunctionName(token))) && !hasImg) { + imgVal = [token]; + hasImg = true; + } else return null; + return { + "list-style-type": typeVal, + "list-style-position": posVal, + "list-style-image": imgVal }; - - - //.CommonJS - CSSStyleSheet.CSSStyleSheet = CSSOM.CSSStyleSheet; - CSSOM.parse = requireParse().parse; // Cannot be included sooner due to the mutual dependency between parse.js and CSSStyleSheet.js - ///CommonJS - return CSSStyleSheet; } - -var CSSNestedDeclarations = {}; - -var hasRequiredCSSNestedDeclarations; - -function requireCSSNestedDeclarations () { - if (hasRequiredCSSNestedDeclarations) return CSSNestedDeclarations; - hasRequiredCSSNestedDeclarations = 1; - //.CommonJS - var CSSOM = { - CSSRule: requireCSSRule().CSSRule, - CSSStyleDeclaration: requireCSSStyleDeclaration().CSSStyleDeclaration +function contractListStyle(values) { + const t = values["list-style-type"]; + const p = values["list-style-position"]; + const i = values["list-style-image"]; + if (!t || !p || !i) return null; + const st = serialize(t).trim(); + const sp = serialize(p).trim(); + const si = serialize(i).trim(); + const wide = checkUniformCssWide([ + st, + sp, + si + ]); + if (wide !== void 0) return wide; + const parts = []; + if (sp.toLowerCase() !== "outside") parts.push(sp); + if (si.toLowerCase() !== "none") parts.push(si); + if (st.toLowerCase() !== "disc") parts.push(st); + return parts.length === 0 ? "disc" : parts.join(" "); +} +const FLEX_LONGHANDS = [ + "flex-grow", + "flex-shrink", + "flex-basis" +]; +function expandFlex(values) { + const filtered = filterSignificantTokens(values); + if (filtered.length === 0 || filtered.length > 3) return null; + const wide = tryExpandCssWide(filtered, FLEX_LONGHANDS); + if (wide) return wide; + if (filtered.length === 1 && filtered[0].type === "ident") { + const v = filtered[0].value.toLowerCase(); + if (v === "none") return { + "flex-grow": [{ + type: "number", + value: 0, + sign: null, + numberType: "integer" + }], + "flex-shrink": [{ + type: "number", + value: 0, + sign: null, + numberType: "integer" + }], + "flex-basis": [{ + type: "ident", + value: "auto" + }] + }; + if (v === "auto") return { + "flex-grow": [{ + type: "number", + value: 1, + sign: null, + numberType: "integer" + }], + "flex-shrink": [{ + type: "number", + value: 1, + sign: null, + numberType: "integer" + }], + "flex-basis": [{ + type: "ident", + value: "auto" + }] + }; + } + let grow = null; + let shrink = null; + let basis = null; + for (const token of filtered) if (token.type === "number") { + if (grow === null) grow = [token]; + else if (shrink === null) shrink = [token]; + else return null; + } else if (isValidLengthOrPercentage(token) || token.type === "ident" && FLEX_BASIS_KEYWORDS.has(token.value.toLowerCase())) { + if (basis === null) basis = [token]; + else return null; + } else return null; + if (grow === null && basis === null) return null; + const one = [{ + type: "number", + value: 1, + sign: null, + numberType: "integer" + }]; + return { + "flex-grow": grow ?? one, + "flex-shrink": shrink ?? one, + "flex-basis": basis ?? (grow !== null ? [{ + type: "dimension", + value: 0, + unit: "px", + sign: null, + numberType: "integer" + }] : [{ + type: "ident", + value: "auto" + }]) }; - // Use cssstyle if available - try { - CSSOM.CSSStyleDeclaration = require("cssstyle").CSSStyleDeclaration; - } catch (e) { - // ignore +} +function contractFlex(values) { + const g = values["flex-grow"]; + const s = values["flex-shrink"]; + const b = values["flex-basis"]; + if (!g || !s || !b) return null; + const sg = serialize(g).trim(); + const ss = serialize(s).trim(); + const sb = serialize(b).trim(); + const wide = checkUniformCssWide([ + sg, + ss, + sb + ]); + if (wide !== void 0) return wide; + if (sg.includes("var(") || ss.includes("var(") || sb.includes("var(")) return sg === ss && sg === sb ? sg : null; + if (sb.toLowerCase() === "auto") { + if (sg === "0" && ss === "1") return "initial"; + if (sg === "1" && ss === "1") return "auto"; + if (sg === "0" && ss === "0") return "none"; } - ///CommonJS - - - /** - * @constructor - * @see https://drafts.csswg.org/css-nesting-1/ - */ - CSSOM.CSSNestedDeclarations = function CSSNestedDeclarations() { - CSSOM.CSSRule.call(this); - this.__style = new CSSOM.CSSStyleDeclaration(); - this.__style.parentRule = this; - }; - - CSSOM.CSSNestedDeclarations.prototype = Object.create(CSSOM.CSSRule.prototype); - CSSOM.CSSNestedDeclarations.prototype.constructor = CSSOM.CSSNestedDeclarations; - - Object.setPrototypeOf(CSSOM.CSSNestedDeclarations, CSSOM.CSSRule); - - Object.defineProperty(CSSOM.CSSNestedDeclarations.prototype, "type", { - value: 0, - writable: false - }); - - Object.defineProperty(CSSOM.CSSNestedDeclarations.prototype, "style", { - get: function() { - return this.__style; - }, - set: function(value) { - if (typeof value === "string") { - this.__style.cssText = value; - } else { - this.__style = value; - } - } - }); - - Object.defineProperty(CSSOM.CSSNestedDeclarations.prototype, "cssText", { - get: function () { - return this.style.cssText; - } - }); - - //.CommonJS - CSSNestedDeclarations.CSSNestedDeclarations = CSSOM.CSSNestedDeclarations; - ///CommonJS - return CSSNestedDeclarations; + if ((sb === "0px" || sb === "0%" || sb === "0") && ss === "1") return `${sg} 1 0px`; + return `${sg} ${ss} ${sb}`; } - -var CSSImportRule = {}; - -var hasRequiredCSSImportRule; - -function requireCSSImportRule () { - if (hasRequiredCSSImportRule) return CSSImportRule; - hasRequiredCSSImportRule = 1; - //.CommonJS - var CSSOM = { - CSSRule: requireCSSRule().CSSRule, - CSSStyleSheet: requireCSSStyleSheet().CSSStyleSheet, - MediaList: requireMediaList().MediaList - }; - var regexPatterns = requireRegexPatterns().regexPatterns; - ///CommonJS - - - /** - * @constructor - * @see http://dev.w3.org/csswg/cssom/#cssimportrule - * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSImportRule - */ - CSSOM.CSSImportRule = function CSSImportRule() { - CSSOM.CSSRule.call(this); - this.__href = ""; - this.__media = new CSSOM.MediaList(); - this.__layerName = null; - this.__supportsText = null; - this.__styleSheet = new CSSOM.CSSStyleSheet(); - }; - - CSSOM.CSSImportRule.prototype = Object.create(CSSOM.CSSRule.prototype); - CSSOM.CSSImportRule.prototype.constructor = CSSOM.CSSImportRule; - - Object.setPrototypeOf(CSSOM.CSSImportRule, CSSOM.CSSRule); - - Object.defineProperty(CSSOM.CSSImportRule.prototype, "type", { - value: 3, - writable: false - }); - - Object.defineProperty(CSSOM.CSSImportRule.prototype, "cssText", { - get: function() { - var mediaText = this.media.mediaText; - return "@import url(\"" + this.href.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + "\")" + (this.layerName !== null ? " layer" + (this.layerName && "(" + this.layerName + ")") : "" ) + (this.supportsText ? " supports(" + this.supportsText + ")" : "" ) + (mediaText ? " " + mediaText : "") + ";"; - } - }); - - Object.defineProperty(CSSOM.CSSImportRule.prototype, "href", { - get: function() { - return this.__href; - } - }); - - Object.defineProperty(CSSOM.CSSImportRule.prototype, "media", { - get: function() { - return this.__media; - }, - set: function(value) { - if (typeof value === "string") { - this.__media.mediaText = value; - } else { - this.__media = value; - } - } - }); - - Object.defineProperty(CSSOM.CSSImportRule.prototype, "layerName", { - get: function() { - return this.__layerName; - } - }); - - Object.defineProperty(CSSOM.CSSImportRule.prototype, "supportsText", { - get: function() { - return this.__supportsText; - } - }); - - Object.defineProperty(CSSOM.CSSImportRule.prototype, "styleSheet", { - get: function() { - return this.__styleSheet; - } - }); - - /** - * NON-STANDARD - * Rule text parser. - * @param {string} cssText - */ - Object.defineProperty(CSSOM.CSSImportRule.prototype, "parse", { - value: function(cssText) { - var i = 0; - - /** - * @import url(partial.css) screen, handheld; - * || | - * after-import media - * | - * url - */ - var state = ''; - - var buffer = ''; - var index; - - var layerRegExp = regexPatterns.layerRegExp; - var layerRuleNameRegExp = regexPatterns.layerRuleNameRegExp; - var doubleOrMoreSpacesRegExp = regexPatterns.doubleOrMoreSpacesRegExp; - - /** - * Extracts the content inside supports() handling nested parentheses. - * @param {string} text - The text to parse - * @returns {object|null} - {content: string, endIndex: number} or null if not found - */ - function extractSupportsContent(text) { - var supportsIndex = text.indexOf('supports('); - if (supportsIndex !== 0) { - return null; - } - - var depth = 0; - var start = supportsIndex + 'supports('.length; - var i = start; - - for (; i < text.length; i++) { - if (text[i] === '(') { - depth++; - } else if (text[i] === ')') { - if (depth === 0) { - // Found the closing parenthesis for supports() - return { - content: text.slice(start, i), - endIndex: i - }; - } - depth--; - } - } - - return null; // Unbalanced parentheses - } - - for (var character; (character = cssText.charAt(i)); i++) { - - switch (character) { - case ' ': - case '\t': - case '\r': - case '\n': - case '\f': - if (state === 'after-import') { - state = 'url'; - } else { - buffer += character; - } - break; - - case '@': - if (!state && cssText.indexOf('@import', i) === i) { - state = 'after-import'; - i += 'import'.length; - buffer = ''; - } - break; - - case 'u': - if (state === 'media') { - buffer += character; - } - if (state === 'url' && cssText.indexOf('url(', i) === i) { - index = cssText.indexOf(')', i + 1); - if (index === -1) { - throw i + ': ")" not found'; - } - i += 'url('.length; - var url = cssText.slice(i, index); - if (url[0] === url[url.length - 1]) { - if (url[0] === '"' || url[0] === "'") { - url = url.slice(1, -1); - } - } - this.__href = url; - i = index; - state = 'media'; - } - break; - - case '"': - if (state === 'after-import' || state === 'url') { - index = cssText.indexOf('"', i + 1); - if (!index) { - throw i + ": '\"' not found"; - } - this.__href = cssText.slice(i + 1, index); - i = index; - state = 'media'; - } - break; - - case "'": - if (state === 'after-import' || state === 'url') { - index = cssText.indexOf("'", i + 1); - if (!index) { - throw i + ': "\'" not found'; - } - this.__href = cssText.slice(i + 1, index); - i = index; - state = 'media'; - } - break; - - case ';': - if (state === 'media') { - if (buffer) { - var bufferTrimmed = buffer.trim(); - - if (bufferTrimmed.indexOf('layer') === 0) { - var layerMatch = bufferTrimmed.match(layerRegExp); - - if (layerMatch) { - var layerName = layerMatch[1].trim(); - - if (layerName.match(layerRuleNameRegExp) !== null) { - this.__layerName = layerMatch[1].trim(); - bufferTrimmed = bufferTrimmed.replace(layerRegExp, '') - .replace(doubleOrMoreSpacesRegExp, ' ') // Replace double or more spaces with single space - .trim(); - } else { - // REVIEW: In the browser, an empty layer() is not processed as a unamed layer - // and treats the rest of the string as mediaText, ignoring the parse of supports() - if (bufferTrimmed) { - this.media.mediaText = bufferTrimmed; - return; - } - } - } else { - this.__layerName = ""; - bufferTrimmed = bufferTrimmed.substring('layer'.length).trim(); - } - } - - var supportsResult = extractSupportsContent(bufferTrimmed); - - if (supportsResult) { - // REVIEW: In the browser, an empty supports() invalidates and ignores the entire @import rule - this.__supportsText = supportsResult.content.trim(); - // Remove the entire supports(...) from the buffer - bufferTrimmed = bufferTrimmed.slice(0, 0) + bufferTrimmed.slice(supportsResult.endIndex + 1); - bufferTrimmed = bufferTrimmed.replace(doubleOrMoreSpacesRegExp, ' ').trim(); - } - - // REVIEW: In the browser, any invalid media is replaced with 'not all' - if (bufferTrimmed) { - this.media.mediaText = bufferTrimmed; - } - } - } - break; - - default: - if (state === 'media') { - buffer += character; - } - break; - } - } - } - }); - - - //.CommonJS - CSSImportRule.CSSImportRule = CSSOM.CSSImportRule; - ///CommonJS - return CSSImportRule; +function contractOverflow(values) { + const x = values["overflow-x"]; + const y = values["overflow-y"]; + if (!x || !y) return null; + const sx = serialize(x).trim(); + const sy = serialize(y).trim(); + const wide = checkUniformCssWide([sx, sy]); + if (wide !== void 0) return wide; + if (sx.includes("var(") || sy.includes("var(")) return sx === sy ? sx : null; + return sx === sy ? sx : `${sx} ${sy}`; } - -var CSSNamespaceRule = {}; - -var hasRequiredCSSNamespaceRule; - -function requireCSSNamespaceRule () { - if (hasRequiredCSSNamespaceRule) return CSSNamespaceRule; - hasRequiredCSSNamespaceRule = 1; - //.CommonJS - var CSSOM = { - CSSRule: requireCSSRule().CSSRule, - CSSStyleSheet: requireCSSStyleSheet().CSSStyleSheet +const LINE_CLAMP_LONGHANDS = [ + "max-lines", + "block-ellipsis", + "continue" +]; +function expandLineClamp(values) { + const filtered = filterSignificantTokens(values); + if (filtered.length === 0) return null; + const wide = tryExpandCssWide(filtered, LINE_CLAMP_LONGHANDS); + if (wide) return wide; + const autoToken = [{ + type: "ident", + value: "auto" + }]; + if (filtered.length === 1 && filtered[0].type === "ident" && filtered[0].value.toLowerCase() === "none") return { + "max-lines": [{ + type: "ident", + value: "none" + }], + "block-ellipsis": autoToken, + "continue": autoToken }; - ///CommonJS - - - /** - * @constructor - * @see https://drafts.csswg.org/cssom/#the-cssnamespacerule-interface - */ - CSSOM.CSSNamespaceRule = function CSSNamespaceRule() { - CSSOM.CSSRule.call(this); - this.__prefix = ""; - this.__namespaceURI = ""; + return { + "max-lines": filtered, + "block-ellipsis": autoToken, + "continue": autoToken }; - - CSSOM.CSSNamespaceRule.prototype = Object.create(CSSOM.CSSRule.prototype); - CSSOM.CSSNamespaceRule.prototype.constructor = CSSOM.CSSNamespaceRule; - - Object.setPrototypeOf(CSSOM.CSSNamespaceRule, CSSOM.CSSRule); - - Object.defineProperty(CSSOM.CSSNamespaceRule.prototype, "type", { - value: 10, - writable: false - }); - - Object.defineProperty(CSSOM.CSSNamespaceRule.prototype, "cssText", { - get: function() { - return "@namespace" + (this.prefix && " " + this.prefix) + " url(\"" + this.namespaceURI + "\");"; - } - }); - - Object.defineProperty(CSSOM.CSSNamespaceRule.prototype, "prefix", { - get: function() { - return this.__prefix; - } - }); - - Object.defineProperty(CSSOM.CSSNamespaceRule.prototype, "namespaceURI", { - get: function() { - return this.__namespaceURI; - } - }); - - - /** - * NON-STANDARD - * Rule text parser. - * @param {string} cssText - */ - Object.defineProperty(CSSOM.CSSNamespaceRule.prototype, "parse", { - value: function(cssText) { - var newPrefix = ""; - var newNamespaceURI = ""; - - // Remove @namespace and trim - var text = cssText.trim(); - if (text.indexOf('@namespace') === 0) { - text = text.slice('@namespace'.length).trim(); - } - - // Remove trailing semicolon if present - if (text.charAt(text.length - 1) === ';') { - text = text.slice(0, -1).trim(); - } - - // Regex to match valid namespace syntax: - // 1. [optional prefix] url("...") or [optional prefix] url('...') or [optional prefix] url() or [optional prefix] url(unquoted) - // 2. [optional prefix] "..." or [optional prefix] '...' - // The prefix must be a valid CSS identifier (letters, digits, hyphens, underscores, starting with letter or underscore) - var re = /^(?:([a-zA-Z_][a-zA-Z0-9_-]*)\s+)?(?:url\(\s*(?:(['"])(.*?)\2\s*|([^)]*?))\s*\)|(['"])(.*?)\5)$/; - var match = text.match(re); - - if (match) { - // If prefix is present - if (match[1]) { - newPrefix = match[1]; - } - // If url(...) form with quotes - if (typeof match[3] !== "undefined") { - newNamespaceURI = match[3]; - } - // If url(...) form without quotes - else if (typeof match[4] !== "undefined") { - newNamespaceURI = match[4].trim(); - } - // If quoted string form - else if (typeof match[6] !== "undefined") { - newNamespaceURI = match[6]; - } - - this.__prefix = newPrefix; - this.__namespaceURI = newNamespaceURI; - } else { - throw new DOMException("Invalid @namespace rule", "InvalidStateError"); - } - } +} +function contractLineClamp(values) { + const lines = values["max-lines"]; + if (!lines) return null; + const sLines = serialize(lines).trim(); + return sLines.toLowerCase() === "none" ? "none" : sLines; +} +const BORDER_RADIUS_PHYSICAL = [ + "border-top-left-radius", + "border-top-right-radius", + "border-bottom-right-radius", + "border-bottom-left-radius" +]; +function expandBorderRadius(values) { + const filtered = filterSignificantTokens(values); + if (filtered.length === 0) return null; + if (filtered[0].type === "ident" && filtered[0].value.toLowerCase() === "logical") return null; + const slashIndex = filtered.findIndex((v) => v.type === "delim" && v.value === "/"); + let hValues; + let vValues; + if (slashIndex !== -1) { + hValues = filtered.slice(0, slashIndex); + vValues = filtered.slice(slashIndex + 1); + if (hValues.length === 0 || hValues.length > 4 || vValues.length === 0 || vValues.length > 4) return null; + if (vValues.some((v) => v.type === "delim" && v.value === "/")) return null; + } else { + if (filtered.length > 4) return null; + hValues = filtered; + vValues = filtered; + } + const hExpanded = expandFourSides(hValues); + const vExpanded = expandFourSides(vValues); + const result = {}; + for (let i = 0; i < 4; i++) { + const h = hExpanded[i]; + const v = vExpanded[i]; + result[BORDER_RADIUS_PHYSICAL[i]] = serialize(h) === serialize(v) ? h : [ + ...h, + { + type: "whitespace", + value: " " + }, + ...v + ]; + } + return result; +} +function contractBorderRadius(values) { + if (!BORDER_RADIUS_PHYSICAL.every((prop) => values[prop] !== void 0)) return null; + const longhands = BORDER_RADIUS_PHYSICAL.map((prop) => values[prop]); + const wide = checkUniformCssWide(longhands.map((lh) => serialize(lh).trim())); + if (wide !== void 0) return wide; + const parsed = longhands.map((lh) => { + const filtered = filterSignificantTokens(lh); + return { + h: serialize([filtered[0]]).trim(), + v: serialize([filtered.length > 1 ? filtered[1] : filtered[0]]).trim() + }; }); - //.CommonJS - CSSNamespaceRule.CSSNamespaceRule = CSSOM.CSSNamespaceRule; - ///CommonJS - return CSSNamespaceRule; + const hStr = formatFourSides(parsed[0].h, parsed[1].h, parsed[2].h, parsed[3].h); + const vStr = formatFourSides(parsed[0].v, parsed[1].v, parsed[2].v, parsed[3].v); + return hStr === vStr ? hStr : `${hStr} / ${vStr}`; } - -var CSSMediaRule = {}; - -var CSSConditionRule = {}; - -var hasRequiredCSSConditionRule; - -function requireCSSConditionRule () { - if (hasRequiredCSSConditionRule) return CSSConditionRule; - hasRequiredCSSConditionRule = 1; - //.CommonJS - var CSSOM = { - CSSRule: requireCSSRule().CSSRule, - CSSRuleList: requireCSSRuleList().CSSRuleList, - CSSGroupingRule: requireCSSGroupingRule().CSSGroupingRule - }; - ///CommonJS - - - /** - * @constructor - * @see https://www.w3.org/TR/css-conditional-3/#the-cssconditionrule-interface - */ - CSSOM.CSSConditionRule = function CSSConditionRule() { - CSSOM.CSSGroupingRule.call(this); - this.__conditionText = ''; +const logicalLonghands = []; +const physicalLonghands = []; +for (const prop of SUPPORTED_PROPERTIES) if (prop !== "all" && prop !== "direction" && prop !== "unicode-bidi" && !prop.startsWith("--") && !(prop in SHORTHANDS_DATA)) { + if (prop in LOGICAL_MAPPING) logicalLonghands.push(prop); + else physicalLonghands.push(prop); +} +const ALL_SHORTHAND_LONGHANDS = Object.freeze([...logicalLonghands, ...physicalLonghands]); +function isCSSWideKeywordOrVar(tokens) { + const nonWs = filterSignificantTokens(tokens); + if (nonWs.length === 1 && nonWs[0].type === "ident" && CSS_WIDE_KEYWORDS$2.has(nonWs[0].value.toLowerCase())) return true; + return nonWs.some((t) => t.type === "function" && getFunctionName(t) === "var"); +} +function expandAll(value) { + if (!value || value.length === 0 || !isCSSWideKeywordOrVar(value)) return null; + return expandUniformLonghands(ALL_SHORTHAND_LONGHANDS, value); +} +function contractAll(longhands) { + let firstVal = null; + for (const lh of ALL_SHORTHAND_LONGHANDS) { + const valTokens = longhands[lh]; + if (!valTokens || valTokens.length === 0) return null; + const serialized = serialize(valTokens).trim(); + if (firstVal === null) firstVal = serialized; + else if (serialized !== firstVal) return null; + } + if (!firstVal) return null; + const lower = firstVal.toLowerCase(); + return CSS_WIDE_KEYWORDS$2.has(lower) || lower.startsWith("var(") ? firstVal : null; +} +function createBoxShorthand(name, physical, logical) { + return { + longhands: SHORTHANDS_DATA[name], + logicalLonghands: logical, + expand: expandBox(physical, logical), + contract: contractBox(physical, logical) }; - - CSSOM.CSSConditionRule.prototype = Object.create(CSSOM.CSSGroupingRule.prototype); - CSSOM.CSSConditionRule.prototype.constructor = CSSOM.CSSConditionRule; - - Object.setPrototypeOf(CSSOM.CSSConditionRule, CSSOM.CSSGroupingRule); - - Object.defineProperty(CSSOM.CSSConditionRule.prototype, "conditionText", { - get: function () { - return this.__conditionText; - } - }); - - //.CommonJS - CSSConditionRule.CSSConditionRule = CSSOM.CSSConditionRule; - ///CommonJS - return CSSConditionRule; } - -var hasRequiredCSSMediaRule; - -function requireCSSMediaRule () { - if (hasRequiredCSSMediaRule) return CSSMediaRule; - hasRequiredCSSMediaRule = 1; - //.CommonJS - var CSSOM = { - CSSRule: requireCSSRule().CSSRule, - CSSRuleList: requireCSSRuleList().CSSRuleList, - CSSGroupingRule: requireCSSGroupingRule().CSSGroupingRule, - CSSConditionRule: requireCSSConditionRule().CSSConditionRule, - MediaList: requireMediaList().MediaList +function createTwoValueShorthand(name, physicalLonghands) { + const longhands = SHORTHANDS_DATA[name]; + return { + longhands, + ...physicalLonghands ? { physicalLonghands } : {}, + expand: expandTwoValue(longhands), + contract: contractTwoValue(longhands) }; - ///CommonJS - - - /** - * @constructor - * @see http://dev.w3.org/csswg/cssom/#cssmediarule - * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSMediaRule - */ - CSSOM.CSSMediaRule = function CSSMediaRule() { - CSSOM.CSSConditionRule.call(this); - this.__media = new CSSOM.MediaList(); +} +function createBorderSideShorthand(prefix, longhands, physicalLonghands) { + return { + longhands, + ...physicalLonghands ? { physicalLonghands } : {}, + expand: expandBorderSide(prefix), + contract: contractBorderSide(prefix) }; - - CSSOM.CSSMediaRule.prototype = Object.create(CSSOM.CSSConditionRule.prototype); - CSSOM.CSSMediaRule.prototype.constructor = CSSOM.CSSMediaRule; - - Object.setPrototypeOf(CSSOM.CSSMediaRule, CSSOM.CSSConditionRule); - - Object.defineProperty(CSSOM.CSSMediaRule.prototype, "type", { - value: 4, - writable: false - }); - - // https://opensource.apple.com/source/WebCore/WebCore-7611.1.21.161.3/css/CSSMediaRule.cpp - Object.defineProperties(CSSOM.CSSMediaRule.prototype, { - "media": { - get: function() { - return this.__media; - }, - set: function(value) { - if (typeof value === "string") { - this.__media.mediaText = value; - } else { - this.__media = value; - } - }, - configurable: true, - enumerable: true - }, - "conditionText": { - get: function() { - return this.media.mediaText; - } - }, - "cssText": { - get: function() { - var values = ""; - var valuesArr = [" {"]; - if (this.cssRules.length) { - valuesArr.push(this.cssRules.reduce(function(acc, rule){ - if (rule.cssText !== "") { - acc.push(rule.cssText); - } - return acc; - }, []).join("\n ")); - } - values = valuesArr.join("\n ") + "\n}"; - return "@media " + this.media.mediaText + values; - } - } - }); - - - //.CommonJS - CSSMediaRule.CSSMediaRule = CSSOM.CSSMediaRule; - ///CommonJS - return CSSMediaRule; } - -var CSSCounterStyleRule = {}; - -var hasRequiredCSSCounterStyleRule; - -function requireCSSCounterStyleRule () { - if (hasRequiredCSSCounterStyleRule) return CSSCounterStyleRule; - hasRequiredCSSCounterStyleRule = 1; - //.CommonJS - var CSSOM = { - CSSRule: requireCSSRule().CSSRule +function createBorderAxisShorthand(name, startProp, endProp, physicalLonghands, logicalLonghands) { + return { + longhands: SHORTHANDS_DATA[name], + physicalLonghands, + logicalLonghands, + expand: (values) => { + if (filterSignificantTokens(values).length === 0) return null; + return { + [startProp]: values, + [endProp]: values + }; + }, + contract: (values) => { + const sVal = values[startProp]; + const eVal = values[endProp]; + const start = sVal ? serialize(sVal).trim() : contractBorderSide(startProp)(values); + const end = eVal ? serialize(eVal).trim() : contractBorderSide(endProp)(values); + return start && end && start === end ? start : null; + } }; - ///CommonJS - - +} +const SHORTHANDS = { + "border-block": createBorderAxisShorthand("border-block", "border-block-start", "border-block-end", [ + "border-top-width", + "border-top-style", + "border-top-color", + "border-bottom-width", + "border-bottom-style", + "border-bottom-color" + ], [ + "border-block-start-width", + "border-block-start-style", + "border-block-start-color", + "border-block-end-width", + "border-block-end-style", + "border-block-end-color" + ]), + "border-block-color": createTwoValueShorthand("border-block-color", ["border-top-color", "border-bottom-color"]), + "border-block-end": createBorderSideShorthand("border-block-end", SHORTHANDS_DATA["border-block-end"], [ + "border-bottom-width", + "border-bottom-style", + "border-bottom-color" + ]), + "border-block-start": createBorderSideShorthand("border-block-start", SHORTHANDS_DATA["border-block-start"], [ + "border-top-width", + "border-top-style", + "border-top-color" + ]), + "border-block-style": createTwoValueShorthand("border-block-style", ["border-top-style", "border-bottom-style"]), + "border-block-width": createTwoValueShorthand("border-block-width", ["border-top-width", "border-bottom-width"]), + "border": { + longhands: BORDER_ALL_LONGHANDS, + expand: expandBorder, + contract: contractBorder + }, + "border-top": createBorderSideShorthand("border-top", [ + "border-top-width", + "border-top-style", + "border-top-color" + ]), + "border-right": createBorderSideShorthand("border-right", [ + "border-right-width", + "border-right-style", + "border-right-color" + ]), + "border-bottom": createBorderSideShorthand("border-bottom", [ + "border-bottom-width", + "border-bottom-style", + "border-bottom-color" + ]), + "border-left": createBorderSideShorthand("border-left", [ + "border-left-width", + "border-left-style", + "border-left-color" + ]), + "border-image": { + longhands: BORDER_IMAGE_LONGHANDS, + expand: expandBorderImage, + contract: contractBorderImage + }, + "outline": { + longhands: [ + "outline-color", + "outline-style", + "outline-width" + ], + expand: expandOutline, + contract: contractOutline + }, + "font-variant": { + longhands: FONT_VARIANT_LONGHANDS, + expand: expandFontVariant, + contract: contractFontVariant + }, + "font": { + longhands: FONT_LONGHANDS, + expand: expandFont, + contract: contractFont + }, + "list-style": { + longhands: LIST_STYLE_LONGHANDS, + expand: expandListStyle, + contract: contractListStyle + }, + "overflow": { + longhands: SHORTHANDS_DATA["overflow"], + expand: expandTwoValue(SHORTHANDS_DATA["overflow"]), + contract: contractOverflow + }, + "flex": { + longhands: FLEX_LONGHANDS, + expand: expandFlex, + contract: contractFlex + }, + "-webkit-flex": { + longhands: FLEX_LONGHANDS, + expand: expandFlex, + contract: contractFlex + }, + "line-clamp": { + longhands: LINE_CLAMP_LONGHANDS, + expand: expandLineClamp, + contract: contractLineClamp + }, + "-webkit-line-clamp": { + longhands: LINE_CLAMP_LONGHANDS, + expand: expandLineClamp, + contract: contractLineClamp + }, + "border-color": createBoxShorthand("border-color", [ + "border-top-color", + "border-right-color", + "border-bottom-color", + "border-left-color" + ], [ + "border-block-start-color", + "border-inline-start-color", + "border-block-end-color", + "border-inline-end-color" + ]), + "border-inline": createBorderAxisShorthand("border-inline", "border-inline-start", "border-inline-end", [ + "border-left-width", + "border-left-style", + "border-left-color", + "border-right-width", + "border-right-style", + "border-right-color" + ], [ + "border-inline-start-width", + "border-inline-start-style", + "border-inline-start-color", + "border-inline-end-width", + "border-inline-end-style", + "border-inline-end-color" + ]), + "border-inline-color": createTwoValueShorthand("border-inline-color", ["border-left-color", "border-right-color"]), + "border-inline-end": createBorderSideShorthand("border-inline-end", SHORTHANDS_DATA["border-inline-end"], [ + "border-right-width", + "border-right-style", + "border-right-color" + ]), + "border-inline-start": createBorderSideShorthand("border-inline-start", SHORTHANDS_DATA["border-inline-start"], [ + "border-left-width", + "border-left-style", + "border-left-color" + ]), + "border-inline-style": createTwoValueShorthand("border-inline-style", ["border-left-style", "border-right-style"]), + "border-inline-width": createTwoValueShorthand("border-inline-width", ["border-left-width", "border-right-width"]), + "border-radius": { + longhands: SHORTHANDS_DATA["border-radius"], + logicalLonghands: [ + "border-start-start-radius", + "border-start-end-radius", + "border-end-end-radius", + "border-end-start-radius" + ], + expand: expandBorderRadius, + contract: contractBorderRadius + }, + "border-style": createBoxShorthand("border-style", [ + "border-top-style", + "border-right-style", + "border-bottom-style", + "border-left-style" + ], [ + "border-block-start-style", + "border-inline-start-style", + "border-block-end-style", + "border-inline-end-style" + ]), + "border-width": createBoxShorthand("border-width", [ + "border-top-width", + "border-right-width", + "border-bottom-width", + "border-left-width" + ], [ + "border-block-start-width", + "border-inline-start-width", + "border-block-end-width", + "border-inline-end-width" + ]), + "inset": createBoxShorthand("inset", [ + "top", + "right", + "bottom", + "left" + ], [ + "inset-block-start", + "inset-inline-start", + "inset-block-end", + "inset-inline-end" + ]), + "inset-block": createTwoValueShorthand("inset-block", ["top", "bottom"]), + "inset-inline": createTwoValueShorthand("inset-inline", ["left", "right"]), + "margin": createBoxShorthand("margin", [ + "margin-top", + "margin-right", + "margin-bottom", + "margin-left" + ], [ + "margin-block-start", + "margin-inline-start", + "margin-block-end", + "margin-inline-end" + ]), + "margin-block": createTwoValueShorthand("margin-block", ["margin-top", "margin-bottom"]), + "margin-inline": createTwoValueShorthand("margin-inline", ["margin-left", "margin-right"]), + "padding": createBoxShorthand("padding", [ + "padding-top", + "padding-right", + "padding-bottom", + "padding-left" + ], [ + "padding-block-start", + "padding-inline-start", + "padding-block-end", + "padding-inline-end" + ]), + "padding-block": createTwoValueShorthand("padding-block", ["padding-top", "padding-bottom"]), + "padding-inline": createTwoValueShorthand("padding-inline", ["padding-left", "padding-right"]), + "scroll-margin": createBoxShorthand("scroll-margin", [ + "scroll-margin-top", + "scroll-margin-right", + "scroll-margin-bottom", + "scroll-margin-left" + ], [ + "scroll-margin-block-start", + "scroll-margin-inline-start", + "scroll-margin-block-end", + "scroll-margin-inline-end" + ]), + "scroll-padding": createBoxShorthand("scroll-padding", [ + "scroll-padding-top", + "scroll-padding-right", + "scroll-padding-bottom", + "scroll-padding-left" + ], [ + "scroll-padding-block-start", + "scroll-padding-inline-start", + "scroll-padding-block-end", + "scroll-padding-inline-end" + ]), + "background": { + longhands: SHORTHANDS_DATA["background"], + expand: expandBackground, + contract: contractBackground + }, + "all": { + longhands: ALL_SHORTHAND_LONGHANDS, + expand: expandAll, + contract: contractAll + } +}; +function getShorthandAllLonghands(shorthand) { + if (shorthand.allLonghands) return shorthand.allLonghands; + if (!shorthand.logicalLonghands && !shorthand.physicalLonghands) return shorthand.longhands; + const combined = Object.freeze([ + ...shorthand.longhands, + ...shorthand.logicalLonghands || [], + ...shorthand.physicalLonghands || [] + ]); + shorthand.allLonghands = combined; + return combined; +} +const LONGHAND_TO_SHORTHAND = {}; +for (const [shorthand, def] of Object.entries(SHORTHANDS)) { + def.allLonghands = Object.freeze([ + ...def.longhands, + ...def.logicalLonghands || [], + ...def.physicalLonghands || [] + ]); + if (shorthand === "all") continue; + for (const longhand of def.longhands) { + if (!LONGHAND_TO_SHORTHAND[longhand]) LONGHAND_TO_SHORTHAND[longhand] = []; + LONGHAND_TO_SHORTHAND[longhand].push(shorthand); + } + if (def.logicalLonghands) for (const longhand of def.logicalLonghands) { + if (!LONGHAND_TO_SHORTHAND[longhand]) LONGHAND_TO_SHORTHAND[longhand] = []; + LONGHAND_TO_SHORTHAND[longhand].push(shorthand); + } + if (def.physicalLonghands) for (const longhand of def.physicalLonghands) { + if (!LONGHAND_TO_SHORTHAND[longhand]) LONGHAND_TO_SHORTHAND[longhand] = []; + if (!LONGHAND_TO_SHORTHAND[longhand].includes(shorthand)) LONGHAND_TO_SHORTHAND[longhand].push(shorthand); + } +} +// src/utils/format.ts +/** @license Copyright 2026 Google LLC. SPDX-License-Identifier: Apache-2.0 */ +const formatter = new Intl.NumberFormat("en-US", { + useGrouping: false, + minimumFractionDigits: 0, + maximumFractionDigits: 6 +}); +function formatNumber(val) { + if (val === 0) return "0"; + if (!Number.isFinite(val)) { + if (val === Infinity) return "infinity"; + if (val === -Infinity) return "-infinity"; + return "nan"; + } + const formatted = formatter.format(val); + return formatted === "-0" ? "0" : formatted; +} +// src/data/gen/selectors.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +const PSEUDO_CLASSES = /* @__PURE__ */ new Set("active,active-view-transition,active-view-transition-type,after,animated-image,any-link,autofill,before,blank,buffering,checked,current,default,defined,dir,disabled,empty,enabled,first,first-child,first-letter,first-line,first-of-page,first-of-type,focus,focus-visible,focus-within,fullscreen,future,has,has-slotted,heading,high-value,host,host-context,hover,in-range,indeterminate,interest-source,interest-target,invalid,is,lang,last-child,last-of-page,last-of-type,left,link,link-to,local-link,low-value,matches,modal,muted,navigation-source,not,nth,nth-child,nth-col,nth-last-child,nth-last-col,nth-last-of-type,nth-of-page,nth-of-type,only-child,only-of-type,open,optimal-value,optional,out-of-range,past,paused,picture-in-picture,placeholder-shown,playing,popover-open,read-only,read-write,required,right,root,scope,seeking,snapped,snapped-block,snapped-inline,snapped-x,snapped-y,stalled,start-of-page,state,target,target-after,target-before,target-current,target-within,unchecked,user-invalid,user-valid,valid,visited,volume-locked,where,xr-overlay".split(",")); +const PSEUDO_ELEMENTS = /* @__PURE__ */ new Set("-moz-progress-bar,-moz-range-progress,-moz-range-thumb,-moz-range-track,-ms-browse,-ms-check,-ms-clear,-ms-expand,-ms-fill,-ms-fill-lower,-ms-fill-upper,-ms-reveal,-ms-thumb,-ms-ticks-after,-ms-ticks-before,-ms-tooltip,-ms-track,-ms-value,-webkit-progress-bar,-webkit-progress-inner-value,-webkit-progress-value,-webkit-slider-runnable-track,-webkit-slider-thumb,after,backdrop,before,checkmark,clear-icon,color-swatch,column,cue,cue-region,details-content,field-component,field-content,field-separator,file-selector-button,first-letter,first-line,grammar-error,highlight,interest-button,marker,nth-fragment,part,picker,picker-icon,placeholder,reveal-icon,scroll-button,scroll-marker,scroll-marker-group,search-text,selection,slider-fill,slider-thumb,slider-track,slotted,spelling-error,step-control,step-down,step-up,target-text,view-transition,view-transition-group,view-transition-group-children,view-transition-image-pair,view-transition-new,view-transition-old".split(",")); +// src/SelectorParser.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +function isIdentToken(val) { + return val !== void 0 && val.type === "ident"; +} +function isDelimToken(val, char) { + return val !== void 0 && val.type === "delim" && (char === void 0 || val.value === char); +} +function isHashToken(val) { + return val !== void 0 && val.type === "hash"; +} +function isStringToken(val) { + return val !== void 0 && (val.type === "string" || val.type === "bad-string"); +} +function isNumberToken(val) { + return val !== void 0 && val.type === "number"; +} +function isDimensionToken(val) { + return val !== void 0 && val.type === "dimension"; +} +function isSimpleBlock(val, associatedType) { + return val !== void 0 && val.type === "simple-block" && (associatedType === void 0 || val.associatedToken.type === associatedType); +} +function isCSSFunction(val, name) { + return val !== void 0 && val.type === "function" && (name === void 0); +} +var ComponentValueCursor = class { + values; + _i = 0; + constructor(values) { + this.values = values; + } + get hasNext() { + return this._i < this.values.length; + } + get i() { + return this._i; + } + set i(pos) { + this._i = pos; + } + get length() { + return this.values.length; + } + get next() { + return this.values[this._i]; + } + peek(offset = 1) { + return this.values[this._i + offset]; + } + consume() { + return this.values[this._i++] || { type: "EOF" }; + } + skipWhitespace() { + while (this._i < this.values.length && this.values[this._i].type === "whitespace") this._i++; + } + skipToNextComma() { + const commaOffset = this.values.slice(this._i).findIndex((v) => v.type === "comma"); + this._i = commaOffset === -1 ? this.values.length : this._i + commaOffset; + } + slice(start, end) { + return this.values.slice(start, end ?? this._i); + } +}; +const LEGACY_PSEUDO_CLASS_ALIASES = { "-webkit-autofill": "autofill" }; +/** +* Selector Parser according to Selectors Level 4. +* +* selectors-4 #grammar +* selectors-4 #parse-selector +* selectors-4 #parse-relative-selector +*/ +var SelectorParser = class SelectorParser { + static PSEUDO_CLASSES = PSEUDO_CLASSES; + static PSEUDO_ELEMENTS = PSEUDO_ELEMENTS; + cursor; + allowRelative; + forgiving; + insideHas; + forbidPseudo; + declaredNamespaces; + strictSupports; + allowVendorPseudos; + allowUnknownPseudos; + constructor(values, options = {}) { + this.cursor = new ComponentValueCursor(values); + this.allowRelative = options.allowRelative ?? false; + this.forgiving = options.forgiving ?? false; + this.insideHas = options.insideHas ?? false; + this.forbidPseudo = options.forbidPseudo ?? false; + this.declaredNamespaces = options.declaredNamespaces; + this.strictSupports = options.strictSupports ?? false; + this.allowVendorPseudos = options.allowVendorPseudos ?? false; + this.allowUnknownPseudos = options.allowUnknownPseudos ?? false; + } + hasAmpersand(values) { + return values.some((val) => { + if (isDelimToken(val, "&")) return true; + if (isSimpleBlock(val)) return this.hasAmpersand(val.value); + if (isCSSFunction(val)) return this.hasAmpersand(val.value); + return false; + }); + } /** - * @constructor - * @see https://drafts.csswg.org/css-counter-styles/#the-csscounterstylerule-interface - */ - CSSOM.CSSCounterStyleRule = function CSSCounterStyleRule() { - CSSOM.CSSRule.call(this); - this.name = ""; - this.__props = ""; - }; - - CSSOM.CSSCounterStyleRule.prototype = Object.create(CSSOM.CSSRule.prototype); - CSSOM.CSSCounterStyleRule.prototype.constructor = CSSOM.CSSCounterStyleRule; - - Object.setPrototypeOf(CSSOM.CSSCounterStyleRule, CSSOM.CSSRule); - - Object.defineProperty(CSSOM.CSSCounterStyleRule.prototype, "type", { - value: 11, - writable: false - }); - - Object.defineProperty(CSSOM.CSSCounterStyleRule.prototype, "cssText", { - get: function() { - // FIXME : Implement real cssText generation based on properties - return "@counter-style " + this.name + " { " + this.__props + " }"; + * Parses component values into a SelectorList. + * + * selectors-4 #grouping () + * selectors-4 #grammar () + * selectors-4 #forgiving-selector (parsing forgiving selector list) + * selectors-4 #parse-selector + */ + parse() { + const selectors = []; + while (this.cursor.hasNext) { + this.cursor.skipWhitespace(); + if (!this.cursor.hasNext || this.cursor.next?.type === "EOF") break; + const start = this.cursor.i; + try { + const selector = this.consumeComplexSelector(); + this.cursor.skipWhitespace(); + const next = this.cursor.next; + if (!next || next.type === "comma") selectors.push(selector); + else throw new DOMException("Unexpected token in selector", "SyntaxError"); + } catch (e) { + if (this.forgiving) { + this.cursor.skipToNextComma(); + const rawSlice = this.cursor.slice(start, this.cursor.i); + let trimmedStart = 0; + while (trimmedStart < rawSlice.length && rawSlice[trimmedStart].type === "whitespace") trimmedStart++; + let trimmedEnd = rawSlice.length - 1; + while (trimmedEnd >= trimmedStart && rawSlice[trimmedEnd].type === "whitespace") trimmedEnd--; + const failedTokens = rawSlice.slice(trimmedStart, trimmedEnd + 1); + if (failedTokens.length > 0) selectors.push({ + type: "invalid-selector", + tokens: failedTokens + }); + } else throw e; + } + if (this.cursor.next?.type === "comma") this.cursor.consume(); } - }); - + if (!this.forgiving && selectors.length === 0) throw new DOMException("Selector list cannot be empty", "SyntaxError"); + return { + type: "selector-list", + selectors + }; + } /** - * NON-STANDARD - * Rule text parser. - * @param {string} cssText - */ - Object.defineProperty(CSSOM.CSSCounterStyleRule.prototype, "parse", { - value: function(cssText) { - // Extract the name from "@counter-style { ... }" - var match = cssText.match(/@counter-style\s+([^\s{]+)\s*\{([^]*)\}/); - if (match) { - this.name = match[1]; - // Get the text inside the brackets and clean it up - var propsText = match[2]; - this.__props = propsText.trim().replace(/\n/g, " ").replace(/(['"])(?:\\.|[^\\])*?\1|(\s{2,})/g, function (match, quote) { - return quote ? match : ' '; - }); + * Validates namespace prefixes against declared namespaces. + * + * selectors-4 #namespaces + * selectors-4 #parse-selector step 2 + */ + validateNamespace(namespace) { + if (this.declaredNamespaces !== void 0 && namespace !== void 0 && namespace !== "*" && namespace !== "") { + if (!this.declaredNamespaces.has(namespace)) throw new DOMException(`Undeclared namespace prefix: "${namespace}"`, "SyntaxError"); + } + } + /** + * Consumes a complex selector. + * + * selectors-4 #structure () + * selectors-4 #grammar + */ + consumeComplexSelector() { + const items = []; + const start = this.cursor.i; + let seenPseudoElement = false; + while (this.cursor.hasNext) { + this.cursor.skipWhitespace(); + if (!this.cursor.hasNext || this.cursor.next?.type === "comma") break; + const combinator = this.tryConsumeCombinator(); + if (combinator) { + if (items.length === 0 && !this.allowRelative) throw new DOMException("Relative selector not allowed in this context", "SyntaxError"); + if (seenPseudoElement) throw new DOMException("Pseudo-element must be at the end of the selector", "SyntaxError"); + if (items.length > 0 && items[items.length - 1].type === "combinator") throw new DOMException("Consecutive combinators are not allowed", "SyntaxError"); + items.push(combinator); + continue; } + const compound = this.consumeCompoundSelector(); + if (compound.selectors.length > 0) { + if (seenPseudoElement) throw new DOMException("Pseudo-element must be at the end of the selector", "SyntaxError"); + const hasPseudo = compound.selectors.some((s) => s.type === "pseudo-element-selector"); + if (items.length > 0 && items[items.length - 1].type === "compound-selector") items.push({ + type: "combinator", + value: " " + }); + items.push(compound); + if (hasPseudo) seenPseudoElement = true; + } else break; } - }); - - //.CommonJS - CSSCounterStyleRule.CSSCounterStyleRule = CSSOM.CSSCounterStyleRule; - ///CommonJS - return CSSCounterStyleRule; -} - -var CSSPropertyRule = {}; - -var hasRequiredCSSPropertyRule; - -function requireCSSPropertyRule () { - if (hasRequiredCSSPropertyRule) return CSSPropertyRule; - hasRequiredCSSPropertyRule = 1; - //.CommonJS - var CSSOM = { - CSSRule: requireCSSRule().CSSRule - }; - ///CommonJS - - + if (items.length > 0 && items[items.length - 1].type === "combinator") throw new DOMException("Trailing combinator is not allowed", "SyntaxError"); + if (items.length === 0) throw new DOMException("Complex selector cannot be empty", "SyntaxError"); + const end = this.cursor.i; + return { + type: "complex-selector", + items, + tokens: this.cursor.slice(start, end) + }; + } /** - * @constructor - * @see https://drafts.css-houdini.org/css-properties-values-api/#the-css-property-rule-interface - */ - CSSOM.CSSPropertyRule = function CSSPropertyRule() { - CSSOM.CSSRule.call(this); - this.__name = ""; - this.__syntax = ""; - this.__inherits = false; - this.__initialValue = null; - }; - - CSSOM.CSSPropertyRule.prototype = Object.create(CSSOM.CSSRule.prototype); - CSSOM.CSSPropertyRule.prototype.constructor = CSSOM.CSSPropertyRule; - - Object.setPrototypeOf(CSSOM.CSSPropertyRule, CSSOM.CSSRule); - - Object.defineProperty(CSSOM.CSSPropertyRule.prototype, "type", { - value: 0, - writable: false - }); - - Object.defineProperty(CSSOM.CSSPropertyRule.prototype, "cssText", { - get: function() { - var text = "@property " + this.name + " {"; - if (this.syntax !== "") { - text += " syntax: \"" + this.syntax.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + "\";"; + * selectors-4 #combinators + * selectors-5 #the-column-combinator (||) + */ + tryConsumeCombinator() { + const token = this.cursor.next; + if (!token) return null; + if (isDelimToken(token)) { + const val = token.value; + if (val === ">" || val === "+" || val === "~") { + this.cursor.consume(); + return { + type: "combinator", + value: val + }; } - text += " inherits: " + (this.inherits ? "true" : "false") + ";"; - if (this.initialValue !== null) { - text += " initial-value: " + this.initialValue + ";"; + if (val === "|" && isDelimToken(this.cursor.peek(1), "|")) { + this.cursor.consume(); + this.cursor.consume(); + return { + type: "combinator", + value: "||" + }; } - text += " }"; - return text; - } - }); - - Object.defineProperty(CSSOM.CSSPropertyRule.prototype, "name", { - get: function() { - return this.__name; - } - }); - - Object.defineProperty(CSSOM.CSSPropertyRule.prototype, "syntax", { - get: function() { - return this.__syntax; - } - }); - - Object.defineProperty(CSSOM.CSSPropertyRule.prototype, "inherits", { - get: function() { - return this.__inherits; } - }); - - Object.defineProperty(CSSOM.CSSPropertyRule.prototype, "initialValue", { - get: function() { - return this.__initialValue; - } - }); - - /** - * NON-STANDARD - * Rule text parser. - * @param {string} cssText - * @returns {boolean} True if the rule is valid and was parsed successfully - */ - Object.defineProperty(CSSOM.CSSPropertyRule.prototype, "parse", { - value: function(cssText) { - // Extract the name from "@property { ... }" - var match = cssText.match(/@property\s+(--[^\s{]+)\s*\{([^]*)\}/); - if (!match) { - return false; + return null; + } + isUserActionPseudoClass(name) { + const lower = name.toLowerCase(); + return [ + "hover", + "active", + "focus", + "focus-visible", + "focus-within" + ].includes(lower); + } + validateSimpleSelectorAfterPseudo(selector) { + if (selector.type === "pseudo-class-selector") { + const lowerName = selector.name.toLowerCase(); + if ([ + "not", + "is", + "where", + "has" + ].includes(lowerName)) return; + else if (!this.isUserActionPseudoClass(selector.name)) throw new DOMException("Only user-action pseudo-classes are allowed after a pseudo-element", "SyntaxError"); + } else throw new DOMException("Only user-action pseudo-classes are allowed after a pseudo-element", "SyntaxError"); + } + consumeCompoundSelector() { + const selectors = []; + let lastPseudoElement = null; + while (this.cursor.hasNext) { + const token = this.cursor.next; + if (!token || token.type === "whitespace" || token.type === "comma") break; + if (isDelimToken(token)) { + const val = token.value; + if (val === ">" || val === "+" || val === "~") break; + if (val === "|") { + if (lastPseudoElement) break; + if (isDelimToken(this.cursor.peek(1), "|")) break; + if (selectors.length > 0) throw new DOMException("Type selector must be first in compound selector", "SyntaxError"); + selectors.push(this.consumeTypeOrUniversalSelector()); + continue; + } + if (val === "*") { + if (lastPseudoElement) break; + if (selectors.length > 0) throw new DOMException("Universal selector must be first in compound selector", "SyntaxError"); + selectors.push(this.consumeTypeOrUniversalSelector()); + continue; + } + if (val === ".") { + if (lastPseudoElement) break; + selectors.push(this.consumeClassSelector()); + continue; + } + if (val === "&") { + if (lastPseudoElement) break; + this.cursor.consume(); + selectors.push({ type: "nesting-selector" }); + continue; + } } - - this.__name = match[1]; - var bodyText = match[2]; - - // Parse syntax descriptor (REQUIRED) - var syntaxMatch = bodyText.match(/syntax\s*:\s*(['"])([^]*?)\1\s*;/); - if (!syntaxMatch) { - return false; // syntax is required + if (isHashToken(token)) { + if (token.hashType !== "id") throw new DOMException("ID selector must be an identifier", "SyntaxError"); + if (lastPseudoElement) break; + selectors.push({ + type: "id-selector", + name: token.value + }); + this.cursor.consume(); + continue; } - this.__syntax = syntaxMatch[2]; - - // Syntax cannot be empty - if (this.__syntax === "") { - return false; + if (isIdentToken(token)) { + if (lastPseudoElement) break; + if (selectors.length > 0) throw new DOMException("Type selector must be first in compound selector", "SyntaxError"); + selectors.push(this.consumeTypeOrUniversalSelector()); + continue; } - - // Parse inherits descriptor (REQUIRED) - var inheritsMatch = bodyText.match(/inherits\s*:\s*(true|false)\s*;/); - if (!inheritsMatch) { - return false; // inherits is required + if (isSimpleBlock(token, "[")) { + if (lastPseudoElement) break; + selectors.push(this.consumeAttributeSelector()); + continue; } - this.__inherits = inheritsMatch[1] === "true"; - - // Parse initial-value descriptor (OPTIONAL, but required if syntax is not "*") - var initialValueMatch = bodyText.match(/initial-value\s*:\s*([^;]+);/); - if (initialValueMatch) { - this.__initialValue = initialValueMatch[1].trim(); - } else { - // If syntax is not "*", initial-value is required - if (this.__syntax !== "*") { - return false; + if (token.type === "colon") { + const selector = this.consumePseudoSelector(); + if (lastPseudoElement) { + const isSlottedOrPart = ["slotted", "part"].includes(lastPseudoElement.toLowerCase()); + if (selector.type === "pseudo-element-selector") { + if (!isSlottedOrPart) throw new DOMException("Pseudo-elements cannot be nested", "SyntaxError"); + } else if (selector.type === "pseudo-class-selector") { + if (!isSlottedOrPart) this.validateSimpleSelectorAfterPseudo(selector); + } else throw new DOMException("Unexpected selector after pseudo-element", "SyntaxError"); } + if (selector.type === "pseudo-element-selector") lastPseudoElement = selector.name; + selectors.push(selector); + continue; } - - return true; // Successfully parsed + break; } - }); - - //.CommonJS - CSSPropertyRule.CSSPropertyRule = CSSOM.CSSPropertyRule; - ///CommonJS - return CSSPropertyRule; -} - -var CSSContainerRule = {}; - -var hasRequiredCSSContainerRule; - -function requireCSSContainerRule () { - if (hasRequiredCSSContainerRule) return CSSContainerRule; - hasRequiredCSSContainerRule = 1; - //.CommonJS - var CSSOM = { - CSSRule: requireCSSRule().CSSRule, - CSSRuleList: requireCSSRuleList().CSSRuleList, - CSSGroupingRule: requireCSSGroupingRule().CSSGroupingRule, - CSSConditionRule: requireCSSConditionRule().CSSConditionRule, - }; - ///CommonJS - - - /** - * @constructor - * @see https://drafts.csswg.org/css-contain-3/ - * @see https://www.w3.org/TR/css-contain-3/ - */ - CSSOM.CSSContainerRule = function CSSContainerRule() { - CSSOM.CSSConditionRule.call(this); - }; - - CSSOM.CSSContainerRule.prototype = Object.create(CSSOM.CSSConditionRule.prototype); - CSSOM.CSSContainerRule.prototype.constructor = CSSOM.CSSContainerRule; - - Object.setPrototypeOf(CSSOM.CSSContainerRule, CSSOM.CSSConditionRule); - - Object.defineProperty(CSSOM.CSSContainerRule.prototype, "type", { - value: 17, - writable: false - }); - - Object.defineProperties(CSSOM.CSSContainerRule.prototype, { - "cssText": { - get: function() { - var values = ""; - var valuesArr = [" {"]; - if (this.cssRules.length) { - valuesArr.push(this.cssRules.reduce(function(acc, rule){ - if (rule.cssText !== "") { - acc.push(rule.cssText); - } - return acc; - }, []).join("\n ")); - } - values = valuesArr.join("\n ") + "\n}"; - return "@container " + this.conditionText + values; - } - }, - "containerName": { - get: function() { - var parts = this.conditionText.trim().split(/\s+/); - if (parts.length > 1 && parts[0] !== '(' && !parts[0].startsWith('(')) { - return parts[0]; - } - return ""; - } - }, - "containerQuery": { - get: function() { - var parts = this.conditionText.trim().split(/\s+/); - if (parts.length > 1 && parts[0] !== '(' && !parts[0].startsWith('(')) { - return parts.slice(1).join(' '); - } - return this.conditionText; - } - }, - }); - - - //.CommonJS - CSSContainerRule.CSSContainerRule = CSSOM.CSSContainerRule; - ///CommonJS - return CSSContainerRule; -} - -var CSSSupportsRule = {}; - -var hasRequiredCSSSupportsRule; - -function requireCSSSupportsRule () { - if (hasRequiredCSSSupportsRule) return CSSSupportsRule; - hasRequiredCSSSupportsRule = 1; - //.CommonJS - var CSSOM = { - CSSRule: requireCSSRule().CSSRule, - CSSRuleList: requireCSSRuleList().CSSRuleList, - CSSGroupingRule: requireCSSGroupingRule().CSSGroupingRule, - CSSConditionRule: requireCSSConditionRule().CSSConditionRule - }; - ///CommonJS - - - /** - * @constructor - * @see https://drafts.csswg.org/css-conditional-3/#the-csssupportsrule-interface - */ - CSSOM.CSSSupportsRule = function CSSSupportsRule() { - CSSOM.CSSConditionRule.call(this); - }; - - CSSOM.CSSSupportsRule.prototype = Object.create(CSSOM.CSSConditionRule.prototype); - CSSOM.CSSSupportsRule.prototype.constructor = CSSOM.CSSSupportsRule; - - Object.setPrototypeOf(CSSOM.CSSSupportsRule, CSSOM.CSSConditionRule); - - Object.defineProperty(CSSOM.CSSSupportsRule.prototype, "type", { - value: 12, - writable: false - }); - - Object.defineProperty(CSSOM.CSSSupportsRule.prototype, "cssText", { - get: function() { - var values = ""; - var valuesArr = [" {"]; - if (this.cssRules.length) { - valuesArr.push(this.cssRules.reduce(function(acc, rule){ - if (rule.cssText !== "") { - acc.push(rule.cssText); - } - return acc; - }, []).join("\n ")); - } - values = valuesArr.join("\n ") + "\n}"; - return "@supports " + this.conditionText + values; - } - }); - - //.CommonJS - CSSSupportsRule.CSSSupportsRule = CSSOM.CSSSupportsRule; - ///CommonJS - return CSSSupportsRule; -} - -var CSSFontFaceRule = {}; - -var hasRequiredCSSFontFaceRule; - -function requireCSSFontFaceRule () { - if (hasRequiredCSSFontFaceRule) return CSSFontFaceRule; - hasRequiredCSSFontFaceRule = 1; - //.CommonJS - var CSSOM = { - CSSStyleDeclaration: requireCSSStyleDeclaration().CSSStyleDeclaration, - CSSRule: requireCSSRule().CSSRule - }; - // Use cssstyle if available - try { - CSSOM.CSSStyleDeclaration = require("cssstyle").CSSStyleDeclaration; - } catch (e) { - // ignore + return { + type: "compound-selector", + selectors + }; } - ///CommonJS - - - /** - * @constructor - * @see http://dev.w3.org/csswg/cssom/#css-font-face-rule - */ - CSSOM.CSSFontFaceRule = function CSSFontFaceRule() { - CSSOM.CSSRule.call(this); - this.__style = new CSSOM.CSSStyleDeclaration(); - this.__style.parentRule = this; - }; - - CSSOM.CSSFontFaceRule.prototype = Object.create(CSSOM.CSSRule.prototype); - CSSOM.CSSFontFaceRule.prototype.constructor = CSSOM.CSSFontFaceRule; - - Object.setPrototypeOf(CSSOM.CSSFontFaceRule, CSSOM.CSSRule); - - Object.defineProperty(CSSOM.CSSFontFaceRule.prototype, "type", { - value: 5, - writable: false - }); - - //FIXME - //CSSOM.CSSFontFaceRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule; - //CSSOM.CSSFontFaceRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule; - - Object.defineProperty(CSSOM.CSSFontFaceRule.prototype, "style", { - get: function() { - return this.__style; - }, - set: function(value) { - if (typeof value === "string") { - this.__style.cssText = value; - } else { - this.__style = value; - } - } - }); - - // http://www.opensource.apple.com/source/WebCore/WebCore-955.66.1/css/WebKitCSSFontFaceRule.cpp - Object.defineProperty(CSSOM.CSSFontFaceRule.prototype, "cssText", { - get: function() { - return "@font-face {" + (this.style.cssText ? " " + this.style.cssText : "") + " }"; - } - }); - - - //.CommonJS - CSSFontFaceRule.CSSFontFaceRule = CSSOM.CSSFontFaceRule; - ///CommonJS - return CSSFontFaceRule; -} - -var CSSHostRule = {}; - -var hasRequiredCSSHostRule; - -function requireCSSHostRule () { - if (hasRequiredCSSHostRule) return CSSHostRule; - hasRequiredCSSHostRule = 1; - //.CommonJS - var CSSOM = { - CSSRule: requireCSSRule().CSSRule, - CSSRuleList: requireCSSRuleList().CSSRuleList - }; - ///CommonJS - - - /** - * @constructor - * @see http://www.w3.org/TR/shadow-dom/#host-at-rule - * @see http://html5index.org/Shadow%20DOM%20-%20CSSHostRule.html - * @deprecated This rule was part of early Shadow DOM drafts but was removed in favor of the more flexible :host and :host-context() pseudo-classes in modern CSS for Web Components. - */ - CSSOM.CSSHostRule = function CSSHostRule() { - CSSOM.CSSRule.call(this); - this.cssRules = new CSSOM.CSSRuleList(); - }; - - CSSOM.CSSHostRule.prototype = Object.create(CSSOM.CSSRule.prototype); - CSSOM.CSSHostRule.prototype.constructor = CSSOM.CSSHostRule; - - Object.setPrototypeOf(CSSOM.CSSHostRule, CSSOM.CSSRule); - - Object.defineProperty(CSSOM.CSSHostRule.prototype, "type", { - value: 1001, - writable: false - }); - - //FIXME - //CSSOM.CSSHostRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule; - //CSSOM.CSSHostRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule; - - Object.defineProperty(CSSOM.CSSHostRule.prototype, "cssText", { - get: function() { - var values = ""; - var valuesArr = [" {"]; - if (this.cssRules.length) { - valuesArr.push(this.cssRules.reduce(function(acc, rule){ - if (rule.cssText !== "") { - acc.push(rule.cssText); - } - return acc; - }, []).join("\n ")); - } - values = valuesArr.join("\n ") + "\n}"; - return "@host" + values; + consumeTypeOrUniversalSelector() { + let namespace = void 0; + const token = this.cursor.next; + if (!token) throw new DOMException("Unexpected EOF in type selector", "SyntaxError"); + const isNextPipe = isDelimToken(this.cursor.peek(1), "|"); + const isNextNextPipe = isDelimToken(this.cursor.peek(2), "|"); + const isColumnCombinator = isNextPipe && isNextNextPipe; + if (isIdentToken(token) && isNextPipe && !isColumnCombinator) { + namespace = token.value; + this.cursor.i += 2; + } else if (isDelimToken(token, "*") && isNextPipe && !isColumnCombinator) { + namespace = "*"; + this.cursor.i += 2; + } else if (isDelimToken(token, "|") && !isNextPipe) { + namespace = ""; + this.cursor.i += 1; } - }); - - - //.CommonJS - CSSHostRule.CSSHostRule = CSSOM.CSSHostRule; - ///CommonJS - return CSSHostRule; -} - -var CSSStartingStyleRule = {}; - -var hasRequiredCSSStartingStyleRule; - -function requireCSSStartingStyleRule () { - if (hasRequiredCSSStartingStyleRule) return CSSStartingStyleRule; - hasRequiredCSSStartingStyleRule = 1; - //.CommonJS - var CSSOM = { - CSSRule: requireCSSRule().CSSRule, - CSSRuleList: requireCSSRuleList().CSSRuleList, - CSSGroupingRule: requireCSSGroupingRule().CSSGroupingRule - }; - ///CommonJS - - - /** - * @constructor - * @see http://www.w3.org/TR/shadow-dom/#host-at-rule - */ - CSSOM.CSSStartingStyleRule = function CSSStartingStyleRule() { - CSSOM.CSSGroupingRule.call(this); - }; - - CSSOM.CSSStartingStyleRule.prototype = Object.create(CSSOM.CSSGroupingRule.prototype); - CSSOM.CSSStartingStyleRule.prototype.constructor = CSSOM.CSSStartingStyleRule; - - Object.setPrototypeOf(CSSOM.CSSStartingStyleRule, CSSOM.CSSGroupingRule); - - Object.defineProperty(CSSOM.CSSStartingStyleRule.prototype, "type", { - value: 1002, - writable: false - }); - - //FIXME - //CSSOM.CSSStartingStyleRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule; - //CSSOM.CSSStartingStyleRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule; - - Object.defineProperty(CSSOM.CSSStartingStyleRule.prototype, "cssText", { - get: function() { - var values = ""; - var valuesArr = [" {"]; - if (this.cssRules.length) { - valuesArr.push(this.cssRules.reduce(function(acc, rule){ - if (rule.cssText !== "") { - acc.push(rule.cssText); - } - return acc; - }, []).join("\n ")); - } - values = valuesArr.join("\n ") + "\n}"; - return "@starting-style" + values; + const next = this.cursor.consume(); + if (isDelimToken(next, "*")) { + this.validateNamespace(namespace); + return { + type: "universal-selector", + namespace + }; } - }); - - - //.CommonJS - CSSStartingStyleRule.CSSStartingStyleRule = CSSOM.CSSStartingStyleRule; - ///CommonJS - return CSSStartingStyleRule; -} - -var CSSKeyframeRule = {}; - -var hasRequiredCSSKeyframeRule; - -function requireCSSKeyframeRule () { - if (hasRequiredCSSKeyframeRule) return CSSKeyframeRule; - hasRequiredCSSKeyframeRule = 1; - //.CommonJS - var CSSOM = { - CSSRule: requireCSSRule().CSSRule, - CSSStyleDeclaration: requireCSSStyleDeclaration().CSSStyleDeclaration - }; - // Use cssstyle if available - try { - CSSOM.CSSStyleDeclaration = require("cssstyle").CSSStyleDeclaration; - } catch (e) { - // ignore + if (!isIdentToken(next)) throw new DOMException("Expected identifier or * after namespace pipe", "SyntaxError"); + this.validateNamespace(namespace); + return { + type: "type-selector", + name: next.value, + namespace + }; } - ///CommonJS - - - /** - * @constructor - * @see http://www.w3.org/TR/css3-animations/#DOM-CSSKeyframeRule - */ - CSSOM.CSSKeyframeRule = function CSSKeyframeRule() { - CSSOM.CSSRule.call(this); - this.keyText = ''; - this.__style = new CSSOM.CSSStyleDeclaration(); - this.__style.parentRule = this; - }; - - CSSOM.CSSKeyframeRule.prototype = Object.create(CSSOM.CSSRule.prototype); - CSSOM.CSSKeyframeRule.prototype.constructor = CSSOM.CSSKeyframeRule; - - Object.setPrototypeOf(CSSOM.CSSKeyframeRule, CSSOM.CSSRule); - - Object.defineProperty(CSSOM.CSSKeyframeRule.prototype, "type", { - value: 8, - writable: false - }); - - //FIXME - //CSSOM.CSSKeyframeRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule; - //CSSOM.CSSKeyframeRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule; - - Object.defineProperty(CSSOM.CSSKeyframeRule.prototype, "style", { - get: function() { - return this.__style; - }, - set: function(value) { - if (typeof value === "string") { - this.__style.cssText = value; - } else { - this.__style = value; + consumeClassSelector() { + this.cursor.consume(); + const ident = this.cursor.consume(); + if (!isIdentToken(ident)) return { + type: "class-selector", + name: "" + }; + return { + type: "class-selector", + name: ident.value + }; + } + consumeAttributeSelector() { + const block = this.cursor.consume(); + if (!isSimpleBlock(block, "[")) throw new DOMException("Expected attribute selector block", "SyntaxError"); + const subCursor = new ComponentValueCursor(block.value); + let name = ""; + let namespace = void 0; + let operator = ""; + let value = ""; + let flags = ""; + subCursor.skipWhitespace(); + if (subCursor.hasNext) { + const v1 = subCursor.next; + const v2 = subCursor.peek(1); + const v3 = subCursor.peek(2); + const isPipeFollowedByEquals = isDelimToken(v2, "|") && isDelimToken(v3, "="); + if (isIdentToken(v1) && isDelimToken(v2, "|") && !isPipeFollowedByEquals) { + namespace = v1.value; + subCursor.i += 2; + } else if (isDelimToken(v1, "*") && isDelimToken(v2, "|") && !isPipeFollowedByEquals) { + namespace = "*"; + subCursor.i += 2; + } else if (isDelimToken(v1, "|") && !isDelimToken(v2, "=")) { + namespace = ""; + subCursor.i += 1; } } - }); - - // http://www.opensource.apple.com/source/WebCore/WebCore-955.66.1/css/WebKitCSSKeyframeRule.cpp - Object.defineProperty(CSSOM.CSSKeyframeRule.prototype, "cssText", { - get: function() { - return this.keyText + " {" + (this.style.cssText ? " " + this.style.cssText : "") + " }"; - } - }); - - - //.CommonJS - CSSKeyframeRule.CSSKeyframeRule = CSSOM.CSSKeyframeRule; - ///CommonJS - return CSSKeyframeRule; -} - -var CSSKeyframesRule = {}; - -var hasRequiredCSSKeyframesRule; - -function requireCSSKeyframesRule () { - if (hasRequiredCSSKeyframesRule) return CSSKeyframesRule; - hasRequiredCSSKeyframesRule = 1; - //.CommonJS - var CSSOM = { - CSSRule: requireCSSRule().CSSRule, - CSSRuleList: requireCSSRuleList().CSSRuleList, - parse: requireParse().parse - }; - var errorUtils = requireErrorUtils().errorUtils; - ///CommonJS - - - /** - * @constructor - * @see http://www.w3.org/TR/css3-animations/#DOM-CSSKeyframesRule - */ - CSSOM.CSSKeyframesRule = function CSSKeyframesRule() { - CSSOM.CSSRule.call(this); - this.name = ''; - this.cssRules = new CSSOM.CSSRuleList(); - - // Set up initial indexed access - this._setupIndexedAccess(); - - // Override cssRules methods after initial setup, store references as non-enumerable properties - var self = this; - var originalPush = this.cssRules.push; - var originalSplice = this.cssRules.splice; - - // Create non-enumerable method overrides - Object.defineProperty(this.cssRules, 'push', { - value: function() { - var result = originalPush.apply(this, arguments); - self._setupIndexedAccess(); - return result; - }, - writable: true, - enumerable: false, - configurable: true - }); - - Object.defineProperty(this.cssRules, 'splice', { - value: function() { - var result = originalSplice.apply(this, arguments); - self._setupIndexedAccess(); - return result; - }, - writable: true, - enumerable: false, - configurable: true - }); - }; - - CSSOM.CSSKeyframesRule.prototype = Object.create(CSSOM.CSSRule.prototype); - CSSOM.CSSKeyframesRule.prototype.constructor = CSSOM.CSSKeyframesRule; - - Object.setPrototypeOf(CSSOM.CSSKeyframesRule, CSSOM.CSSRule); - - Object.defineProperty(CSSOM.CSSKeyframesRule.prototype, "type", { - value: 7, - writable: false - }); - - // http://www.opensource.apple.com/source/WebCore/WebCore-955.66.1/css/WebKitCSSKeyframesRule.cpp - Object.defineProperty(CSSOM.CSSKeyframesRule.prototype, "cssText", { - get: function() { - var values = ""; - var valuesArr = [" {"]; - if (this.cssRules.length) { - valuesArr.push(this.cssRules.reduce(function(acc, rule){ - if (rule.cssText !== "") { - acc.push(rule.cssText); + const valName = subCursor.next; + if (isIdentToken(valName)) { + name = valName.value; + subCursor.consume(); + } + subCursor.skipWhitespace(); + const valOp = subCursor.next; + if (isDelimToken(valOp)) { + operator = valOp.value; + subCursor.consume(); + const valEq = subCursor.next; + if (isDelimToken(valEq, "=")) { + operator += valEq.value; + subCursor.consume(); } - return acc; - }, []).join("\n ")); } - values = valuesArr.join("\n ") + "\n}"; - var cssWideKeywords = ['initial', 'inherit', 'revert', 'revert-layer', 'unset', 'none']; - var processedName = cssWideKeywords.includes(this.name) ? '"' + this.name + '"' : this.name; - return "@" + (this._vendorPrefix || '') + "keyframes " + processedName + values; - } - }); - - /** - * Appends a new keyframe rule to the list of keyframes. - * - * @param {string} rule - The keyframe rule string to append (e.g., "50% { opacity: 0.5; }") - * @see https://www.w3.org/TR/css-animations-1/#dom-csskeyframesrule-appendrule - */ - CSSOM.CSSKeyframesRule.prototype.appendRule = function appendRule(rule) { - if (arguments.length === 0) { - errorUtils.throwMissingArguments(this, 'appendRule', 'CSSKeyframesRule'); + subCursor.skipWhitespace(); + const valVal = subCursor.next; + if (isStringToken(valVal) || isIdentToken(valVal)) { + value = valVal.value; + subCursor.consume(); } - - var parsedRule; - try { - // Parse the rule string as a keyframe rule - var tempStyleSheet = CSSOM.parse("@keyframes temp { " + rule + " }"); - if (tempStyleSheet.cssRules.length > 0 && tempStyleSheet.cssRules[0].cssRules.length > 0) { - parsedRule = tempStyleSheet.cssRules[0].cssRules[0]; - } else { - throw new Error("Failed to parse keyframe rule"); - } - } catch (e) { - errorUtils.throwParseError(this, 'appendRule', 'CSSKeyframesRule', rule); + subCursor.skipWhitespace(); + const valFlag = subCursor.next; + if (isIdentToken(valFlag)) { + const flagValue = valFlag.value; + const lowerFlag = flagValue.toLowerCase(); + if (lowerFlag !== "i" && lowerFlag !== "s") throw new DOMException(`Invalid attribute selector flag: ${flagValue}`, "SyntaxError"); + flags = flagValue; + subCursor.consume(); } - - parsedRule.__parentRule = this; - this.cssRules.push(parsedRule); - }; - - /** - * Deletes a keyframe rule that matches the specified key. - * - * @param {string} select - The keyframe selector to delete (e.g., "50%", "from", "to") - * @see https://www.w3.org/TR/css-animations-1/#dom-csskeyframesrule-deleterule - */ - CSSOM.CSSKeyframesRule.prototype.deleteRule = function deleteRule(select) { - if (arguments.length === 0) { - errorUtils.throwMissingArguments(this, 'deleteRule', 'CSSKeyframesRule'); + subCursor.skipWhitespace(); + if (subCursor.hasNext) throw new DOMException("Unexpected content in attribute selector", "SyntaxError"); + if (!name) throw new DOMException("Expected attribute name in attribute selector", "SyntaxError"); + this.validateNamespace(namespace); + return { + type: "attribute-selector", + name, + namespace, + operator, + value, + flags + }; + } + consumePseudoSelector() { + this.cursor.consume(); + let isPseudoElement = false; + if (this.cursor.next?.type === "colon") { + this.cursor.consume(); + isPseudoElement = true; } - - var normalizedSelect = this._normalizeKeyText(select); - - for (var i = 0; i < this.cssRules.length; i++) { - var rule = this.cssRules[i]; - if (this._normalizeKeyText(rule.keyText) === normalizedSelect) { - rule.__parentRule = null; - this.cssRules.splice(i, 1); - return; + const token = this.cursor.consume(); + if (!token) return { + type: "pseudo-class-selector", + name: "" + }; + if (isIdentToken(token)) { + const originalName = token.value; + const lowerName = originalName.toLowerCase(); + const name = LEGACY_PSEUDO_CLASS_ALIASES[lowerName] || originalName; + const effectiveLowerName = name.toLowerCase(); + const isVendorPrefixed = /^-[a-zA-Z0-9]+-/.test(effectiveLowerName); + if (isPseudoElement) { + if (this.forbidPseudo || this.insideHas) throw new DOMException("Pseudo-elements are not allowed in this context", "SyntaxError"); + const isAllowedVendor = this.allowVendorPseudos && isVendorPrefixed || !this.strictSupports && effectiveLowerName.startsWith("-webkit-"); + if (!PSEUDO_ELEMENTS.has(effectiveLowerName) && !isAllowedVendor && !this.allowUnknownPseudos) throw new DOMException(`Unknown pseudo-element ::${name}`, "SyntaxError"); + return { + type: "pseudo-element-selector", + name + }; + } + if ([ + "before", + "after", + "first-line", + "first-letter" + ].includes(effectiveLowerName)) { + if (this.forbidPseudo || this.insideHas) throw new DOMException("Pseudo-elements are not allowed in this context", "SyntaxError"); + return { + type: "pseudo-element-selector", + name + }; + } + const isAllowedVendor = this.allowVendorPseudos && isVendorPrefixed || !this.strictSupports && effectiveLowerName.startsWith("-webkit-"); + if (!PSEUDO_CLASSES.has(effectiveLowerName) && !isAllowedVendor && !this.allowUnknownPseudos) throw new DOMException(`Unknown pseudo-class :${name}`, "SyntaxError"); + return { + type: "pseudo-class-selector", + name + }; + } else if (isCSSFunction(token)) { + const func = token; + const name = func.name; + const lowerName = name.toLowerCase(); + const isVendorPrefixed = /^-[a-zA-Z0-9]+-/.test(lowerName); + if (isPseudoElement) { + if (this.forbidPseudo || this.insideHas) throw new DOMException("Pseudo-elements are not allowed in this context", "SyntaxError"); + if (!PSEUDO_ELEMENTS.has(lowerName) && !(this.allowVendorPseudos && isVendorPrefixed) && !this.allowUnknownPseudos) throw new DOMException(`Unknown pseudo-element ::${name}()`, "SyntaxError"); + if (lowerName === "slotted") { + const subParser = new SelectorParser(func.value, { + insideHas: this.insideHas, + forbidPseudo: true, + declaredNamespaces: this.declaredNamespaces, + strictSupports: this.strictSupports, + allowVendorPseudos: this.allowVendorPseudos, + allowUnknownPseudos: this.allowUnknownPseudos + }); + subParser.cursor.skipWhitespace(); + const compound = subParser.consumeCompoundSelector(); + subParser.cursor.skipWhitespace(); + if (subParser.cursor.i !== func.value.length || compound.selectors.length === 0) throw new DOMException("Argument to ::slotted() must be a compound selector", "SyntaxError"); + return { + type: "pseudo-element-selector", + name, + argument: { + type: "selector-list", + selectors: [{ + type: "complex-selector", + items: [compound], + tokens: func.value + }] + } + }; + } + return { + type: "pseudo-element-selector", + name, + argument: func.value + }; + } + if (!PSEUDO_CLASSES.has(lowerName) && lowerName !== "matches" && !(this.allowVendorPseudos && isVendorPrefixed) && !this.allowUnknownPseudos) throw new DOMException(`Unknown pseudo-class :${name}()`, "SyntaxError"); + if ([ + "is", + "not", + "has", + "where", + "matches" + ].includes(lowerName)) { + const isHas = lowerName === "has"; + if (isHas && this.insideHas) throw new DOMException(":has() cannot be nested", "SyntaxError"); + const isForgiving = !this.strictSupports && [ + "is", + "where", + "matches" + ].includes(lowerName); + const isLogicalPseudo = [ + "is", + "where", + "not", + "matches" + ].includes(lowerName); + return { + type: "pseudo-class-selector", + name, + argument: new SelectorParser(func.value, { + allowRelative: isHas, + forgiving: isForgiving, + insideHas: isHas || this.insideHas, + forbidPseudo: isLogicalPseudo || isHas || this.forbidPseudo, + declaredNamespaces: this.declaredNamespaces, + strictSupports: this.strictSupports, + allowVendorPseudos: this.allowVendorPseudos, + allowUnknownPseudos: this.allowUnknownPseudos + }).parse() + }; + } + if (["host", "host-context"].includes(lowerName)) { + const subParser = new SelectorParser(func.value, { + insideHas: this.insideHas, + forbidPseudo: true, + declaredNamespaces: this.declaredNamespaces, + strictSupports: this.strictSupports, + allowVendorPseudos: this.allowVendorPseudos, + allowUnknownPseudos: this.allowUnknownPseudos + }); + subParser.cursor.skipWhitespace(); + const compound = subParser.consumeCompoundSelector(); + subParser.cursor.skipWhitespace(); + if (subParser.cursor.i !== func.value.length || compound.selectors.length === 0) throw new DOMException(`Argument to :${name}() must be a compound selector`, "SyntaxError"); + return { + type: "pseudo-class-selector", + name, + argument: { + type: "selector-list", + selectors: [{ + type: "complex-selector", + items: [compound], + tokens: func.value + }] + } + }; + } + if ([ + "nth-child", + "nth-last-child", + "nth-of-type", + "nth-last-of-type" + ].includes(lowerName)) { + let ofIdx = -1; + for (let k = 0; k < func.value.length; k++) { + const v = func.value[k]; + if (isIdentToken(v) && v.value.toLowerCase() === "of") { + ofIdx = k; + break; + } + } + if (ofIdx !== -1) { + if (["nth-of-type", "nth-last-of-type"].includes(lowerName)) throw new DOMException(`'of' is not allowed in :${name}()`, "SyntaxError"); + const nth = func.value.slice(0, ofIdx); + this.validateAnPlusB(nth); + return { + type: "pseudo-class-selector", + name, + argument: new SelectorParser(func.value.slice(ofIdx + 1), { + insideHas: this.insideHas, + forbidPseudo: true, + allowRelative: false, + declaredNamespaces: this.declaredNamespaces, + strictSupports: this.strictSupports, + allowVendorPseudos: this.allowVendorPseudos, + allowUnknownPseudos: this.allowUnknownPseudos + }).parse(), + nth + }; + } else { + this.validateAnPlusB(func.value); + return { + type: "pseudo-class-selector", + name, + argument: func.value, + nth: func.value + }; + } } + if (lowerName === "dir") this.validateDir(func.value); + if (lowerName === "heading") this.validateHeading(func.value); + if (lowerName === "lang") this.validateLang(func.value); + return { + type: "pseudo-class-selector", + name, + argument: func.value + }; + } + throw new DOMException("Expected identifier or function after colon in pseudo-selector", "SyntaxError"); + } + validateAnPlusB(values) { + const res = parseAnPlusB(values); + if (!res) { + const text = getOriginalText(values).trim(); + throw new DOMException(`Invalid An+B expression: ${text}`, "SyntaxError"); + } + return res; + } + validateHeading(values) { + const nonComment = values.filter((v) => v.type !== "comment"); + let start = 0; + while (start < nonComment.length && nonComment[start].type === "whitespace") start++; + let end = nonComment.length - 1; + while (end >= start && nonComment[end].type === "whitespace") end--; + if (start > end) throw new DOMException("Argument to :heading() cannot be empty", "SyntaxError"); + const tokens = nonComment.slice(start, end + 1); + let expectInteger = true; + for (let i = 0; i < tokens.length; i++) { + const t = tokens[i]; + if (t.type === "whitespace") continue; + if (expectInteger) { + if (t.type === "number" && t.numberType === "integer") expectInteger = false; + else throw new DOMException("Argument to :heading() must be comma-separated integers", "SyntaxError"); + } else if (t.type === "comma" || t.type === "delim" && t.value === ",") expectInteger = true; + else throw new DOMException("Expected comma in :heading() arguments", "SyntaxError"); + } + if (expectInteger) throw new DOMException("Trailing comma in :heading() arguments", "SyntaxError"); + } + validateDir(values) { + const nonWs = values.filter((v) => v.type !== "whitespace" && v.type !== "comment"); + const firstToken = nonWs[0]; + if (nonWs.length !== 1 || !isIdentToken(firstToken)) throw new DOMException("Argument to :dir() must be a single identifier", "SyntaxError"); + const val = firstToken.value.toLowerCase(); + if (val !== "ltr" && val !== "rtl" && val !== "auto") throw new DOMException("Argument to :dir() must be ltr, rtl, or auto", "SyntaxError"); + } + validateLang(values) { + const nonWs = values.filter((v) => v.type !== "whitespace" && v.type !== "comment"); + if (nonWs.length === 0) throw new DOMException("Argument to :lang() cannot be empty", "SyntaxError"); + let expectItem = true; + for (const v of nonWs) if (expectItem) { + if (!isIdentToken(v) && !isStringToken(v)) throw new DOMException("Argument to :lang() must be identifiers or strings", "SyntaxError"); + expectItem = false; + } else { + if (v.type !== "comma") throw new DOMException("Expected comma in :lang() argument", "SyntaxError"); + expectItem = true; } + if (expectItem) throw new DOMException("Trailing comma in :lang() argument", "SyntaxError"); + } +}; +/** +* Parses An+B microsyntax into { a, b }. +* +* selectors-4 #child-index +* css-syntax-3 #anb-microsyntax +* css-syntax-3 #anb-syntax +* css-syntax-3 #the-anb-type ( production) +*/ +function parseAnPlusB(values) { + const nonComment = values.filter((v) => v.type !== "comment"); + let start = 0; + while (start < nonComment.length && nonComment[start].type === "whitespace") start++; + let end = nonComment.length - 1; + while (end >= start && nonComment[end].type === "whitespace") end--; + if (start > end) return null; + const tokens = nonComment.slice(start, end + 1); + const isIntegerNumber = (t) => { + return isNumberToken(t) && t.numberType === "integer"; }; - - /** - * Finds and returns the keyframe rule that matches the specified key. - * When multiple rules have the same key, returns the last one. - * - * @param {string} select - The keyframe selector to find (e.g., "50%", "from", "to") - * @return {CSSKeyframeRule|null} The matching keyframe rule, or null if not found - * @see https://www.w3.org/TR/css-animations-1/#dom-csskeyframesrule-findrule - */ - CSSOM.CSSKeyframesRule.prototype.findRule = function findRule(select) { - if (arguments.length === 0) { - errorUtils.throwMissingArguments(this, 'findRule', 'CSSKeyframesRule'); + if (tokens.length === 1) { + const t = tokens[0]; + if (isIdentToken(t)) { + const lower = t.value.toLowerCase(); + if (lower === "odd") return { + a: 2, + b: 1 + }; + if (lower === "even") return { + a: 2, + b: 0 + }; + if (lower === "n") return { + a: 1, + b: 0 + }; + if (lower === "-n") return { + a: -1, + b: 0 + }; + const matchNdashDigit = lower.match(/^n-(\d+)$/); + if (matchNdashDigit) return { + a: 1, + b: -parseInt(matchNdashDigit[1], 10) + }; + const matchDashNdashDigit = lower.match(/^-n-(\d+)$/); + if (matchDashNdashDigit) return { + a: -1, + b: -parseInt(matchDashNdashDigit[1], 10) + }; + return null; } - - var normalizedSelect = this._normalizeKeyText(select); - - // Iterate backwards to find the last matching rule - for (var i = this.cssRules.length - 1; i >= 0; i--) { - var rule = this.cssRules[i]; - if (this._normalizeKeyText(rule.keyText) === normalizedSelect) { - return rule; - } + if (isIntegerNumber(t)) return { + a: 0, + b: Number(t.value) + }; + if (isDimensionToken(t) && t.numberType === "integer") { + const unit = (t.unit || "").toLowerCase(); + if (unit === "n") return { + a: Number(t.value), + b: 0 + }; + const matchDim = unit.match(/^n-(\d+)$/); + if (matchDim) return { + a: Number(t.value), + b: -parseInt(matchDim[1], 10) + }; + return null; } - return null; + } + let idx = 0; + let plusPrefix = false; + if (isDelimToken(tokens[0], "+")) { + plusPrefix = true; + idx = 1; + if (tokens[1]?.type === "whitespace") return null; + } + const nextNonWs = (from) => { + let p = from; + while (p < tokens.length && tokens[p].type === "whitespace") p++; + return p; }; - - /** - * Normalizes keyframe selector text for comparison. - * Handles "from" -> "0%" and "to" -> "100%" conversions and trims whitespace. - * - * @private - * @param {string} keyText - The keyframe selector text to normalize - * @return {string} The normalized keyframe selector text - */ - CSSOM.CSSKeyframesRule.prototype._normalizeKeyText = function _normalizeKeyText(keyText) { - if (!keyText) return ''; - - var normalized = keyText.toString().trim().toLowerCase(); - - // Convert keywords to percentages for comparison - if (normalized === 'from') { - return '0%'; - } else if (normalized === 'to') { - return '100%'; + const t1 = tokens[idx]; + if (!t1) return null; + let a = null; + let hasDashAfterN = false; + if (isIdentToken(t1)) { + const lower = t1.value.toLowerCase(); + if (lower === "n") a = 1; + else if (lower === "-n" && !plusPrefix) a = -1; + else if (lower === "n-") { + a = 1; + hasDashAfterN = true; + } else if (lower === "-n-" && !plusPrefix) { + a = -1; + hasDashAfterN = true; + } else if (lower.startsWith("n-")) { + const match = lower.match(/^n-(\d+)$/); + if (match && idx === tokens.length - 1) return { + a: 1, + b: -parseInt(match[1], 10) + }; } - - return normalized; - }; - - /** - * Makes CSSKeyframesRule iterable over its cssRules. - * Allows for...of loops and other iterable methods. - */ - if (typeof Symbol !== 'undefined' && Symbol.iterator) { - CSSOM.CSSKeyframesRule.prototype[Symbol.iterator] = function() { - var index = 0; - var cssRules = this.cssRules; - + } else if (isDimensionToken(t1) && t1.numberType === "integer" && !plusPrefix) { + const unit = (t1.unit || "").toLowerCase(); + if (unit === "n") a = Number(t1.value); + else if (unit === "n-") { + a = Number(t1.value); + hasDashAfterN = true; + } + } + if (a === null) return null; + const afterT1 = nextNonWs(idx + 1); + if (afterT1 >= tokens.length) { + if (!hasDashAfterN) return { + a, + b: 0 + }; + return null; + } + const t2 = tokens[afterT1]; + if (hasDashAfterN) { + if (isIntegerNumber(t2) && !t2.sign) { + if (nextNonWs(afterT1 + 1) < tokens.length) return null; return { - next: function() { - if (index < cssRules.length) { - return { value: cssRules[index++], done: false }; - } else { - return { done: true }; - } - } + a, + b: -Number(t2.value) }; + } + return null; + } + if (isIntegerNumber(t2) && t2.sign) { + if (nextNonWs(afterT1 + 1) < tokens.length) return null; + return { + a, + b: Number(t2.value) }; } - - /** - * Adds indexed getters for direct access to cssRules by index. - * This enables rule[0], rule[1], etc. access patterns. - * Works in environments where Proxy is not available (like jsdom). - */ - CSSOM.CSSKeyframesRule.prototype._setupIndexedAccess = function() { - // Remove any existing indexed properties - for (var i = 0; i < 1000; i++) { // reasonable upper limit - if (this.hasOwnProperty(i)) { - delete this[i]; - } else { - break; - } - } - - // Add indexed getters for current cssRules - for (var i = 0; i < this.cssRules.length; i++) { - (function(index) { - Object.defineProperty(this, index, { - get: function() { - return this.cssRules[index]; - }, - enumerable: false, - configurable: true - }); - }.call(this, i)); + if (isDelimToken(t2, "+") || isDelimToken(t2, "-")) { + const sign = t2.value === "+" ? 1 : -1; + const afterT2 = nextNonWs(afterT1 + 1); + if (afterT2 >= tokens.length) return null; + const t3 = tokens[afterT2]; + if (isIntegerNumber(t3) && !t3.sign) { + if (nextNonWs(afterT2 + 1) < tokens.length) return null; + return { + a, + b: sign * Number(t3.value) + }; } - - // Update length property - Object.defineProperty(this, 'length', { - get: function() { - return this.cssRules.length; - }, - enumerable: false, - configurable: true - }); + return null; + } + return null; +} +// src/serializer.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +/** +* Determines whether two consecutive tokens require an empty comment separator +* between them to prevent coalescing per css-syntax-3 #serialization. +* // css-syntax-3 #serialization-tables +*/ +function requiresTokenSeparator(t1, t2) { + const isIdent1 = t1.type === "ident"; + const isAtKeyword1 = t1.type === "at-keyword"; + const isHash1 = t1.type === "hash"; + const isDimension1 = t1.type === "dimension"; + const isDelimHash1 = t1.type === "delim" && t1.value === "#"; + const isDelimDash1 = t1.type === "delim" && t1.value === "-"; + const isNumber1 = t1.type === "number"; + const isDelimAt1 = t1.type === "delim" && t1.value === "@"; + const isDelimDot1 = t1.type === "delim" && t1.value === "."; + const isDelimPlus1 = t1.type === "delim" && t1.value === "+"; + const isDelimSlash1 = t1.type === "delim" && t1.value === "/"; + if (!isIdent1 && !isAtKeyword1 && !isHash1 && !isDimension1 && !isDelimHash1 && !isDelimDash1 && !isNumber1 && !isDelimAt1 && !isDelimDot1 && !isDelimPlus1 && !isDelimSlash1) return false; + const isIdent2 = t2.type === "ident"; + const isFunction2 = t2.type === "function"; + const isUrl2 = t2.type === "url"; + const isBadUrl2 = t2.type === "bad-url"; + const isDelimDash2 = t2.type === "delim" && t2.value === "-"; + const isNumber2 = t2.type === "number"; + const isPercentage2 = t2.type === "percentage"; + const isDimension2 = t2.type === "dimension"; + const isCDC2 = t2.type === "CDC"; + const isOpenParen2 = t2.type === "(" || t2.type === "delim" && t2.value === "("; + const isDelimStar2 = t2.type === "delim" && t2.value === "*"; + const isDelimPercent2 = t2.type === "delim" && t2.value === "%"; + const inGroupA = isIdent2 || isFunction2 || isUrl2 || isBadUrl2 || isDelimDash2 || isNumber2 || isPercentage2 || isDimension2 || isCDC2; + if (isIdent1) return inGroupA || isOpenParen2; + if (isAtKeyword1 || isHash1 || isDimension1 || isDelimHash1 || isDelimDash1) return inGroupA; + if (isNumber1) return isIdent2 || isFunction2 || isUrl2 || isBadUrl2 || isNumber2 || isPercentage2 || isDimension2 || isCDC2 || isDelimPercent2; + if (isDelimAt1) return isIdent2 || isFunction2 || isUrl2 || isBadUrl2 || isDelimDash2 || isCDC2; + if (isDelimDot1 || isDelimPlus1) return isNumber2 || isPercentage2 || isDimension2; + if (isDelimSlash1) return isDelimStar2; + return false; +} +function getFirstToken(node) { + if (typeof node !== "object" || node === null) return null; + if (node.type === "simple-block") return node.associatedToken; + if (node.type === "function" && "name" in node) return { + type: "function", + value: node.name }; - - - - - - //.CommonJS - CSSKeyframesRule.CSSKeyframesRule = CSSOM.CSSKeyframesRule; - ///CommonJS - return CSSKeyframesRule; + const t = node; + if (t.type === "EOF") return null; + return t; } - -var CSSValueExpression = {}; - -var CSSValue = {}; - -var hasRequiredCSSValue; - -function requireCSSValue () { - if (hasRequiredCSSValue) return CSSValue; - hasRequiredCSSValue = 1; - //.CommonJS - var CSSOM = {}; - ///CommonJS - - - /** - * @constructor - * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue - * - * TODO: add if needed - */ - CSSOM.CSSValue = function CSSValue() { +function getLastToken(node) { + if (typeof node !== "object" || node === null) return null; + if (node.type === "simple-block") { + const start = node.associatedToken?.value; + const end = getMirrorToken(start); + return { + type: end || ")", + value: end + }; + } + if (node.type === "function" && "name" in node) return { + type: ")", + value: ")" }; - - CSSOM.CSSValue.prototype = { - constructor: CSSOM.CSSValue, - - // @see: http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue - set cssText(text) { - var name = this._getConstructorName(); - - throw new Error('DOMException: property "cssText" of "' + name + '" is readonly and can not be replaced with "' + text + '"!'); - }, - - get cssText() { - var name = this._getConstructorName(); - - throw new Error('getter "cssText" of "' + name + '" is not implemented!'); - }, - - _getConstructorName: function() { - var s = this.constructor.toString(), - c = s.match(/function\s([^\(]+)/), - name = c[1]; - - return name; - } - }; - - - //.CommonJS - CSSValue.CSSValue = CSSOM.CSSValue; - ///CommonJS - return CSSValue; + const t = node; + if (t.type === "EOF") return null; + return t; } - -var hasRequiredCSSValueExpression; - -function requireCSSValueExpression () { - if (hasRequiredCSSValueExpression) return CSSValueExpression; - hasRequiredCSSValueExpression = 1; - //.CommonJS - var CSSOM = { - CSSValue: requireCSSValue().CSSValue - }; - ///CommonJS - - - /** - * @constructor - * @see http://msdn.microsoft.com/en-us/library/ms537634(v=vs.85).aspx - * - */ - CSSOM.CSSValueExpression = function CSSValueExpression(token, idx) { - this._token = token; - this._idx = idx; +/** +* Serializes an array of CSS component values or tokens back into a CSS string, +* inserting `/** /` comment separators between adjacent tokens that would otherwise merge. +*/ +function serialize(nodes, preserveCase = false, propertyName) { + if (propertyName === "font-family") return serializeFontFamily(nodes); + let result = ""; + let prevLastToken = null; + for (const node of nodes) { + if (node.type === "EOF") continue; + const firstToken = getFirstToken(node); + if (prevLastToken && firstToken && requiresTokenSeparator(prevLastToken, firstToken)) result += "/**/"; + result += serializeNode(node, preserveCase); + const last = getLastToken(node); + if (last) prevLastToken = last; + } + return result; +} +function trimWhitespaceTokens(tokens) { + let start = 0; + while (start < tokens.length && tokens[start].type === "whitespace") start++; + let end = tokens.length - 1; + while (end >= start && tokens[end].type === "whitespace") end--; + return start <= end ? tokens.slice(start, end + 1) : []; +} +function serializeNode(node, preserveCase) { + if (typeof node !== "object" || node === null || !("type" in node)) return ""; + if (node.type === "simple-block") { + const start = node.associatedToken.value; + return start + serialize(node.value, preserveCase) + getMirrorToken(start); + } + if (node.type === "function" && "name" in node) { + let args = node.value; + const funcName = preserveCase ? node.name : node.name.toLowerCase(); + if (funcName === "counter") { + let i = args.length - 1; + while (i >= 0 && args[i].type === "whitespace") i--; + if (i >= 0 && args[i].type === "ident" && args[i].value === "decimal") { + let j = i - 1; + while (j >= 0 && args[j].type === "whitespace") j--; + if (j >= 0 && args[j].type === "comma") args = args.slice(0, j); + } + } else if (funcName === "url") args = trimWhitespaceTokens(args); + else if (funcName === "attr") { + let i = 0; + while (i < args.length && args[i].type === "whitespace") i++; + if (i < args.length && args[i].type === "delim" && args[i].value === "|") { + args = args.slice(i + 1); + let k = args.length - 1; + while (k >= 0 && args[k].type === "whitespace") k--; + if (k >= 0 && args[k].type === "string" && args[k].value === "") { + let l = k - 1; + while (l >= 0 && args[l].type === "whitespace") l--; + if (l >= 0 && args[l].type === "comma") args = args.slice(0, l); + } + } + args = trimWhitespaceTokens(args); + } + return `${funcName}(${serialize(args, preserveCase)})`; + } + return serializeToken(node, preserveCase); +} +function serializeToken(token, preserveCase = false) { + switch (token.type) { + case "ident": return serializeIdentifier(token.value); + case "at-keyword": return "@" + serializeIdentifier(token.value); + case "hash": return "#" + token.value; + case "string": return preserveCase && token.originalText && !token.originalText.endsWith("\\") ? token.originalText : serializeString(token.value); + case "url": return preserveCase ? serializeUrlToken(token.value, token.originalText) : serializeUrl(token.value); + case "delim": return token.value === "\\" ? "\\\n" : token.value; + case "number": return formatNumber(token.value); + case "percentage": return formatNumber(token.value) + "%"; + case "dimension": return formatNumber(token.value) + (token.unit ? serializeIdentifier(token.unit) : ""); + case "whitespace": return preserveCase && token.originalText ? token.originalText : token.value; + case "comment": return token.value || "/**/"; + case "CDO": return ""; + case "colon": return ":"; + case "semicolon": return ";"; + case "comma": return ","; + case "[": + case "]": + case "{": + case "}": + case "(": + case ")": return token.value; + case "function": return serializeIdentifier(preserveCase ? token.value : token.value.toLowerCase()) + "("; + case "unicode-range": return token.value; + case "EOF": return ""; + default: return token.value || ""; + } +} +function getMirrorToken(start) { + if (start === "{") return "}"; + if (start === "[") return "]"; + if (start === "(") return ")"; + return ""; +} +function getOriginalText(values) { + let text = ""; + for (const val of values) if (val.type === "simple-block") text += (val.associatedToken.originalText || "") + getOriginalText(val.value) + getMirrorToken(val.associatedToken.value); + else if (val.type === "function") { + const func = val; + text += `${func.name}(${getOriginalText(func.value)})`; + } else text += val.originalText || val.value; + return text; +} +/** +* // cssom-1 #serialize-an-identifier +*/ +function serializeIdentifier(id) { + let result = ""; + for (let i = 0; i < id.length; i++) { + const charCode = id.charCodeAt(i); + const char = id[i]; + if (charCode === 0) { + result += "�"; + continue; + } + if (charCode >= 1 && charCode <= 31 || charCode === 127) { + result += escapeAsCodePoint(charCode); + continue; + } + if (i === 0 && charCode >= 48 && charCode <= 57) { + result += escapeAsCodePoint(charCode); + continue; + } + if (i === 1 && charCode >= 48 && charCode <= 57 && id.charCodeAt(0) === 45) { + result += escapeAsCodePoint(charCode); + continue; + } + if (i === 0 && charCode === 45 && id.length === 1) { + result += "\\-"; + continue; + } + if (charCode >= 128 || charCode === 45 || charCode === 95 || charCode >= 48 && charCode <= 57 || charCode >= 65 && charCode <= 90 || charCode >= 97 && charCode <= 122) { + result += char; + continue; + } + result += "\\" + char; + } + return result; +} +/** +* // cssom-1 #serialize-a-string +*/ +function serializeString(s) { + let result = "\""; + for (let i = 0; i < s.length; i++) { + const charCode = s.charCodeAt(i); + const char = s[i]; + if (charCode === 0) { + result += "�"; + continue; + } + if (charCode >= 1 && charCode <= 31 || charCode === 127) { + result += escapeAsCodePoint(charCode); + continue; + } + if (charCode === 34 || charCode === 92) { + result += "\\" + char; + continue; + } + result += char; + } + result += "\""; + return result; +} +function serializeUrl(val) { + return `url(${serializeString(val)})`; +} +function serializeUrlToken(val, originalText) { + if (originalText && !val.includes("�") && originalText.endsWith(")")) return originalText; + let result = ""; + for (let i = 0; i < val.length; i++) { + const charCode = val.charCodeAt(i); + const char = val[i]; + if (charCode === 34 || charCode === 39 || charCode === 40 || charCode === 41 || charCode === 92 || charCode <= 32 || charCode === 127) result += "\\" + char; + else result += char; + } + return `url(${result})`; +} +function escapeAsCodePoint(charCode) { + return "\\" + charCode.toString(16) + " "; +} +const logicalShorthandsEntries = Object.entries({ + "margin-inline": { + start: "margin-inline-start", + end: "margin-inline-end", + allowDifferent: true + }, + "padding-inline": { + start: "padding-inline-start", + end: "padding-inline-end", + allowDifferent: true + }, + "margin-block": { + start: "margin-block-start", + end: "margin-block-end", + allowDifferent: true + }, + "padding-block": { + start: "padding-block-start", + end: "padding-block-end", + allowDifferent: true + }, + "inset-inline": { + start: "inset-inline-start", + end: "inset-inline-end", + allowDifferent: true + }, + "inset-block": { + start: "inset-block-start", + end: "inset-block-end", + allowDifferent: true + }, + "border-inline-width": { + start: "border-inline-start-width", + end: "border-inline-end-width", + allowDifferent: true + }, + "border-block-width": { + start: "border-block-start-width", + end: "border-block-end-width", + allowDifferent: true + }, + "border-inline-style": { + start: "border-inline-start-style", + end: "border-inline-end-style", + allowDifferent: true + }, + "border-block-style": { + start: "border-block-start-style", + end: "border-block-end-style", + allowDifferent: true + }, + "border-inline-color": { + start: "border-inline-start-color", + end: "border-inline-end-color", + allowDifferent: true + }, + "border-block-color": { + start: "border-block-start-color", + end: "border-block-end-color", + allowDifferent: true + }, + "border-inline": { + start: "border-inline-start", + end: "border-inline-end", + allowDifferent: false + }, + "border-block": { + start: "border-block-start", + end: "border-block-end", + allowDifferent: false + }, + "overflow": { + start: "overflow-x", + end: "overflow-y", + allowDifferent: true + }, + "overscroll-behavior": { + start: "overscroll-behavior-x", + end: "overscroll-behavior-y", + allowDifferent: true + } +}); +const propertyToGroup = { + "margin-top": "margin", + "margin-right": "margin", + "margin-bottom": "margin", + "margin-left": "margin", + "margin-inline-start": "margin", + "margin-inline-end": "margin", + "margin-block-start": "margin", + "margin-block-end": "margin", + "padding-top": "padding", + "padding-right": "padding", + "padding-bottom": "padding", + "padding-left": "padding", + "padding-inline-start": "padding", + "padding-inline-end": "padding", + "padding-block-start": "padding", + "padding-block-end": "padding", + "top": "inset", + "right": "inset", + "bottom": "inset", + "left": "inset", + "inset-inline-start": "inset", + "inset-inline-end": "inset", + "inset-block-start": "inset", + "inset-block-end": "inset", + "border-top-width": "border-width", + "border-right-width": "border-width", + "border-bottom-width": "border-width", + "border-left-width": "border-width", + "border-inline-start-width": "border-width", + "border-inline-end-width": "border-width", + "border-block-start-width": "border-width", + "border-block-end-width": "border-width", + "border-top-style": "border-style", + "border-right-style": "border-style", + "border-bottom-style": "border-style", + "border-left-style": "border-style", + "border-inline-start-style": "border-style", + "border-inline-end-style": "border-style", + "border-block-start-style": "border-style", + "border-block-end-style": "border-style", + "border-top-color": "border-color", + "border-right-color": "border-color", + "border-bottom-color": "border-color", + "border-left-color": "border-color", + "border-inline-start-color": "border-color", + "border-inline-end-color": "border-color", + "border-block-start-color": "border-color", + "border-block-end-color": "border-color", + "width": "size", + "height": "size", + "inline-size": "size", + "block-size": "size", + "min-width": "min-size", + "min-height": "min-size", + "min-inline-size": "min-size", + "min-block-size": "min-size", + "max-width": "max-size", + "max-height": "max-size", + "max-inline-size": "max-size", + "max-block-size": "max-size", + "border-top-left-radius": "border-radius", + "border-top-right-radius": "border-radius", + "border-bottom-right-radius": "border-radius", + "border-bottom-left-radius": "border-radius", + "border-start-start-radius": "border-radius", + "border-start-end-radius": "border-radius", + "border-end-start-radius": "border-radius", + "border-end-end-radius": "border-radius", + "border-top": "border", + "border-right": "border", + "border-bottom": "border", + "border-left": "border", + "border-block-start": "border", + "border-block-end": "border", + "border-inline-start": "border", + "border-inline-end": "border", + "overflow-x": "overflow", + "overflow-y": "overflow", + "overflow-inline": "overflow", + "overflow-block": "overflow", + "overscroll-behavior-x": "overscroll-behavior", + "overscroll-behavior-y": "overscroll-behavior", + "overscroll-behavior-inline": "overscroll-behavior", + "overscroll-behavior-block": "overscroll-behavior", + "outline-color": "outline", + "outline-style": "outline", + "outline-width": "outline", + "list-style-type": "list-style", + "list-style-position": "list-style", + "list-style-image": "list-style", + "flex-grow": "flex", + "flex-shrink": "flex", + "flex-basis": "flex", + "font-style": "font", + "font-variant-caps": "font", + "font-weight": "font", + "font-stretch": "font", + "font-size": "font", + "line-height": "font", + "font-family": "font", + "font-variant-ligatures": "font-variant", + "font-variant-alternates": "font-variant", + "font-variant-numeric": "font-variant", + "font-variant-east-asian": "font-variant", + "font-variant-position": "font-variant", + "font-variant-emoji": "font-variant" +}; +const genericShorthands = { + "border-top": [ + "border-top-width", + "border-top-style", + "border-top-color" + ], + "border-right": [ + "border-right-width", + "border-right-style", + "border-right-color" + ], + "border-bottom": [ + "border-bottom-width", + "border-bottom-style", + "border-bottom-color" + ], + "border-left": [ + "border-left-width", + "border-left-style", + "border-left-color" + ], + "border-block-start": [ + "border-block-start-width", + "border-block-start-style", + "border-block-start-color" + ], + "border-block-end": [ + "border-block-end-width", + "border-block-end-style", + "border-block-end-color" + ], + "border-inline-start": [ + "border-inline-start-width", + "border-inline-start-style", + "border-inline-start-color" + ], + "border-inline-end": [ + "border-inline-end-width", + "border-inline-end-style", + "border-inline-end-color" + ], + "outline": [ + "outline-color", + "outline-style", + "outline-width" + ], + "list-style": [ + "list-style-type", + "list-style-position", + "list-style-image" + ], + "flex": [ + "flex-grow", + "flex-shrink", + "flex-basis" + ], + "border-image": [ + "border-image-source", + "border-image-slice", + "border-image-width", + "border-image-outset", + "border-image-repeat" + ], + "line-clamp": [ + "max-lines", + "block-ellipsis", + "continue" + ] +}; +const genericShorthandsEntries = Object.entries(genericShorthands); +function checkIntervening(decls, allDecls, declIndices) { + const indices = decls.map((d) => declIndices.get(d)); + const startIdx = Math.min(...indices); + const endIdx = Math.max(...indices); + const names = new Set(decls.map((d) => d.name)); + const groups = new Set(decls.map((d) => propertyToGroup[d.name]).filter(Boolean)); + const isSideShorthand = groups.size > 1; + for (let i = startIdx + 1; i < endIdx; i++) { + const intervening = allDecls[i]; + if (names.has(intervening.name)) continue; + const interveningGroup = propertyToGroup[intervening.name]; + if (!interveningGroup) continue; + if (isSideShorthand) { + const sidePrefix = decls[0].name.replace(/-(width|style|color)$/, ""); + if (intervening.name.startsWith(sidePrefix + "-") || ["all", "border"].includes(intervening.name)) return true; + } else { + if (groups.has(interveningGroup)) return true; + if (["all", "border"].includes(intervening.name)) return true; + } + } + return false; +} +const BOX_SHORTHAND_NAMES = /* @__PURE__ */ new Set("margin,padding,border-width,border-style,border-color,scroll-margin,scroll-padding,inset,overflow-clip-margin,border-radius".split(",")); +const BOX_SHORTHAND_ENTRIES = Object.entries(SHORTHANDS).filter(([shorthand, def]) => BOX_SHORTHAND_NAMES.has(shorthand) && def.logicalLonghands?.length === 4); +function tryCombineBoxShorthand(d, declMap, processed, declarations, declIndices) { + for (const [shorthand, def] of BOX_SHORTHAND_ENTRIES) { + const physical = def.longhands; + const logical = def.logicalLonghands; + const isPhysical = physical.includes(d.name); + const isLogical = logical.includes(d.name); + if (!isPhysical && !isLogical) continue; + const allDecls = (isPhysical ? physical : logical).map((name) => declMap.get(name)); + if (!allDecls.every((other) => other && !processed.has(other) && other.important === d.important)) continue; + if (checkIntervening(allDecls, declarations, declIndices)) continue; + const valuesForContract = {}; + for (const other of allDecls) valuesForContract[other.name] = other.value; + const value = def.contract(valuesForContract); + if (value !== null) { + for (const other of allDecls) processed.add(other); + return `${shorthand}: ${value}${d.important ? " !important" : ""}`; + } + } + return null; +} +function tryCombineLogicalShorthand(d, declMap, processed, declarations, declIndices) { + for (const [shorthand, longhands] of logicalShorthandsEntries) { + if (d.name !== longhands.start && d.name !== longhands.end) continue; + const otherName = d.name === longhands.start ? longhands.end : longhands.start; + const otherDecl = declMap.get(otherName); + if (!otherDecl || processed.has(otherDecl) || d.important !== otherDecl.important) continue; + if (checkIntervening([d, otherDecl], declarations, declIndices)) continue; + const startDecl = d.name === longhands.start ? d : otherDecl; + const endDecl = d.name === longhands.end ? d : otherDecl; + const valS = serialize(startDecl.value).trim(); + const valE = serialize(endDecl.value).trim(); + if (valS === valE) { + processed.add(startDecl); + processed.add(endDecl); + return `${shorthand}: ${valS}${d.important ? " !important" : ""}`; + } + if (longhands.allowDifferent && !valS.includes("var(") && !valE.includes("var(")) { + processed.add(startDecl); + processed.add(endDecl); + return `${shorthand}: ${valS} ${valE}${d.important ? " !important" : ""}`; + } + } + return null; +} +function tryCombineGenericShorthand(d, declMap, processed, declarations, declIndices) { + for (const [shorthand, longhands] of genericShorthandsEntries) { + if (!longhands.includes(d.name)) continue; + const allDecls = longhands.map((name) => declMap.get(name)); + if (!allDecls.every((other) => other && !processed.has(other) && other.important === d.important)) continue; + if (checkIntervening(allDecls, declarations, declIndices)) continue; + const record = {}; + for (const other of allDecls) record[other.name] = other.value; + const contracted = SHORTHANDS[shorthand]?.contract(record); + if (contracted !== null && contracted !== void 0) { + for (const other of allDecls) processed.add(other); + return { + name: shorthand, + value: contracted, + important: d.important + }; + } + } + return null; +} +const BORDER_PHYSICAL_SIDE_LONGHANDS = [ + "border-top-width", + "border-top-style", + "border-top-color", + "border-right-width", + "border-right-style", + "border-right-color", + "border-bottom-width", + "border-bottom-style", + "border-bottom-color", + "border-left-width", + "border-left-style", + "border-left-color" +]; +const BORDER_IMAGE_LONGHAND_NAMES = [ + "border-image-source", + "border-image-slice", + "border-image-width", + "border-image-outset", + "border-image-repeat" +]; +function tryCombineBorderFull(d, declMap, processed, declarations, declIndices) { + if (!BORDER_PHYSICAL_SIDE_LONGHANDS.includes(d.name)) return null; + const allDecls = BORDER_PHYSICAL_SIDE_LONGHANDS.map((name) => declMap.get(name)); + if (!allDecls.every((other) => other && !processed.has(other) && other.important === d.important)) return null; + if (checkIntervening(allDecls, declarations, declIndices)) return null; + const imageDecls = BORDER_IMAGE_LONGHAND_NAMES.map((name) => declMap.get(name)).filter((img) => Boolean(img && !processed.has(img))); + if (imageDecls.length === 0) return null; + const imageRecord = { + "border-image-source": [{ + type: "ident", + value: "none" + }], + "border-image-slice": [{ + type: "percentage", + value: 100, + sign: null + }], + "border-image-width": [{ + type: "number", + value: 1, + sign: null, + numberType: "integer" + }], + "border-image-outset": [{ + type: "number", + value: 0, + sign: null, + numberType: "integer" + }], + "border-image-repeat": [{ + type: "ident", + value: "stretch" + }] }; - - CSSOM.CSSValueExpression.prototype = Object.create(CSSOM.CSSValue.prototype); - CSSOM.CSSValueExpression.prototype.constructor = CSSOM.CSSValueExpression; - - Object.setPrototypeOf(CSSOM.CSSValueExpression, CSSOM.CSSValue); - - /** - * parse css expression() value - * - * @return {Object} - * - error: - * or - * - idx: - * - expression: - * - * Example: - * - * .selector { - * zoom: expression(documentElement.clientWidth > 1000 ? '1000px' : 'auto'); - * } - */ - CSSOM.CSSValueExpression.prototype.parse = function() { - var token = this._token, - idx = this._idx; - - var character = '', - expression = '', - error = '', - info, - paren = []; - - - for (; ; ++idx) { - character = token.charAt(idx); - - // end of token - if (character === '') { - error = 'css expression error: unfinished expression!'; - break; + for (const img of imageDecls) imageRecord[img.name] = img.value; + if (!isInitialBorderImage(imageRecord)) return null; + const record = { ...imageRecord }; + for (const other of allDecls) record[other.name] = other.value; + const contracted = SHORTHANDS["border"]?.contract(record); + if (contracted !== null && contracted !== void 0) { + for (const other of allDecls) processed.add(other); + for (const img of imageDecls) processed.add(img); + return `border: ${contracted}${d.important ? " !important" : ""}`; + } + return null; +} +const ORDERED_SHORTHAND_COMBINERS = [ + { + name: "font", + longhands: SHORTHANDS["font"].longhands + }, + { + name: "font-variant", + longhands: SHORTHANDS["font-variant"].longhands + }, + { + name: "background", + longhands: [ + "background-image", + "background-position", + "background-size", + "background-repeat", + "background-attachment", + "background-origin", + "background-clip", + "background-color" + ] + }, + { + name: "border-block", + longhands: [ + "border-block-start-width", + "border-block-start-style", + "border-block-start-color", + "border-block-end-width", + "border-block-end-style", + "border-block-end-color" + ] + }, + { + name: "border-inline", + longhands: [ + "border-inline-start-width", + "border-inline-start-style", + "border-inline-start-color", + "border-inline-end-width", + "border-inline-end-style", + "border-inline-end-color" + ] + } +]; +function tryCombineNamedShorthand(shorthandName, longhands, d, declMap, processed, declarations, declIndices) { + if (!longhands.includes(d.name)) return null; + const allDecls = longhands.map((name) => declMap.get(name)); + if (!allDecls.every((other) => other && !processed.has(other) && other.important === d.important)) return null; + if (checkIntervening(allDecls, declarations, declIndices)) return null; + const record = {}; + for (const other of allDecls) record[other.name] = other.value; + const contracted = SHORTHANDS[shorthandName]?.contract(record); + if (contracted !== null && contracted !== void 0) { + for (const other of allDecls) processed.add(other); + return `${shorthandName}: ${contracted}${d.important ? " !important" : ""}`; + } + return null; +} +const CSS_WIDE_AND_DEFAULT = /* @__PURE__ */ new Set("initial,inherit,unset,revert,revert-layer,default".split(",")); +function serializeFontFamilyItem(tokens) { + const nonWs = tokens.filter((t) => t.type !== "whitespace" && t.type !== "comment" && t.type !== "EOF"); + if (nonWs.length === 1 && nonWs[0].type === "string") { + let strVal = nonWs[0].value; + if (strVal.startsWith("'") && strVal.endsWith("'") || strVal.startsWith("\"") && strVal.endsWith("\"")) strVal = strVal.slice(1, -1); + const lower = strVal.toLowerCase(); + if (GENERIC_FONT_FAMILIES.has(lower) || CSS_WIDE_AND_DEFAULT.has(lower)) return `"${strVal}"`; + if (strVal !== strVal.trim() || /\s{2,}|\t|\n|\r/.test(strVal)) return `"${strVal}"`; + const words = strVal.split(" "); + return words.length > 0 && words.every((word) => word.length > 0 && !/^[0-9]|^--|^-[0-9]/.test(word) && /^[a-zA-Z_-][a-zA-Z0-9_-]*$/.test(word)) ? strVal : `"${strVal}"`; + } + if (nonWs.every((t) => t.type === "ident")) return nonWs.map((t) => serializeNode(t, false)).join(" "); + return tokens.map((t) => serializeNode(t, false)).join(""); +} +function serializeFontFamily(values) { + const groups = []; + let current = []; + for (const token of values) if (token.type === "comma") { + groups.push(current); + current = []; + } else current.push(token); + if (current.length > 0) groups.push(current); + return groups.map((g) => serializeFontFamilyItem(g).trim()).filter((s) => s.length > 0).join(", "); +} +function serializeDeclarations(declarations) { + if (declarations.length === 0) return ""; + const declMap = /* @__PURE__ */ new Map(); + const declIndices = /* @__PURE__ */ new Map(); + for (let i = 0; i < declarations.length; i++) { + const d = declarations[i]; + declMap.set(d.name, d); + declIndices.set(d, i); + } + if (declarations.length >= ALL_SHORTHAND_LONGHANDS.length) { + const firstDecl = declMap.get(ALL_SHORTHAND_LONGHANDS[0]); + if (firstDecl) { + const firstValLower = serialize(firstDecl.value).trim().toLowerCase(); + const firstImportant = firstDecl.important; + if (CSS_WIDE_AND_DEFAULT.has(firstValLower) || firstValLower.startsWith("var(")) { + if (ALL_SHORTHAND_LONGHANDS.every((lh) => { + const d = declMap.get(lh); + return d && serialize(d.value).trim().toLowerCase() === firstValLower && d.important === firstImportant; + })) { + const allLonghandsSet = new Set(ALL_SHORTHAND_LONGHANDS); + const allDecl = { + type: "declaration", + name: "all", + value: firstDecl.value, + important: firstImportant + }; + const newDecls = []; + let inserted = false; + for (const d of declarations) if (allLonghandsSet.has(d.name)) { + if (!inserted) { + newDecls.push(allDecl); + inserted = true; + } + } else newDecls.push(d); + return serializeDeclarations(newDecls); + } } - - switch(character) { - case '(': - paren.push(character); - expression += character; - break; - - case ')': - paren.pop(character); - expression += character; - break; - - case '/': - if ((info = this._parseJSComment(token, idx))) { // comment? - if (info.error) { - error = 'css expression error: unfinished comment in expression!'; - } else { - idx = info.idx; - // ignore the comment + } + } + const processed = /* @__PURE__ */ new Set(); + const result = []; + for (const d of declarations) { + if (processed.has(d)) continue; + let combined = tryCombineBorderFull(d, declMap, processed, declarations, declIndices); + if (!combined) for (const spec of ORDERED_SHORTHAND_COMBINERS) { + combined = tryCombineNamedShorthand(spec.name, spec.longhands, d, declMap, processed, declarations, declIndices); + if (combined) break; + } + if (!combined) combined = tryCombineBoxShorthand(d, declMap, processed, declarations, declIndices); + if (!combined) { + const generic = tryCombineGenericShorthand(d, declMap, processed, declarations, declIndices); + if (generic) { + const sides = [ + "border-top", + "border-right", + "border-bottom", + "border-left" + ]; + if (sides.includes(generic.name)) { + const sideResults = sides.map((side) => { + if (side === generic.name) return generic; + const existing = declMap.get(side); + if (existing && !processed.has(existing)) return { + name: side, + value: serialize(existing.value).trim(), + important: existing.important, + decl: existing + }; + const longhands = genericShorthands[side]; + if (!longhands) return null; + const sideLonghands = longhands.map((lh) => declMap.get(lh)); + if (sideLonghands.every((lh) => lh && !processed.has(lh) && lh.important === generic.important)) { + if (checkIntervening(sideLonghands, declarations, declIndices)) return null; + return { + name: side, + value: sideLonghands.map((lh) => serialize(lh.value).trim()).filter((v) => v !== "").join(" "), + important: generic.important, + longhands: sideLonghands + }; } - } else if ((info = this._parseJSRexExp(token, idx))) { // regexp - idx = info.idx; - expression += info.text; - } else { // other - expression += character; + return null; + }); + if (sideResults.every((r) => r !== null && r.value === generic.value && r.important === generic.important)) { + for (const r of sideResults) if (r && "longhands" in r) r.longhands.forEach((lh) => processed.add(lh)); + else if (r && "decl" in r) processed.add(r.decl); + combined = `border: ${generic.value}${generic.important ? " !important" : ""}`; + } else combined = `${generic.name}: ${generic.value}${generic.important ? " !important" : ""}`; + } else combined = `${generic.name}: ${generic.value}${generic.important ? " !important" : ""}`; + } + } + if (!combined) combined = tryCombineLogicalShorthand(d, declMap, processed, declarations, declIndices); + if (combined) result.push(combined); + else { + const isCustom = d.name.startsWith("--"); + let val; + if (d.name === "font-family") val = serializeFontFamily(d.value); + else if (d.name === "flex-basis" && serialize(d.value).trim() === "0") val = "0px"; + else val = d.raw && !d.raw.includes("var(") ? d.raw : serialize(d.value, isCustom, d.name).trim(); + result.push(`${serializeIdentifier(d.name)}: ${val}${d.important ? " !important" : ""}`); + processed.add(d); + } + } + return result.join("; ") + ";"; +} +function serializeSelectorList(list, nsContext) { + const context = { + hasDefaultNamespace: typeof nsContext === "boolean" ? nsContext : Boolean(nsContext?.hasDefaultNamespace), + defaultNamespacePrefixes: typeof nsContext === "object" && nsContext !== null ? nsContext.defaultNamespacePrefixes : void 0 + }; + return list.selectors.map((s) => s.type === "invalid-selector" ? serialize(s.tokens) : serializeComplexSelector(s, context)).join(", "); +} +function serializeComplexSelector(complex, nsContext) { + const { hasDefaultNamespace, defaultNamespacePrefixes } = nsContext; + return complex.items.map((item, idx) => { + if (item.type === "combinator") { + if (item.value === " ") return " "; + return idx === 0 ? `${item.value} ` : ` ${item.value} `; + } + return item.selectors.filter((s, sIdx) => { + if (s.type === "universal-selector" && item.selectors.length > 1 && sIdx === 0) { + const isDefaultNs = s.namespace !== void 0 && s.namespace !== "" && defaultNamespacePrefixes?.has(s.namespace); + if (s.namespace === void 0 || isDefaultNs || s.namespace === "*" && !hasDefaultNamespace) return false; + } + return true; + }).map((s) => serializeSimpleSelector(s, nsContext)).join(""); + }).join(""); +} +function formatAnPlusB(tokens) { + const parsed = parseAnPlusB(tokens); + if (parsed === null) return serialize(tokens).trim(); + const { a, b } = parsed; + if (a === 0) return b.toString(); + const partA = a === 1 ? "n" : a === -1 ? "-n" : `${a}n`; + if (b === 0) return partA; + return b > 0 ? `${partA}+${b}` : `${partA}${b}`; +} +function formatSelectorNamespacePrefix(namespace, nsContext, isElementSelector) { + if (namespace === void 0) return ""; + if (isElementSelector && namespace !== "" && nsContext.defaultNamespacePrefixes?.has(namespace)) return ""; + if (namespace === "*") return !isElementSelector || nsContext.hasDefaultNamespace ? "*|" : ""; + if (namespace === "") return isElementSelector ? "|" : ""; + return `${serializeIdentifier(namespace)}|`; +} +const NTH_PSEUDO_CLASSES = /* @__PURE__ */ new Set("nth-child,nth-last-child,nth-of-type,nth-last-of-type".split(",")); +function serializeSimpleSelector(simple, nsContext) { + switch (simple.type) { + case "type-selector": return formatSelectorNamespacePrefix(simple.namespace, nsContext, true) + serializeIdentifier(simple.name); + case "universal-selector": return formatSelectorNamespacePrefix(simple.namespace, nsContext, true) + "*"; + case "id-selector": return "#" + serializeIdentifier(simple.name); + case "class-selector": return "." + serializeIdentifier(simple.name); + case "attribute-selector": { + let attr = "[" + formatSelectorNamespacePrefix(simple.namespace, nsContext, false) + serializeIdentifier(simple.name); + if (simple.operator) attr += simple.operator + serializeString(simple.value || ""); + if (simple.flags) attr += " " + simple.flags; + return attr + "]"; + } + case "pseudo-class-selector": { + let pc = `:${simple.name}`; + const isNth = NTH_PSEUDO_CLASSES.has(simple.name.toLowerCase()); + if (simple.argument) { + if ("type" in simple.argument && simple.argument.type === "selector-list") { + const selStr = serializeSelectorList(simple.argument, nsContext); + pc += isNth && simple.nth ? `(${formatAnPlusB(simple.nth)} of ${selStr})` : `(${selStr})`; + } else { + const tokens = simple.argument; + pc += `(${isNth ? formatAnPlusB(tokens) : serialize(tokens).trim()})`; + } + } + return pc; + } + case "pseudo-element-selector": { + let pe = `::${simple.name}`; + if (simple.argument) pe += "type" in simple.argument && simple.argument.type === "selector-list" ? `(${serializeSelectorList(simple.argument, nsContext)})` : `(${serialize(simple.argument).trim()})`; + return pe; + } + case "nesting-selector": return "&"; + default: return ""; + } +} +// src/AbstractTokenizer.ts +/** +* @license +* Copyright 2026 Google LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +var AbstractTokenizer = class { + unicodeRangesAllowed = false; + errors = []; + parseError(message) { + this.errors.push({ message }); + console.warn(`CSS Parse Error: ${message}`); + } + consumeToken() { + while (true) { + this.consumeComments(); + const cp = this.consume(); + if (cp === -1) return { + type: "EOF", + value: "" + }; + if (this.isWhitespace(cp)) { + while (this.isWhitespace(this.cp)) this.consume(); + return { + type: "whitespace", + value: " " + }; + } + switch (cp) { + case 34: return this.consumeStringToken(34); + case 35: + if (this.isIdentCodePoint(this.cp) || this.isValidEscape(this.cp, this.peek(1))) { + const token = { + type: "hash", + value: "", + hashType: "unrestricted" + }; + if (this.wouldStartIdentSequence(this.cp, this.peek(1), this.peek(2))) token.hashType = "id"; + token.value = this.consumeIdentSequence(); + return token; } - break; - - case "'": - case '"': - info = this._parseJSString(token, idx, character); - if (info) { // string - idx = info.idx; - expression += info.text; - } else { - expression += character; + return { + type: "delim", + value: String.fromCodePoint(cp) + }; + case 39: return this.consumeStringToken(39); + case 40: return { + type: "(", + value: "(" + }; + case 41: return { + type: ")", + value: ")" + }; + case 43: + if (this.wouldStartNumber(cp, this.cp, this.peek(1))) { + this.reconsume(); + return this.consumeNumericToken(); } - break; - - default: - expression += character; - break; + return { + type: "delim", + value: "+" + }; + case 44: return { + type: "comma", + value: "," + }; + case 45: + if (this.wouldStartNumber(cp, this.cp, this.peek(1))) { + this.reconsume(); + return this.consumeNumericToken(); + } + if (this.cp === 45 && this.peek(1) === 62) { + this.consume(); + this.consume(); + return { + type: "CDC", + value: "-->" + }; + } + if (this.wouldStartIdentSequence(cp, this.cp, this.peek(1))) { + this.reconsume(); + return this.consumeIdentLikeToken(); + } + return { + type: "delim", + value: "-" + }; + case 46: + if (this.wouldStartNumber(cp, this.cp, this.peek(1))) { + this.reconsume(); + return this.consumeNumericToken(); + } + return { + type: "delim", + value: "." + }; + case 47: return { + type: "delim", + value: "/" + }; + case 58: return { + type: "colon", + value: ":" + }; + case 59: return { + type: "semicolon", + value: ";" + }; + case 60: + if (this.cp === 33 && this.peek(1) === 45 && this.peek(2) === 45) { + this.consume(); + this.consume(); + this.consume(); + return { + type: "CDO", + value: "