🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

jsdom

Package Overview
Dependencies
Maintainers
6
Versions
285
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

jsdom - npm Package Compare versions

Comparing version
29.1.1
to
30.0.0
+170
lib/generated/idl/CSS.js
"use strict";
const conversions = require("webidl-conversions");
const utils = require("./utils.js");
const implSymbol = utils.implSymbol;
const ctorRegistrySymbol = utils.ctorRegistrySymbol;
const interfaceName = "CSS";
exports.is = value => {
return utils.isObject(value) && Object.hasOwn(value, implSymbol) && value[implSymbol] instanceof Impl.implementation;
};
exports.isImpl = value => {
return utils.isObject(value) && value instanceof Impl.implementation;
};
exports.convert = (globalObject, value, { context = "The provided value" } = {}) => {
if (exports.is(value)) {
return utils.implForWrapper(value);
}
throw new globalObject.TypeError(`${context} is not of type 'CSS'.`);
};
function makeWrapper(globalObject, newTarget) {
let proto;
if (newTarget !== undefined) {
proto = newTarget.prototype;
}
if (!utils.isObject(proto)) {
proto = globalObject[ctorRegistrySymbol]["CSS"].prototype;
}
return Object.create(proto);
}
exports.create = (globalObject, constructorArgs, privateData) => {
const wrapper = makeWrapper(globalObject);
return exports.setup(wrapper, globalObject, constructorArgs, privateData);
};
exports.createImpl = (globalObject, constructorArgs, privateData) => {
const wrapper = exports.create(globalObject, constructorArgs, privateData);
return utils.implForWrapper(wrapper);
};
exports._internalSetup = (wrapper, globalObject) => {};
exports.setup = (wrapper, globalObject, constructorArgs = [], privateData = {}) => {
privateData.wrapper = wrapper;
exports._internalSetup(wrapper, globalObject);
Object.defineProperty(wrapper, implSymbol, {
value: new Impl.implementation(globalObject, constructorArgs, privateData),
configurable: true
});
wrapper[implSymbol][utils.wrapperSymbol] = wrapper;
if (Impl.init) {
Impl.init(wrapper[implSymbol]);
}
return wrapper;
};
exports.new = (globalObject, newTarget) => {
const wrapper = makeWrapper(globalObject, newTarget);
exports._internalSetup(wrapper, globalObject);
Object.defineProperty(wrapper, implSymbol, {
value: Object.create(Impl.implementation.prototype),
configurable: true
});
wrapper[implSymbol][utils.wrapperSymbol] = wrapper;
if (Impl.init) {
Impl.init(wrapper[implSymbol]);
}
return wrapper[implSymbol];
};
const exposed = new Set(["Window"]);
exports.install = (globalObject, globalNames) => {
if (!globalNames.some(globalName => exposed.has(globalName))) {
return;
}
const ctorRegistry = utils.initCtorRegistry(globalObject);
class CSS {
constructor() {
throw new globalObject.TypeError("Illegal constructor");
}
supports(conditionText) {
const esValue = this !== null && this !== undefined ? this : globalObject;
if (!exports.is(esValue)) {
throw new globalObject.TypeError("'supports' called on an object that is not a valid instance of CSS.");
}
if (arguments.length < 1) {
throw new globalObject.TypeError(
`Failed to execute 'supports' on 'CSS': 1 argument required, but only ${arguments.length} present.`
);
}
const args = [];
switch (arguments.length) {
case 1:
{
let curArg = arguments[0];
curArg = conversions["DOMString"](curArg, {
context: "Failed to execute 'supports' on 'CSS': parameter 1",
globals: globalObject
});
args.push(curArg);
}
break;
default:
{
let curArg = arguments[0];
curArg = conversions["DOMString"](curArg, {
context: "Failed to execute 'supports' on 'CSS': parameter 1",
globals: globalObject
});
args.push(curArg);
}
{
let curArg = arguments[1];
curArg = conversions["DOMString"](curArg, {
context: "Failed to execute 'supports' on 'CSS': parameter 2",
globals: globalObject
});
args.push(curArg);
}
}
return esValue[implSymbol].supports(...args);
}
escape(ident) {
const esValue = this !== null && this !== undefined ? this : globalObject;
if (!exports.is(esValue)) {
throw new globalObject.TypeError("'escape' called on an object that is not a valid instance of CSS.");
}
if (arguments.length < 1) {
throw new globalObject.TypeError(
`Failed to execute 'escape' on 'CSS': 1 argument required, but only ${arguments.length} present.`
);
}
const args = [];
{
let curArg = arguments[0];
curArg = conversions["DOMString"](curArg, {
context: "Failed to execute 'escape' on 'CSS': parameter 1",
globals: globalObject
});
args.push(curArg);
}
return utils.tryWrapperForImpl(esValue[implSymbol].escape(...args));
}
}
delete CSS.constructor;
Object.defineProperties(CSS.prototype, {
supports: { enumerable: true },
escape: { enumerable: true },
[Symbol.toStringTag]: { value: "CSS", configurable: true }
});
ctorRegistry[interfaceName] = CSS;
};
const Impl = require("../../jsdom/living/css/CSS-impl.js");
"use strict";
const propertyDefinitions = require("../../../generated/css-property-definitions.js");
const { asciiLowercase } = require("../helpers/strings.js");
const csstree = require("./helpers/patched-csstree.js");
const PROP_VAL_REGEXP = /^([^():]+)\s*:\s*(.+)\s*$/;
class CSSImpl {
#testNode;
constructor(globalObject) {
this._globalObject = globalObject;
}
// https://drafts.csswg.org/cssom/#the-css.escape()-method
escape(ident) {
const l = ident.length;
const firstCodeUnit = l > 0 ? ident.charCodeAt(0) : 0;
let escaped = "";
for (let i = 0; i < l; i++) {
const codeUnit = ident.charCodeAt(i);
// NULL character (U+0000) becomes the replacement character (U+FFFD)
if (codeUnit === 0x0000) {
escaped += "\uFFFD";
continue;
}
// Control characters, a leading digit, or the digit in a "hyphen + digit" sequence
if (
(codeUnit >= 0x0001 && codeUnit <= 0x001F) ||
codeUnit === 0x007F ||
(i === 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
(i === 1 && codeUnit >= 0x0030 && codeUnit <= 0x0039 && firstCodeUnit === 0x002D)
) {
escaped += `\\${codeUnit.toString(16)} `;
continue;
}
// If the string consists of a single hyphen
if (i === 0 && l === 1 && codeUnit === 0x002D) {
escaped += `\\${ident.charAt(i)}`;
continue;
}
// ASCII characters that do not require escaping (alphanumeric, hyphen, underscore)
// and non-ASCII characters
if (
codeUnit >= 0x0080 ||
codeUnit === 0x002D ||
codeUnit === 0x005F ||
(codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
(codeUnit >= 0x0041 && codeUnit <= 0x005A) ||
(codeUnit >= 0x0061 && codeUnit <= 0x007A)
) {
escaped += ident.charAt(i);
continue;
}
// Escape all other symbols with a backslash
escaped += `\\${ident.charAt(i)}`;
}
return escaped;
}
// https://drafts.csswg.org/css-conditional-3/#the-css-namespace
supports(...args) {
if (args.length === 1) {
return this.#supportsConditionText(args[0].trim());
}
const [property, value] = args;
return this.#supportsDeclaration(property, value);
}
#supportsConditionText(conditionText) {
// Condition without parens (e.g., "color: red")
if (PROP_VAL_REGEXP.test(conditionText)) {
const [, property, value] = PROP_VAL_REGEXP.exec(conditionText);
return this.#supportsDeclaration(property, value);
}
const ast = csstree.parse(`@supports ${conditionText} {}`, { context: "stylesheet" });
return this.#evaluateList(ast.children.first.prelude?.children);
}
#supportsDeclaration(property, value) {
// Custom property
if (property.startsWith("--")) {
try {
const ast = csstree.parse(value, { context: "value" });
if (ast) {
return true;
}
} catch {
// Fall through
}
return false;
}
const lowerProp = asciiLowercase(property);
if (!propertyDefinitions.has(lowerProp)) {
return false;
}
return this.#isValueSupported(lowerProp, value);
}
#evaluateList(list) {
if (!list || !list.head) {
return false;
}
let result = null;
let currentOperator = null;
let isNot = false;
let expectingCondition = true;
let current = list.head;
while (current !== null) {
const node = current.data;
const { children, declaration, name, property, type, value } = node;
if (type === "WhiteSpace" || type === "Comment") {
current = current.next;
continue;
} else if (type === "Identifier") {
const ident = asciiLowercase(name);
if (ident === "not") {
if (result !== null || isNot) {
return false;
}
isNot = true;
} else if (ident === "and" || ident === "or") {
if (isNot || expectingCondition) {
return false;
} else if (currentOperator !== null && currentOperator !== ident) {
return false;
}
currentOperator = ident;
expectingCondition = true;
} else {
// Any other unexpected identifier makes the condition invalid
return false;
}
current = current.next;
continue;
}
if (!expectingCondition) {
return false;
}
let childResult;
switch (type) {
case "Parentheses":
case "Condition": {
childResult = this.#evaluateList(children);
break;
}
case "Declaration": {
childResult = this.#validateDeclarationAST(property, value);
break;
}
case "SupportsDeclaration": {
childResult = this.#validateDeclarationAST(declaration.property, declaration.value);
break;
}
default: {
// TODO: Support Function types (e.g., selector())
return false;
}
}
if (isNot) {
childResult = !childResult;
isNot = false;
}
if (result === null) {
result = childResult;
} else if (currentOperator === "and") {
result &&= childResult;
} else if (currentOperator === "or") {
result ||= childResult;
}
expectingCondition = false;
current = current.next;
}
// Catch unexpected end of input (e.g., CSS.supports("not"), CSS.supports("(color: red) and"))
if (isNot || (expectingCondition && result !== null)) {
return false;
}
return result === null ? false : result;
}
#validateDeclarationAST(property, ast) {
// Custom property
if (property.startsWith("--")) {
return true;
}
const lowerProp = asciiLowercase(property);
if (!propertyDefinitions.has(lowerProp)) {
return false;
}
return this.#isValueSupported(lowerProp, csstree.generate(ast));
}
#isValueSupported(property, value) {
if (!this.#testNode) {
this.#testNode = this._globalObject.document.createElement("div");
}
this.#testNode.style.setProperty(property, value);
const isSupported = this.#testNode.style.getPropertyValue(property) !== "";
this.#testNode.style.cssText = "";
return isSupported;
}
}
exports.implementation = CSSImpl;
"use strict";
// TODO: Support math keyword.
const cssValues = require("./css-values");
const FONT_SIZE_REGEXP = /^((?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?)(%|[a-z]+)$/;
const LENGTH_REGEXP = /^([+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?)(%|[a-z]*)$/;
// Absolute font size mapping table.
const absoluteFontSize = new Map([
["xx-small", { px: 9, ratio: 9 / 16 }],
["x-small", { px: 10, ratio: 5 / 8 }],
["small", { px: 13, ratio: 13 / 16 }],
["medium", { px: 16, ratio: 1 }],
["large", { px: 18, ratio: 9 / 8 }],
["x-large", { px: 24, ratio: 1.5 }],
["xx-large", { px: 32, ratio: 2 }],
["xxx-large", { px: 48, ratio: 3 }]
]);
// Ratio of relative font size to pixels.
const relativeFontSize = new Map([
["smaller", 1 / 1.2],
["larger", 1.2]
]);
// Ratio of absolute length to pixels.
const absoluteLength = new Map([
["cm", 96 / 2.54],
["mm", 96 / 25.4],
["q", 96 / 101.6],
["in", 96],
["pc", 16],
["pt", 96 / 72],
["px", 1]
]);
// Ratio of root relative length to pixels.
const rootRelativeLength = new Map([
["rcap", 1],
["rch", 0.5],
["rem", 1],
["rex", 0.5],
["ric", 1],
["rlh", 1.2]
]);
// Ratio of relative length or percentage to pixels.
const relativeLength = new Map([
["%", 0.01],
["cap", 1],
["ch", 0.5],
["em", 1],
["ex", 0.5],
["ic", 1],
["lh", 1.2]
]);
function resolveFontSizeInPixels(elementImpl, size, root, parent) {
const isRelative = typeof parent === "number";
if (absoluteFontSize.has(size)) {
const pxSize = absoluteFontSize.get(size).px;
if (isRelative) {
return pxSize * parent;
}
return pxSize;
} else if (isRelative && relativeFontSize.has(size)) {
return relativeFontSize.get(size) * parent;
}
const match = FONT_SIZE_REGEXP.exec(size);
if (match) {
const [, value, unit] = match;
if (absoluteLength.has(unit)) {
return value * absoluteLength.get(unit);
} else if (rootRelativeLength.has(unit)) {
const pxSize = root ?? absoluteFontSize.get("medium").px;
return value * rootRelativeLength.get(unit) * pxSize;
} else if (relativeLength.has(unit)) {
if (isRelative) {
return value * relativeLength.get(unit) * parent;
}
const pxSize = root ?? absoluteFontSize.get("medium").px;
return value * relativeLength.get(unit) * pxSize;
}
}
return Number.NaN;
}
function resolveLengthInPixels(elementImpl, size, dimension, isFontSize) {
const { em, rem, vh, vw } = dimension;
if (absoluteFontSize.has(size)) {
return absoluteFontSize.get(size).px;
} else if (relativeFontSize.has(size)) {
return relativeFontSize.get(size) * em;
} else if (cssValues.hasCalcFunc(size)) {
let resolvedSize;
if (elementImpl === elementImpl._ownerDocument.documentElement) {
resolvedSize = cssValues.resolveCalc(size, {
dimension: {
em: absoluteFontSize.get("medium").px,
rem: absoluteFontSize.get("medium").px,
vh: elementImpl._globalObject.innerHeight / 100,
vw: elementImpl._globalObject.innerWidth / 100
},
format: "computedValue"
});
} else {
resolvedSize = cssValues.resolveCalc(size, {
dimension,
format: "computedValue"
});
}
const [, value] = FONT_SIZE_REGEXP.exec(resolvedSize);
return Number(value);
}
const match = LENGTH_REGEXP.exec(size);
if (match) {
const [, value, unit] = match;
// Percentage value resolution varies depending on the property.
// Therefore, except for font-size, the values are returned as-is without attempting to resolve them.
if (unit === "%" && !isFontSize) {
return size;
} else if (absoluteLength.has(unit)) {
return value * absoluteLength.get(unit);
} else if (rootRelativeLength.has(unit)) {
const pxSize = rem ?? absoluteFontSize.get("medium").px;
return value * rootRelativeLength.get(unit) * pxSize;
} else if (relativeLength.has(unit)) {
const pxSize = em ?? absoluteFontSize.get("medium").px;
return value * relativeLength.get(unit) * pxSize;
}
switch (unit) {
case "vb": {
return value * vh;
}
case "vi": {
return value * vw;
}
case "vmax": {
return value * Math.max(vh, vw);
}
case "vmin": {
return value * Math.min(vh, vw);
}
default: {
if (Object.hasOwn(dimension, unit)) {
return value * dimension[unit];
}
}
}
}
// Return as-is as a fallback.
return size;
}
exports.absoluteFontSize = absoluteFontSize;
exports.resolveFontSizeInPixels = resolveFontSizeInPixels;
exports.resolveLengthInPixels = resolveLengthInPixels;
"use strict";
const parsers = require("../helpers/css-values");
const backgroundPosition = require("./backgroundPosition");
const property = backgroundPosition.propertyX;
const shorthand = backgroundPosition.property;
const descriptor = {
set(v) {
v = v.trim();
if (parsers.hasVarFunc(v)) {
this._setProperty("background", "");
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
backgroundPosition.setLonghand(this, property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the background-position-x property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
return backgroundPosition.parseLonghand(property, v);
}
module.exports = {
descriptor,
parse,
property
};
"use strict";
const parsers = require("../helpers/css-values");
const backgroundPosition = require("./backgroundPosition");
const property = backgroundPosition.propertyY;
const shorthand = backgroundPosition.property;
const descriptor = {
set(v) {
v = v.trim();
if (parsers.hasVarFunc(v)) {
this._setProperty("background", "");
this._setProperty(shorthand, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
backgroundPosition.setLonghand(this, property, val, priority);
}
}
},
get() {
return this.getPropertyValue(property);
},
enumerable: true,
configurable: true
};
/**
* Parses the background-position-y property value.
*
* @param {string} v - The value to parse.
* @returns {string|undefined} The parsed value or undefined if invalid.
*/
function parse(v) {
return backgroundPosition.parseLonghand(property, v);
}
module.exports = {
descriptor,
parse,
property
};
+1
-1

@@ -109,3 +109,3 @@ "use strict";

if (utils.isObject(curArg)) {
if (curArg[Symbol.iterator] !== undefined) {
if (utils.getMethod(curArg, Symbol.iterator, "Failed to construct 'Headers': parameter 1") !== undefined) {
if (!utils.isObject(curArg)) {

@@ -112,0 +112,0 @@ throw new globalObject.TypeError(

@@ -114,3 +114,3 @@ "use strict";

const arrayBufferByteLengthGetter =
Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, "byteLength").get;
Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, "byteLength").get;
function isArrayBuffer(value) {

@@ -202,2 +202,99 @@ try {

function getMethod(value, property, errPrefix = "The provided value") {
const func = value[property];
if (func === undefined || func === null) {
return undefined;
}
if (typeof func !== "function") {
throw new TypeError(`${errPrefix}'s ${property} property is not a function.`);
}
return func;
}
function createAsyncFromSyncIterator(syncIterator) {
// Instead of re-implementing CreateAsyncFromSyncIterator and %AsyncFromSyncIteratorPrototype%,
// we use yield* inside an async generator function to achieve the same result.
// Wrap the sync iterator inside a sync iterable, so we can use it with yield*.
const syncIterable = {
[Symbol.iterator]: () => syncIterator
};
// Create an async generator function and immediately invoke it.
const asyncIterator = (async function* () {
return yield* syncIterable;
})();
// Return as an async iterator record.
return asyncIterator;
}
function convertAsyncSequence(object, itemConverter, errPrefix = "The provided value") {
if (!isObject(object)) {
throw new TypeError(`${errPrefix} is not an object.`);
}
let method = getMethod(object, Symbol.asyncIterator, errPrefix);
let type = "async";
if (method === undefined) {
method = getMethod(object, Symbol.iterator, errPrefix);
if (method === undefined) {
throw new TypeError(`${errPrefix} is not an async iterable object.`);
}
type = "sync";
}
return {
object,
method,
type,
// The wrapperSymbol ensures that if the async sequence is used as a return value,
// that it exposes the original JavaScript value.
// https://webidl.spec.whatwg.org/#js-async-iterable
[wrapperSymbol]: object,
// Implement the async iterator protocol, so users can iterate
// the async sequence directly (e.g. with for await...of)
// instead of needing to call a separate helper function to open the async sequence.
// https://webidl.spec.whatwg.org/#async-sequence-open
[Symbol.asyncIterator]() {
return openAsyncSequence(object, method, type, itemConverter, `${errPrefix}'s iterator`);
}
};
}
function openAsyncSequence(object, method, type, itemConverter, errPrefix = "The provided value") {
let iterator = call(method, object);
if (!isObject(iterator)) {
throw new TypeError(`${errPrefix}'s method must return an object`);
}
if (type === "sync") {
iterator = createAsyncFromSyncIterator(iterator);
}
const nextMethod = iterator.next;
return {
async next() {
const nextResult = await call(nextMethod, iterator);
if (!isObject(nextResult)) {
throw new TypeError(`${errPrefix}'s next method must return an object`);
}
const { done, value } = nextResult;
if (done) {
return { done: true, value: undefined };
}
return { done: false, value: itemConverter(value) };
},
async return(reason) {
const returnMethod = getMethod(iterator, "return", errPrefix);
if (returnMethod === undefined) {
return { done: true, value: undefined };
}
const returnResult = await call(returnMethod, iterator, reason);
if (!isObject(returnResult)) {
throw new TypeError(`${errPrefix}'s return method must return an object`);
}
return { done: true, value: undefined };
},
[Symbol.asyncIterator]() {
return this;
}
};
}
const supportsPropertyIndex = Symbol("supports property index");

@@ -237,2 +334,4 @@ const supportedPropertyIndices = Symbol("supported property indices");

isArrayIndexPropName,
getMethod,
convertAsyncSequence,
supportsPropertyIndex,

@@ -239,0 +338,0 @@ supportedPropertyIndices,

@@ -115,3 +115,5 @@ "use strict";

if (utils.isObject(curArg)) {
if (curArg[Symbol.iterator] !== undefined) {
if (
utils.getMethod(curArg, Symbol.iterator, "Failed to construct 'WebSocket': parameter 2") !== undefined
) {
if (!utils.isObject(curArg)) {

@@ -118,0 +120,0 @@ throw new globalObject.TypeError(

@@ -8,4 +8,2 @@ "use strict";

const { Dispatcher } = require("undici");
const WrapHandler = require("undici/lib/handler/wrap-handler.js");
const UnwrapHandler = require("undici/lib/handler/unwrap-handler.js");
const { toBase64 } = require("@exodus/bytes/base64.js");

@@ -76,9 +74,2 @@ const { utf8Encode } = require("../../living/helpers/encoding");

dispatch(opts, handler) {
// Wrap handler to normalize OLD API (onConnect/onHeaders/onData/onComplete/onError) to NEW API
// (onRequestStart/onResponseStart/onResponseData/onResponseEnd/onResponseError). This is necessary because undici's
// internals call the old API a lot, despite it being undocumented:
// * https://github.com/nodejs/undici/issues/4771
// * https://github.com/nodejs/undici/issues/4780
const wrappedHandler = WrapHandler.wrap(handler);
// Get URL from opaque if present (required for file: URLs since they have origin "null"),

@@ -90,3 +81,3 @@ // otherwise reconstruct from opts.origin + opts.path (works for http/https/ws/wss)

if (urlRecord === null) {
wrappedHandler.onResponseError?.(null, new TypeError(`Invalid URL: ${urlString}`));
handler.onResponseError?.(null, new TypeError(`Invalid URL: ${urlString}`));
return false;

@@ -96,7 +87,7 @@ }

if (urlRecord.scheme === "data") {
return this.#dispatchDataURL(urlRecord, wrappedHandler);
return this.#dispatchDataURL(urlRecord, handler);
}
if (urlRecord.scheme === "file") {
return this.#dispatchFileURL(urlRecord, wrappedHandler);
return this.#dispatchFileURL(urlRecord, handler);
}

@@ -108,4 +99,4 @@

// instead of becoming unhandled rejections that silently prevent the response promise from ever resolving.
this.#dispatchHTTP(urlRecord, wrappedHandler, opts).catch(err => {
wrappedHandler.onResponseError?.(null, err);
this.#dispatchHTTP(urlRecord, handler, opts).catch(err => {
handler.onResponseError?.(null, err);
});

@@ -202,2 +193,8 @@ return true;

return currentController.reason;
},
get rawHeaders() {
return currentController.rawHeaders;
},
get rawTrailers() {
return currentController.rawTrailers;
}

@@ -597,8 +594,4 @@ };

#buildDispatchChain() {
// Convert handlers to old-style before passing to the base dispatcher. When Node's built-in fetch() has run,
// it registers a v6 undici Agent as the global dispatcher via the shared Symbol.for('undici.globalDispatcher.1').
// Our v7 code passes new-style handlers, which v6 rejects. Old-style handlers are accepted by both v6 and v7.
// See https://github.com/jsdom/jsdom/issues/4047
let innerDispatch = (opts, h) => {
return this.#baseDispatcher.dispatch(opts, UnwrapHandler.unwrap(h));
return this.#baseDispatcher.dispatch(opts, h);
};

@@ -605,0 +598,0 @@

@@ -130,2 +130,8 @@ "use strict";

return abortController.signal.reason;
},
get rawHeaders() {
return innerUndiciController?.rawHeaders;
},
get rawTrailers() {
return innerUndiciController?.rawTrailers;
}

@@ -138,16 +144,16 @@ };

},
onRequestUpgrade(...args) {
handler.onRequestUpgrade?.(...args);
onRequestUpgrade(controller, ...args) {
handler.onRequestUpgrade?.(undiciController, ...args);
},
onResponseStart(...args) {
handler.onResponseStart?.(...args);
onResponseStart(controller, ...args) {
handler.onResponseStart?.(undiciController, ...args);
},
onResponseData(...args) {
handler.onResponseData?.(...args);
onResponseData(controller, ...args) {
handler.onResponseData?.(undiciController, ...args);
},
onResponseEnd(...args) {
handler.onResponseEnd?.(...args);
onResponseEnd(controller, ...args) {
handler.onResponseEnd?.(undiciController, ...args);
},
onResponseError(...args) {
handler.onResponseError?.(...args);
onResponseError(controller, ...args) {
handler.onResponseError?.(undiciController, ...args);
}

@@ -154,0 +160,0 @@ };

@@ -8,2 +8,3 @@ "use strict";

const { define, mixin } = require("../utils");
const CSS = require("../../generated/idl/CSS");
const Element = require("../../generated/idl/Element");

@@ -83,2 +84,5 @@ const EventTarget = require("../../generated/idl/EventTarget");

// Mount CSS.
window.CSS = CSS.create(window, [], {});
// Now we have an EventTarget contructor so we can work on the prototype chain.

@@ -85,0 +89,0 @@

@@ -12,2 +12,3 @@ "use strict";

const csstree = require("./helpers/patched-csstree");
const fontSizes = require("./helpers/font-sizes");
const {

@@ -339,3 +340,3 @@ borderProperties,

const { inherited, initial = "", longhands } = cssValues.getPropertyDefinition(property);
const { caseSensitive, functionTypes = {} } = this.#getPropertyMetadata(property);
const { caseSensitive, dimensionTypes = {}, functionTypes = {} } = this.#getPropertyMetadata(property);
const isColor = Boolean(functionTypes.color || functionTypes.paint);

@@ -374,3 +375,3 @@

return this.#resolveLonghand(property, value, { caseSensitive, isColor });
return this.#resolveLonghand(property, value, { caseSensitive, dimensionTypes, isColor });
}

@@ -425,3 +426,3 @@

#resolveLonghand(property, value, { caseSensitive, isColor }) {
#resolveLonghand(property, value, { caseSensitive, dimensionTypes, isColor }) {
const options = this.#prepareComputedValueOpts();

@@ -438,2 +439,27 @@ const parsedValue = cssValues.parsePropertyValue(property, value, {

}
} else if (dimensionTypes) {
const { length: lengthType } = dimensionTypes;
if (lengthType) {
const isFontSize = property === "font-size";
const dimension =
this.#ownerNode !== this.#ownerNode._ownerDocument.documentElement &&
options.dimension ?
{ ...options.dimension } :
{};
// Use the element's own font-size for em units if the property is not "font-size".
if (!isFontSize) {
const ownFontSize = this.getPropertyValue("font-size");
if (ownFontSize) {
const parsedOwnFontSize = parseFloat(ownFontSize);
if (!Number.isNaN(parsedOwnFontSize)) {
dimension.em = parsedOwnFontSize;
}
}
}
const resolvedValue =
fontSizes.resolveLengthInPixels(this.#ownerNode, value, dimension, isFontSize);
if (typeof resolvedValue === "number" && !Number.isNaN(resolvedValue)) {
return `${Number(resolvedValue.toPrecision(6))}px`;
}
}
}

@@ -494,5 +520,7 @@

const rawColor = this.#values.get("color") ?? "";
const rawFontSize = this.#values.get("font-size") ?? "";
if (
this.#computedValueOpts.get("rawColorScheme") === rawColorScheme &&
this.#computedValueOpts.get("rawColor") === rawColor
this.#computedValueOpts.get("rawColor") === rawColor &&
this.#computedValueOpts.get("rawFontSize") === rawFontSize
) {

@@ -504,2 +532,3 @@ return options;

this.#computedValueOpts.set("rawColor", rawColor);
this.#computedValueOpts.set("rawFontSize", rawFontSize);

@@ -550,4 +579,29 @@ // Prepare color-scheme.

// TODO: Add customProperty, dimension etc.
// Prepare dimension.
let rem, em;
if (this.#ownerNode._ownerDocument.documentElement.firstElementChild) {
const rootFontSize = computedStyle.getInheritedPropertyValue(
"font-size",
this.#ownerNode._ownerDocument.documentElement.firstElementChild,
{ inherit: true, initial: "medium" }
);
rem = fontSizes.resolveFontSizeInPixels(this.#ownerNode, rootFontSize);
} else {
rem = fontSizes.resolveFontSizeInPixels(this.#ownerNode, "medium");
}
if (this.#ownerNode.parentElement) {
em = computedStyle.getParentFontSizeInPixels(this.#ownerNode);
} else {
em = rem;
}
const dimension = {
em,
rem,
vh: this._globalObject.innerHeight / 100,
vw: this._globalObject.innerWidth / 100
};
options.dimension = dimension;
// TODO: Add customProperty etc.
// Store options.

@@ -577,3 +631,3 @@ this.#computedValueOpts.set("options", options);

if (borderProperties.has(itemProperty)) {
const itemValue = this.getPropertyValue(itemProperty);
const itemValue = this.#values.get(itemProperty) ?? "";
const longhandPriority = this._priorities.get(itemProperty) ?? "";

@@ -634,3 +688,3 @@ let itemPriority = longhandPriority;

} else {
const longhandValue = this.getPropertyValue(longhandProperty);
const longhandValue = this.#values.get(longhandProperty) ?? "";
const longhandPriority = this._priorities.get(longhandProperty) ?? "";

@@ -736,3 +790,3 @@ if (!longhandValue || longhandPriority !== priority) {

for (const [longhandProperty] of shorthandFor) {
const longhandValue = this.getPropertyValue(longhandProperty);
const longhandValue = this.#values.get(longhandProperty) ?? "";
const longhandPriority = this._priorities.get(longhandProperty) ?? "";

@@ -739,0 +793,0 @@ if (!longhandValue || longhandPriority !== priority) {

@@ -10,7 +10,8 @@ "use strict";

const CSSStyleRule = require("../../../../generated/idl/CSSStyleRule.js");
const { asciiLowercase } = require("../../helpers/strings");
const { asciiLowercase } = require("../../helpers/strings.js");
const { evaluateMediaList } = require("../MediaList-impl.js");
const { parseStyleSheet } = require("./css-parser");
const { isGlobalKeyword } = require("./css-values");
const { systemColors } = require("./system-colors");
const { parseStyleSheet } = require("./css-parser.js");
const { isGlobalKeyword } = require("./css-values.js");
const { absoluteFontSize } = require("./font-sizes.js");
const { systemColors } = require("./system-colors.js");

@@ -56,2 +57,5 @@ const defaultStyleSheet = fs.readFileSync(

// Cache the declaration before processing.
styleCache.set(elementImpl, declaration);
applyStyleSheetRules(elementImpl, declaration);

@@ -63,4 +67,2 @@

styleCache.set(elementImpl, declaration);
return declaration;

@@ -198,3 +200,8 @@ }

function replaceEmptyValueAndKeywords(property, value, elementImpl, { inherit, initial, isColor, longhands }) {
function replaceEmptyValueAndKeywords(
property,
value,
elementImpl,
{ inherit, initial, isColor, longhands }
) {
if (value === "") {

@@ -218,10 +225,6 @@ if (longhands) {

const styleCache = elementImpl._ownerDocument._styleCache;
const { parentElement } = elementImpl;
if (!parentElement) {
return initial;
}
let parent = parentElement;
let parent = elementImpl.parentElement;
while (parent) {
let declaration;
let declaration, value;
if (styleCache.has(parent)) {

@@ -237,3 +240,3 @@ declaration = styleCache.get(parent);

}
let value = declaration.getPropertyValue(property);
value = declaration.getPropertyValue(property);
if (isColor) {

@@ -310,5 +313,30 @@ // Restore the _computed flag.

function getParentFontSizeInPixels(elementImpl) {
const styleCache = elementImpl._ownerDocument._styleCache;
const parent = elementImpl.parentElement;
let declaration;
if (styleCache.has(parent)) {
declaration = styleCache.get(parent);
} else {
declaration = prepareComputedStyleDeclaration(parent, styleCache);
}
const fontSize = declaration.getPropertyValue("font-size");
if (absoluteFontSize.has(fontSize)) {
return absoluteFontSize.get(fontSize).px;
}
const parsed = parseFloat(fontSize);
if (!Number.isNaN(parsed)) {
return parsed;
}
// Fallback to initial font-size (medium)
return absoluteFontSize.get("medium").px;
}
exports.SHADOW_DOM_PSEUDO_REGEXP = /^::(?:part|slotted)\(/i;
exports.getComputedStyleDeclaration = getComputedStyleDeclaration;
exports.getInheritedPropertyValue = getInheritedPropertyValue;
exports.getParentFontSizeInPixels = getParentFontSizeInPixels;
exports.replaceEmptyValueAndKeywords = replaceEmptyValueAndKeywords;

@@ -133,2 +133,5 @@ "use strict";

}
if (opt.format === "computedValue") {
return cssCalc(val, opt);
}
const cacheKey = `resolveCalc_${val}_${opt.format}`;

@@ -147,6 +150,3 @@ const cachedValue = lruCache.get(cacheKey);

if (itemType === AST_TYPES.FUNCTION) {
const value = cssTree
.generate(item)
.replace(/\)(?!\)|\s|,)/g, ") ")
.trim();
const value = generateCSSFromAST(item);
if (calcNameRegEx.test(itemName)) {

@@ -235,6 +235,6 @@ const newValue = cssCalc(value, opt);

case AST_TYPES.FUNCTION: {
const raw = itemCount === 1 ? val : cssTree.generate(item).replace(/\)(?!\)|\s|,)/g, ") ");
const raw = itemCount === 1 ? val : generateCSSFromAST(item);
// Remove "${name}(" from the start and ")" from the end
const itemValue = raw.trim().slice(name.length + 1, -1);
if (name === "calc") {
if (calcNameRegEx.test(name)) {
if (children.size === 1) {

@@ -303,2 +303,6 @@ const child = children.first;

function generateCSSFromAST(ast) {
return cssTree.generate(ast).replace(/\)(?!\)|\s|,)/g, ") ").replace(/,(?!\s)/g, ", ").trim();
}
/**

@@ -305,0 +309,0 @@ * Parses a numeric value (number, dimension, percentage).

@@ -7,2 +7,3 @@ "use strict";

const backgroundColor = require("../properties/backgroundColor");
const backgroundPosition = require("../properties/backgroundPosition");
const border = require("../properties/border");

@@ -40,2 +41,3 @@ const borderWidth = require("../properties/borderWidth");

[background.property, background],
[backgroundPosition.property, backgroundPosition],
[

@@ -230,2 +232,13 @@ border.property,

}
if (property === backgroundPosition.propertyX || property === backgroundPosition.propertyY) {
for (let i = 0; i < bgLength; i++) {
const bgValue = bgValues[i];
const x = bgValue[backgroundPosition.propertyX] ?? background.initialValues.get(backgroundPosition.propertyX);
const y = bgValue[backgroundPosition.propertyY] ?? background.initialValues.get(backgroundPosition.propertyY);
const position = backgroundPosition.serialize(x, y);
if (position) {
bgValue[backgroundPosition.property] = position;
}
}
}
const backgrounds = [];

@@ -241,3 +254,6 @@ for (const bgValue of bgValues) {

let boxSet = false;
for (const longhand of background.initialValues.keys()) {
for (const longhand of background.shorthandFor.keys()) {
if (longhand === backgroundPosition.propertyX || longhand === backgroundPosition.propertyY) {
continue;
}
const value = bgValue[longhand];

@@ -1220,2 +1236,13 @@ if (!value) {

const shorthandItem = shorthandProperties.get(property);
if (property === backgroundPosition.property && item.size === shorthandItem.shorthandFor.size) {
const xItem = item.get(backgroundPosition.propertyX);
const yItem = item.get(backgroundPosition.propertyY);
if (xItem && yItem && xItem.priority === yItem.priority) {
const value = backgroundPosition.serialize(xItem.value, yItem.value);
if (value) {
shorthandItems.set(property, createPropertyItem(property, value, xItem.priority));
}
}
continue;
}
if (item.size === shorthandItem.shorthandFor.size && shorthandItem.position) {

@@ -1384,3 +1411,16 @@ const positionValues = [];

const shorthandItem = shorthandProperties.get(property);
const parsedValues = shorthandItem.parse(value);
let parsedValues = shorthandItem.parse(value);
if (property === backgroundPosition.property) {
const parsedBackgroundPosition = backgroundPosition.parseLonghands(value);
if (parsedBackgroundPosition && hasPrecedingBackground) {
parsedProperties.delete(backgroundPosition.propertyX);
parsedProperties.delete(backgroundPosition.propertyY);
parsedProperties.delete(backgroundPosition.property);
}
parsedValues = parsedBackgroundPosition && {
[backgroundPosition.propertyX]: parsedBackgroundPosition.x,
[backgroundPosition.propertyY]: parsedBackgroundPosition.y,
[backgroundPosition.property]: parsedBackgroundPosition.value
};
}
let omitShorthandProperty = false;

@@ -1387,0 +1427,0 @@ if (Array.isArray(parsedValues)) {

@@ -6,2 +6,4 @@ "use strict";

const backgroundPosition = require("./backgroundPosition");
const backgroundPositionX = require("./backgroundPositionX");
const backgroundPositionY = require("./backgroundPositionY");
const backgroundSize = require("./backgroundSize");

@@ -19,2 +21,4 @@ const backgroundRepeat = require("./backgroundRepeat");

[backgroundPosition.property, "0% 0%"],
[backgroundPositionX.property, "0%"],
[backgroundPositionY.property, "0%"],
[backgroundSize.property, "auto"],

@@ -31,2 +35,4 @@ [backgroundRepeat.property, "repeat"],

[backgroundPosition.property, backgroundPosition],
[backgroundPositionX.property, backgroundPositionX],
[backgroundPositionY.property, backgroundPositionY],
[backgroundSize.property, backgroundSize],

@@ -40,2 +46,13 @@ [backgroundRepeat.property, backgroundRepeat],

const serializationLonghands = [
backgroundImage.property,
backgroundPosition.property,
backgroundSize.property,
backgroundRepeat.property,
backgroundOrigin.property,
backgroundClip.property,
backgroundAttachment.property,
backgroundColor.property
];
const descriptor = {

@@ -64,2 +81,4 @@ set(v) {

[backgroundPosition.property, []],
[backgroundPositionX.property, []],
[backgroundPositionY.property, []],
[backgroundSize.property, []],

@@ -86,2 +105,7 @@ [backgroundRepeat.property, []],

arr.push(value);
}
}
for (const longhand of serializationLonghands) {
const value = bgValue[longhand];
if (value) {
if (longhand === backgroundOrigin.property) {

@@ -130,3 +154,3 @@ if (!isDefaultBox) {

if (parsers.isGlobalKeyword(v)) {
for (const [longhand] of shorthandFor) {
for (const longhand of serializationLonghands) {
if (this.getPropertyValue(longhand) !== v) {

@@ -205,3 +229,3 @@ return "";

let boxSet = false;
for (const [longhand] of shorthandFor) {
for (const longhand of serializationLonghands) {
let value;

@@ -279,2 +303,4 @@ if (bgMap.has(longhand)) {

[backgroundPosition.property]: initialValues.get(backgroundPosition.property),
[backgroundPositionX.property]: initialValues.get(backgroundPositionX.property),
[backgroundPositionY.property]: initialValues.get(backgroundPositionY.property),
[backgroundSize.property]: initialValues.get(backgroundSize.property),

@@ -291,2 +317,4 @@ [backgroundRepeat.property]: initialValues.get(backgroundRepeat.property),

[backgroundPosition.property]: initialValues.get(backgroundPosition.property),
[backgroundPositionX.property]: initialValues.get(backgroundPositionX.property),
[backgroundPositionY.property]: initialValues.get(backgroundPositionY.property),
[backgroundSize.property]: initialValues.get(backgroundSize.property),

@@ -343,8 +371,11 @@ [backgroundRepeat.property]: initialValues.get(backgroundRepeat.property),

case backgroundPosition.property: {
const parsedValue = value.parse(part);
if (parsedValue) {
bgPosition.push(parsedValue);
if (value.parse(part)) {
bgPosition.push(part);
}
break;
}
case backgroundPositionX.property:
case backgroundPositionY.property: {
break;
}
case backgroundRepeat.property: {

@@ -409,2 +440,6 @@ const parsedValue = value.parse(part);

}
case backgroundPositionX.property:
case backgroundPositionY.property: {
break;
}
case backgroundRepeat.property: {

@@ -439,6 +474,7 @@ const parsedValue = value.parse(part);

if (bgPosition.length) {
const { parse: parser } = shorthandFor.get(backgroundPosition.property);
const value = parser(bgPosition.join(" "));
const value = backgroundPosition.parseLonghands(bgPosition.join(" "));
if (value) {
bg[backgroundPosition.property] = value;
bg[backgroundPosition.property] = value.value;
bg[backgroundPositionX.property] = value.x;
bg[backgroundPositionY.property] = value.y;
}

@@ -445,0 +481,0 @@ }

@@ -9,5 +9,7 @@ "use strict";

const property = "background-position";
const shorthand = "background";
const keyX = ["left", "right"];
const keyY = ["top", "bottom"];
const propertyX = "background-position-x";
const propertyY = "background-position-y";
const background = "background";
const keyX = ["left", "right", "x-start", "x-end"];
const keyY = ["top", "bottom", "y-start", "y-end"];
const keywordsX = ["center", ...keyX];

@@ -17,2 +19,7 @@ const keywordsY = ["center", ...keyY];

const shorthandFor = new Map([
[propertyX, { property: propertyX, position: "x" }],
[propertyY, { property: propertyY, position: "y" }]
]);
const descriptor = {

@@ -22,10 +29,17 @@ set(v) {

if (parsers.hasVarFunc(v)) {
this._setProperty(shorthand, "");
this._setProperty(background, "");
this._setProperty(propertyX, "");
this._setProperty(propertyY, "");
this._setProperty(property, v);
} else {
const val = parse(v);
if (typeof val === "string") {
const val = parseLonghands(v);
if (val) {
const priority =
!this._priorities.get(shorthand) && this._priorities.has(property) ? this._priorities.get(property) : "";
this._setProperty(property, val, priority);
!this._priorities.get(background) && this._priorities.has(property) ?
this._priorities.get(property) :
"";
this._setProperty(background, "");
this._setProperty(propertyX, val.x, priority);
this._setProperty(propertyY, val.y, priority);
this._setProperty(property, val.value, priority);
}

@@ -41,3 +55,196 @@ }

function toValue(part) {
if (part.type === AST_TYPES.IDENTIFIER || part.type === AST_TYPES.GLOBAL_KEYWORD) {
return part.name;
}
return parsers.resolveNumericValue([part], { type: "length" });
}
function isX(value) {
return keywordsX.includes(value);
}
function isY(value) {
return keywordsY.includes(value);
}
function isKeyX(value) {
return keyX.includes(value);
}
function isKeyY(value) {
return keyY.includes(value);
}
function isKeyword(value) {
return keywords.includes(value);
}
function isOffset(value) {
return Boolean(value) && !isKeyword(value);
}
function parseLayer(value) {
if (!Array.isArray(value) || !value.length || value.length > 4) {
return undefined;
}
const parts = value.map(toValue);
if (parts.some(part => !part)) {
return undefined;
}
let x = "";
let y = "";
let shorthandValue = "";
switch (parts.length) {
case 1: {
const [val] = parts;
if (parsers.isGlobalKeyword(val)) {
x = val;
y = val;
} else if (val === "center") {
x = val;
y = val;
} else if (isKeyY(val)) {
x = "center";
y = val;
} else {
x = val;
y = "center";
}
break;
}
case 2: {
const [val1, val2] = parts;
if (isX(val1) && isY(val2)) {
x = val1;
y = val2;
} else if (isY(val1) && isX(val2)) {
x = val2;
y = val1;
} else if (isKeyX(val1) && isOffset(val2)) {
x = `${val1} ${val2}`;
y = "center";
shorthandValue = `${val1} ${val2}`;
} else if (val1 === "center" && isOffset(val2)) {
x = val1;
y = val2;
} else if (isKeyY(val1) && isOffset(val2)) {
x = "center";
y = `${val1} ${val2}`;
} else if (isOffset(val1) && isKeyY(val2)) {
x = val1;
y = val2;
} else if (isOffset(val1) && val2 === "center") {
x = val1;
y = val2;
} else if (isOffset(val1) && isOffset(val2)) {
x = val1;
y = val2;
}
break;
}
case 3: {
const [val1, val2, val3] = parts;
if (val1 === "center" && isKeyX(val2) && isOffset(val3)) {
x = `${val2} ${val3}`;
y = val1;
} else if (val1 === "center" && isKeyY(val2) && isOffset(val3)) {
x = val1;
y = `${val2} ${val3}`;
} else if (isKeyX(val1) && isOffset(val2) && isY(val3)) {
x = `${val1} ${val2}`;
y = val3;
} else if (isKeyX(val1) && isKeyY(val2) && isOffset(val3)) {
x = val1;
y = `${val2} ${val3}`;
} else if (isKeyY(val1) && isOffset(val2) && isX(val3)) {
x = val3;
y = `${val1} ${val2}`;
} else if (isKeyY(val1) && isKeyX(val2) && isOffset(val3)) {
x = `${val2} ${val3}`;
y = val1;
}
break;
}
case 4: {
const [val1, val2, val3, val4] = parts;
if (isKeyX(val1) && isOffset(val2) && isKeyY(val3) && isOffset(val4)) {
x = `${val1} ${val2}`;
y = `${val3} ${val4}`;
} else if (isKeyY(val1) && isOffset(val2) && isKeyX(val3) && isOffset(val4)) {
x = `${val3} ${val4}`;
y = `${val1} ${val2}`;
}
break;
}
default:
}
if (!x || !y) {
return undefined;
}
return {
value: shorthandValue || serializeLayer(x, y),
x,
y
};
}
/**
* Parses the background-position property value into shorthand and longhand values.
*
* @param {string} v - The value to parse.
* @returns {object|undefined} The parsed values or undefined if invalid.
*/
function parseLonghands(v) {
if (v === "") {
return {
value: v,
x: v,
y: v
};
}
const values = parsers.splitValue(v, {
delimiter: ","
});
const parsedValues = [];
const xValues = [];
const yValues = [];
for (const val of values) {
const value = parsers.parsePropertyValue(property, val);
if (Array.isArray(value) && value.length) {
const parsed = parseLayer(value);
if (!parsed) {
return undefined;
}
parsedValues.push(parsed.value);
xValues.push(parsed.x);
yValues.push(parsed.y);
} else if (typeof value === "string") {
parsedValues.push(value);
xValues.push(value);
yValues.push(value);
} else {
return undefined;
}
}
if (parsedValues.length) {
return {
value: parsedValues.join(", "),
x: xValues.join(", "),
y: yValues.join(", ")
};
}
return undefined;
}
/**
* Parses the background-position property value.

@@ -49,2 +256,7 @@ *

function parse(v) {
const parsed = parseLonghands(v);
return parsed?.value;
}
function parseLonghand(propertyName, v) {
if (v === "") {

@@ -58,137 +270,24 @@ return v;

for (const val of values) {
const value = parsers.parsePropertyValue(property, val);
const value = parsers.parsePropertyValue(propertyName, val);
if (Array.isArray(value) && value.length) {
const [part1, part2, part3, part4] = value;
let parsedValue = "";
switch (value.length) {
case 1: {
if (part1.type === AST_TYPES.GLOBAL_KEYWORD) {
parsedValue = part1.name;
} else {
const val1 =
part1.type === AST_TYPES.IDENTIFIER ?
part1.name :
parsers.resolveNumericValue([part1], { type: "length" });
if (val1) {
if (val1 === "center") {
parsedValue = `${val1} ${val1}`;
} else if (val1 === "top" || val1 === "bottom") {
parsedValue = `center ${val1}`;
} else {
parsedValue = `${val1} center`;
}
}
}
break;
if (value.length > 2) {
return undefined;
}
const [part1, part2] = value;
if (part1.type === AST_TYPES.GLOBAL_KEYWORD && value.length === 1) {
parsedValues.push(part1.name);
} else if (part1.type === AST_TYPES.IDENTIFIER && value.length === 1) {
parsedValues.push(part1.name);
} else if (value.length === 1) {
const parsedValue = parsers.resolveNumericValue([part1], { type: "length" });
if (!parsedValue) {
return undefined;
}
case 2: {
const val1 =
part1.type === AST_TYPES.IDENTIFIER ? part1.name : parsers.resolveNumericValue([part1], { type: "length" });
const val2 =
part2.type === AST_TYPES.IDENTIFIER ? part2.name : parsers.resolveNumericValue([part2], { type: "length" });
if (val1 && val2) {
if (keywordsX.includes(val1) && keywordsY.includes(val2)) {
parsedValue = `${val1} ${val2}`;
} else if (keywordsY.includes(val1) && keywordsX.includes(val2)) {
parsedValue = `${val2} ${val1}`;
} else if (keywordsX.includes(val1)) {
if (val2 === "center" || !keywordsX.includes(val2)) {
parsedValue = `${val1} ${val2}`;
}
} else if (keywordsY.includes(val2)) {
if (!keywordsY.includes(val1)) {
parsedValue = `${val1} ${val2}`;
}
} else if (!keywordsY.includes(val1) && !keywordsX.includes(val2)) {
parsedValue = `${val1} ${val2}`;
}
}
break;
parsedValues.push(parsedValue);
} else if (part1.type === AST_TYPES.IDENTIFIER) {
const offset = parsers.resolveNumericValue([part2], { type: "length" });
if (!offset) {
return undefined;
}
case 3: {
const val1 = part1.type === AST_TYPES.IDENTIFIER && part1.name;
const val2 =
part2.type === AST_TYPES.IDENTIFIER ? part2.name : parsers.resolveNumericValue([part2], { type: "length" });
const val3 =
part3.type === AST_TYPES.IDENTIFIER ? part3.name : parsers.resolveNumericValue([part3], { type: "length" });
if (val1 && val2 && val3) {
let posX = "";
let offX = "";
let posY = "";
let offY = "";
if (keywordsX.includes(val1)) {
if (keyY.includes(val2)) {
if (!keywords.includes(val3)) {
posX = val1;
posY = val2;
offY = val3;
}
} else if (keyY.includes(val3)) {
if (!keywords.includes(val2)) {
posX = val1;
offX = val2;
posY = val3;
}
}
} else if (keywordsY.includes(val1)) {
if (keyX.includes(val2)) {
if (!keywords.includes(val3)) {
posX = val2;
offX = val3;
posY = val1;
}
} else if (keyX.includes(val3)) {
if (!keywords.includes(val2)) {
posX = val3;
posY = val1;
offY = val2;
}
}
}
if (posX && posY) {
if (offX) {
parsedValue = `${posX} ${offX} ${posY}`;
} else if (offY) {
parsedValue = `${posX} ${posY} ${offY}`;
}
}
}
break;
}
case 4: {
const val1 = part1.type === AST_TYPES.IDENTIFIER && part1.name;
const val2 = parsers.resolveNumericValue([part2], { type: "length" });
const val3 = part3.type === AST_TYPES.IDENTIFIER && part3.name;
const val4 = parsers.resolveNumericValue([part4], { type: "length" });
if (val1 && val2 && val3 && val4) {
let posX = "";
let offX = "";
let posY = "";
let offY = "";
if (keywordsX.includes(val1) && keyY.includes(val3)) {
posX = val1;
offX = val2;
posY = val3;
offY = val4;
} else if (keyX.includes(val1) && keywordsY.includes(val3)) {
posX = val1;
offX = val2;
posY = val3;
offY = val4;
} else if (keyY.includes(val1) && keywordsX.includes(val3)) {
posX = val3;
offX = val4;
posY = val1;
offY = val2;
}
if (posX && offX && posY && offY) {
parsedValue = `${posX} ${offX} ${posY} ${offY}`;
}
}
break;
}
default:
}
if (parsedValue) {
parsedValues.push(parsedValue);
parsedValues.push(`${part1.name} ${offset}`);
} else {

@@ -199,2 +298,4 @@ return undefined;

parsedValues.push(value);
} else {
return undefined;
}

@@ -208,6 +309,68 @@ }

function serializeLayer(x, y) {
if (parsers.isGlobalKeyword(x) || parsers.isGlobalKeyword(y)) {
return x === y ? x : "";
}
if (x === "center" && y === "center") {
return "center center";
}
return `${x} ${y}`;
}
function serialize(x, y) {
const xValues = parsers.splitValue(x, {
delimiter: ","
});
const yValues = parsers.splitValue(y, {
delimiter: ","
});
const length = Math.max(xValues.length, yValues.length);
const values = [];
for (let i = 0; i < length; i++) {
const xValue = xValues[i] !== undefined ? xValues[i] : xValues[0];
const yValue = yValues[i] !== undefined ? yValues[i] : yValues[0];
const value = serializeLayer(xValue, yValue);
if (!value) {
return undefined;
}
values.push(value);
}
return values.join(", ");
}
function setLonghand(style, longhand, value, priority) {
const otherLonghand = longhand === propertyX ? propertyY : propertyX;
style._setProperty(background, "");
style._setProperty(property, "");
style._setProperty(longhand, value, priority);
const otherValue = style.getPropertyValue(otherLonghand);
if (!value || !otherValue || parsers.hasVarFunc(value) || parsers.hasVarFunc(otherValue)) {
return;
}
const otherPriority = style.getPropertyPriority(otherLonghand);
if (priority !== otherPriority) {
return;
}
const x = longhand === propertyX ? value : otherValue;
const y = longhand === propertyY ? value : otherValue;
const shorthandValue = serialize(x, y);
if (shorthandValue) {
style._setProperty(property, shorthandValue, priority);
}
}
module.exports = {
descriptor,
parse,
property
parseLonghand,
parseLonghands,
property,
propertyX,
propertyY,
serialize,
setLonghand,
shorthandFor
};

@@ -24,2 +24,20 @@ "use strict";

/**
* Trim the given `Uint8Array` to a smaller size by transferring its underlying `ArrayBuffer`.
*
* Used when we have a growable internal buffer, usually in the Node.js realm, and the final byte count is smaller than
* the backing store's current size. This is destructive: the original `Uint8Array`'s buffer is transferred, so it is no
* longer usable after calling this function.
*
* This expects `arr` to start at byte offset 0 and represent the beginning of its backing buffer, such as buffers
* allocated internally for accumulating response bytes.
*
* @param {Uint8Array} arr - The `Uint8Array` whose backing buffer should be resized.
* @param {number} newSize - The number of bytes to keep.
* @returns {Uint8Array} - A new `Uint8Array` backed by the transferred, resized `ArrayBuffer`.
*/
exports.trimUint8Array = (arr, newSize) => {
return new Uint8Array(arr.buffer.transfer(newSize));
};
/**
* Create a copy of the data in a given `ArrayBuffer`, in a new `ArrayBuffer` created in the target realm.

@@ -59,7 +77,3 @@ *

exports.copyToArrayBufferInTargetRealmDestructively = (arrayBuffer, newRealm) => {
if (!newRealm.ArrayBuffer.prototype.transfer) {
return exports.copyToArrayBufferInTargetRealm(arrayBuffer, newRealm);
}
return newRealm.ArrayBuffer.prototype.transfer.call(arrayBuffer);
};

@@ -63,2 +63,3 @@ "use strict";

CSSNestedDeclarations: require("../../generated/idl/CSSNestedDeclarations.js"),
CSS: require("../../generated/idl/CSS.js"),

@@ -65,0 +66,0 @@ HTMLElement: require("../../generated/idl/HTMLElement.js"),

@@ -851,3 +851,5 @@ "use strict";

createTreeWalker(root, whatToShow, filter) {
// `@asamuzakjp/dom-selector` calls this method directly for universal selectors, bypassing the Web IDL wrapper
// that supplies the default value.
createTreeWalker(root, whatToShow, filter = null) {
return TreeWalker.createImpl(this._globalObject, [], { root, whatToShow, filter });

@@ -854,0 +856,0 @@ }

@@ -23,3 +23,7 @@ "use strict";

const { fragmentSerialization } = require("../domparsing/serialization");
const { copyToArrayBufferInTargetRealmDestructively, concatTypedArrays } = require("../helpers/binary-data");
const {
copyToArrayBufferInTargetRealmDestructively,
concatTypedArrays,
trimUint8Array
} = require("../helpers/binary-data");
const { setupForSimpleEventAccessors } = require("../helpers/create-event-accessor");

@@ -34,10 +38,2 @@ const { utf8Encode, utf8Decode } = require("../helpers/encoding");

// TODO: simplify after dropping Node.js v20 support
function trimUint8Array(arr, newSize) {
if (arr.buffer.transfer) {
return new Uint8Array(arr.buffer.transfer(newSize));
}
return arr.slice(0, newSize);
}
let syncWorker = null;

@@ -44,0 +40,0 @@

{
"name": "jsdom",
"version": "29.1.1",
"version": "30.0.0",
"description": "A JavaScript implementation of many web standards",

@@ -26,7 +26,7 @@ "keywords": [

"dependencies": {
"@asamuzakjp/css-color": "^5.1.11",
"@asamuzakjp/dom-selector": "^7.1.1",
"@asamuzakjp/css-color": "^6.0.5",
"@asamuzakjp/dom-selector": "^8.2.5",
"@bramus/specificity": "^2.4.2",
"@csstools/css-syntax-patches-for-csstree": "^1.1.3",
"@exodus/bytes": "^1.15.0",
"@csstools/css-syntax-patches-for-csstree": "^1.1.6",
"@exodus/bytes": "^1.15.1",
"css-tree": "^3.2.1",

@@ -37,16 +37,16 @@ "data-urls": "^7.0.0",

"is-potential-custom-element-name": "^1.0.1",
"lru-cache": "^11.3.5",
"lru-cache": "^11.5.2",
"parse5": "^8.0.1",
"saxes": "^6.0.0",
"symbol-tree": "^3.2.4",
"tough-cookie": "^6.0.1",
"undici": "^7.25.0",
"tough-cookie": "^6.0.2",
"undici": "^8.7.0",
"w3c-xmlserializer": "^5.0.0",
"webidl-conversions": "^8.0.1",
"whatwg-mimetype": "^5.0.0",
"whatwg-url": "^16.0.1",
"whatwg-url": "^17.1.0",
"xml-name-validator": "^5.0.0"
},
"peerDependencies": {
"canvas": "^3.0.0"
"canvas": "^3.2.3"
},

@@ -61,20 +61,19 @@ "peerDependenciesMeta": {

"@stylistic/eslint-plugin": "^5.10.0",
"@webref/css": "^8.5.0",
"eslint": "^10.2.1",
"@webref/css": "^8.7.0",
"eslint": "^10.7.0",
"eslint-plugin-html": "^8.1.4",
"eslint-plugin-n": "^17.24.0",
"globals": "^17.5.0",
"js-yaml": "^4.1.1",
"minimatch": "^10.2.5",
"mocha": "^11.7.5",
"eslint-plugin-n": "^18.2.2",
"globals": "^17.7.0",
"js-yaml": "^5.2.1",
"mocha": "^11.7.6",
"mocha-sugar-free": "^1.4.0",
"npm-run-all2": "^8.0.4",
"npm-run-all2": "^9.0.2",
"opener": "^1.5.2",
"pngjs": "^7.0.0",
"semver": "^7.7.4",
"semver": "^7.8.5",
"server-destroy": "^1.0.1",
"tinybench": "^6.0.0",
"tinybench": "^6.0.2",
"webidl2": "^24.5.0",
"webidl2js": "^19.1.0",
"wireit": "^0.14.12"
"webidl2js": "^20.1.0",
"wireit": "^0.14.13"
},

@@ -98,3 +97,4 @@ "scripts": {

"wpt:update": "git submodule update --init --recursive --remote && cd test/web-platform-tests/tests && python wpt.py manifest --path ../wpt-manifest.json",
"benchmark": "node ./benchmark/runner"
"benchmark": "node ./benchmark/runner",
"benchmark:compare": "node ./benchmark/compare"
},

@@ -128,4 +128,4 @@ "wireit": {

"engines": {
"node": "^20.19.0 || ^22.13.0 || >=24.0.0"
"node": "^22.22.2 || ^24.15.0 || >=26.0.0"
}
}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display