Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#1188@minor: Makes properties getters and setters in Node classes acc… #1209

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 67 additions & 1 deletion packages/happy-dom/src/PropertySymbol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ export const setupVMContext = Symbol('setupVMContext');
export const shadowRoot = Symbol('shadowRoot');
export const start = Symbol('start');
export const style = Symbol('style');
export const styleSheet = Symbol('styleSheet');
export const target = Symbol('target');
export const textAreaNode = Symbol('textAreaNode');
export const unobserve = Symbol('unobserve');
Expand All @@ -82,3 +81,70 @@ export const mutationObservers = Symbol('mutationObservers');
export const openerFrame = Symbol('openerFrame');
export const openerWindow = Symbol('openerFrame');
export const popup = Symbol('popup');
export const isConnected = Symbol('isConnected');
export const parentNode = Symbol('parentNode');
export const nodeType = Symbol('nodeType');
export const tagName = Symbol('tagName');
export const prefix = Symbol('prefix');
export const scrollHeight = Symbol('scrollHeight');
export const scrollWidth = Symbol('scrollWidth');
export const scrollTop = Symbol('scrollTop');
export const scrollLeft = Symbol('scrollLeft');
export const attributes = Symbol('attributes');
export const namespaceURI = Symbol('namespaceURI');
export const accessKey = Symbol('accessKey');
export const accessKeyLabel = Symbol('accessKeyLabel');
export const contentEditable = Symbol('contentEditable');
export const isContentEditable = Symbol('isContentEditable');
export const offsetHeight = Symbol('offsetHeight');
export const offsetWidth = Symbol('offsetWidth');
export const offsetLeft = Symbol('offsetLeft');
export const offsetTop = Symbol('offsetTop');
export const clientHeight = Symbol('clientHeight');
export const clientWidth = Symbol('clientWidth');
export const clientLeft = Symbol('clientLeft');
export const clientTop = Symbol('clientTop');
export const name = Symbol('name');
export const specified = Symbol('specified');
export const adoptedStyleSheets = Symbol('adoptedStyleSheets');
export const implementation = Symbol('implementation');
export const readyState = Symbol('readyState');
export const ownerWindow = Symbol('ownerWindow');
export const publicId = Symbol('publicId');
export const systemId = Symbol('systemId');
export const validationMessage = Symbol('validationMessage');
export const validity = Symbol('validity');
export const returnValue = Symbol('returnValue');
export const elements = Symbol('elements');
export const length = Symbol('length');
export const complete = Symbol('complete');
export const naturalHeight = Symbol('naturalHeight');
export const naturalWidth = Symbol('naturalWidth');
export const loading = Symbol('loading');
export const x = Symbol('x');
export const y = Symbol('y');
export const defaultChecked = Symbol('defaultChecked');
export const files = Symbol('files');
export const sheet = Symbol('sheet');
export const volume = Symbol('volume');
export const paused = Symbol('paused');
export const currentTime = Symbol('currentTime');
export const playbackRate = Symbol('playbackRate');
export const defaultPlaybackRate = Symbol('defaultPlaybackRate');
export const muted = Symbol('muted');
export const defaultMuted = Symbol('defaultMuted');
export const preservesPitch = Symbol('preservesPitch');
export const buffered = Symbol('buffered');
export const duration = Symbol('duration');
export const error = Symbol('error');
export const ended = Symbol('ended');
export const networkState = Symbol('networkState');
export const textTracks = Symbol('textTracks');
export const videoTracks = Symbol('videoTracks');
export const seeking = Symbol('seeking');
export const seekable = Symbol('seekable');
export const played = Symbol('played');
export const options = Symbol('options');
export const content = Symbol('content');
export const mode = Symbol('mode');
export const host = Symbol('host');
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export default class BrowserFrameNavigator {
(<number>frame.window.devicePixelRatio) = devicePixelRatio;

if (options?.referrer) {
(<string>frame.window.document.referrer) = options.referrer;
frame.window.document[PropertySymbol.referrer] = options.referrer;
}

if (targetURL.protocol === 'about:') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,21 +79,21 @@ export default abstract class AbstractCSSStyleDeclaration {

if (this.#ownerElement) {
const style = new CSSStyleDeclarationPropertyManager({ cssText });
let styleAttribute = <IAttr>this.#ownerElement.attributes['style'];
let styleAttribute = <IAttr>this.#ownerElement[PropertySymbol.attributes]['style'];

if (!styleAttribute) {
styleAttribute = this.#ownerElement.ownerDocument.createAttribute('style');
styleAttribute = this.#ownerElement[PropertySymbol.ownerDocument].createAttribute('style');
// We use "[PropertySymbol.setNamedItemWithoutConsequences]" here to avoid triggering setting "Element.style.cssText" when setting the "style" attribute.
(<NamedNodeMap>this.#ownerElement.attributes)[
(<NamedNodeMap>this.#ownerElement[PropertySymbol.attributes])[
PropertySymbol.setNamedItemWithoutConsequences
](styleAttribute);
}

if (this.#ownerElement.isConnected) {
this.#ownerElement.ownerDocument[PropertySymbol.cacheID]++;
if (this.#ownerElement[PropertySymbol.isConnected]) {
this.#ownerElement[PropertySymbol.ownerDocument][PropertySymbol.cacheID]++;
}

styleAttribute.value = style.toString();
styleAttribute[PropertySymbol.value] = style.toString();
} else {
this.#style = new CSSStyleDeclarationPropertyManager({ cssText });
}
Expand Down Expand Up @@ -136,25 +136,25 @@ export default abstract class AbstractCSSStyleDeclaration {
if (!stringValue) {
this.removeProperty(name);
} else if (this.#ownerElement) {
let styleAttribute = <IAttr>this.#ownerElement.attributes['style'];
let styleAttribute = <IAttr>this.#ownerElement[PropertySymbol.attributes]['style'];

if (!styleAttribute) {
styleAttribute = this.#ownerElement.ownerDocument.createAttribute('style');
styleAttribute = this.#ownerElement[PropertySymbol.ownerDocument].createAttribute('style');

// We use "[PropertySymbol.setNamedItemWithoutConsequences]" here to avoid triggering setting "Element.style.cssText" when setting the "style" attribute.
(<NamedNodeMap>this.#ownerElement.attributes)[
(<NamedNodeMap>this.#ownerElement[PropertySymbol.attributes])[
PropertySymbol.setNamedItemWithoutConsequences
](styleAttribute);
}

if (this.#ownerElement.isConnected) {
this.#ownerElement.ownerDocument[PropertySymbol.cacheID]++;
if (this.#ownerElement[PropertySymbol.isConnected]) {
this.#ownerElement[PropertySymbol.ownerDocument][PropertySymbol.cacheID]++;
}

const style = this.#elementStyle.getElementStyle();
style.set(name, stringValue, !!priority);

styleAttribute.value = style.toString();
styleAttribute[PropertySymbol.value] = style.toString();
} else {
this.#style.set(name, stringValue, !!priority);
}
Expand All @@ -180,15 +180,16 @@ export default abstract class AbstractCSSStyleDeclaration {
style.remove(name);
const newCSSText = style.toString();

if (this.#ownerElement.isConnected) {
this.#ownerElement.ownerDocument[PropertySymbol.cacheID]++;
if (this.#ownerElement[PropertySymbol.isConnected]) {
this.#ownerElement[PropertySymbol.ownerDocument][PropertySymbol.cacheID]++;
}

if (newCSSText) {
(<IAttr>this.#ownerElement.attributes['style']).value = newCSSText;
(<IAttr>this.#ownerElement[PropertySymbol.attributes]['style'])[PropertySymbol.value] =
newCSSText;
} else {
// We use "[PropertySymbol.removeNamedItemWithoutConsequences]" here to avoid triggering setting "Element.style.cssText" when setting the "style" attribute.
(<NamedNodeMap>this.#ownerElement.attributes)[
(<NamedNodeMap>this.#ownerElement[PropertySymbol.attributes])[
PropertySymbol.removeNamedItemWithoutConsequences
]('style');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export default class CSSStyleDeclarationElementStyle {
return this.getComputedElementStyle();
}

const cssText = this.element.attributes['style']?.value;
const cssText = this.element[PropertySymbol.attributes]['style']?.[PropertySymbol.value];

if (cssText) {
if (this.cache.propertyManager && this.cache.cssText === cssText) {
Expand Down Expand Up @@ -94,34 +94,37 @@ export default class CSSStyleDeclarationElementStyle {
};
let shadowRootElements: Array<IStyleAndElement> = [];

if (!this.element.isConnected) {
if (!this.element[PropertySymbol.isConnected]) {
return new CSSStyleDeclarationPropertyManager();
}

if (
this.cache.propertyManager &&
this.cache.documentCacheID === this.element.ownerDocument[PropertySymbol.cacheID]
this.cache.documentCacheID ===
this.element[PropertySymbol.ownerDocument][PropertySymbol.cacheID]
) {
return this.cache.propertyManager;
}

this.cache.documentCacheID = this.element.ownerDocument[PropertySymbol.cacheID];
this.cache.documentCacheID = this.element[PropertySymbol.ownerDocument][PropertySymbol.cacheID];

// Walks through all parent elements and stores them in an array with element and matching CSS text.
while (styleAndElement.element) {
if (styleAndElement.element.nodeType === NodeTypeEnum.elementNode) {
if (styleAndElement.element[PropertySymbol.nodeType] === NodeTypeEnum.elementNode) {
const rootNode = styleAndElement.element.getRootNode();
if (rootNode.nodeType === NodeTypeEnum.documentNode) {
if (rootNode[PropertySymbol.nodeType] === NodeTypeEnum.documentNode) {
documentElements.unshift(styleAndElement);
} else {
shadowRootElements.unshift(styleAndElement);
}
parentElements.unshift(styleAndElement);
}

if (styleAndElement.element === this.element.ownerDocument) {
if (styleAndElement.element === this.element[PropertySymbol.ownerDocument]) {
const styleSheets = <INodeList<IHTMLStyleElement>>(
this.element.ownerDocument.querySelectorAll('style,link[rel="stylesheet"]')
this.element[PropertySymbol.ownerDocument].querySelectorAll(
'style,link[rel="stylesheet"]'
)
);

for (const styleSheet of styleSheets) {
Expand All @@ -134,17 +137,25 @@ export default class CSSStyleDeclarationElementStyle {
}
}

for (const styleSheet of this.element[PropertySymbol.ownerDocument].adoptedStyleSheets) {
this.parseCSSRules({
elements: documentElements,
cssRules: styleSheet.cssRules
});
}

styleAndElement = { element: null, cssTexts: [] };
} else if (
styleAndElement.element.nodeType === NodeTypeEnum.documentFragmentNode &&
styleAndElement.element[PropertySymbol.nodeType] === NodeTypeEnum.documentFragmentNode &&
(<IShadowRoot>styleAndElement.element).host
) {
const shadowRoot = <IShadowRoot>styleAndElement.element;
const styleSheets = <INodeList<IHTMLStyleElement>>(
(<IShadowRoot>styleAndElement.element).querySelectorAll('style,link[rel="stylesheet"]')
shadowRoot.querySelectorAll('style,link[rel="stylesheet"]')
);

styleAndElement = {
element: <IElement>(<IShadowRoot>styleAndElement.element).host,
element: <IElement>shadowRoot.host,
cssTexts: []
};

Expand All @@ -158,9 +169,21 @@ export default class CSSStyleDeclarationElementStyle {
});
}
}

for (const styleSheet of shadowRoot.adoptedStyleSheets) {
this.parseCSSRules({
elements: shadowRootElements,
cssRules: styleSheet.cssRules,
hostElement: styleAndElement
});
}

shadowRootElements = [];
} else {
styleAndElement = { element: <IElement>styleAndElement.element.parentNode, cssTexts: [] };
styleAndElement = {
element: <IElement>styleAndElement.element[PropertySymbol.parentNode],
cssTexts: []
};
}
}

Expand All @@ -175,36 +198,49 @@ export default class CSSStyleDeclarationElementStyle {
parentElement.cssTexts.sort((a, b) => a.priorityWeight - b.priorityWeight);

let elementCSSText = '';
if (CSSStyleDeclarationElementDefaultCSS[(<IElement>parentElement.element).tagName]) {
if (
CSSStyleDeclarationElementDefaultCSS[
(<IElement>parentElement.element)[PropertySymbol.tagName]
]
) {
if (
typeof CSSStyleDeclarationElementDefaultCSS[(<IElement>parentElement.element).tagName] ===
'string'
typeof CSSStyleDeclarationElementDefaultCSS[
(<IElement>parentElement.element)[PropertySymbol.tagName]
] === 'string'
) {
elementCSSText +=
CSSStyleDeclarationElementDefaultCSS[(<IElement>parentElement.element).tagName];
CSSStyleDeclarationElementDefaultCSS[
(<IElement>parentElement.element)[PropertySymbol.tagName]
];
} else {
for (const key of Object.keys(
CSSStyleDeclarationElementDefaultCSS[(<IElement>parentElement.element).tagName]
CSSStyleDeclarationElementDefaultCSS[
(<IElement>parentElement.element)[PropertySymbol.tagName]
]
)) {
if (key === 'default' || !!parentElement.element[key]) {
elementCSSText +=
CSSStyleDeclarationElementDefaultCSS[(<IElement>parentElement.element).tagName][
key
];
CSSStyleDeclarationElementDefaultCSS[
(<IElement>parentElement.element)[PropertySymbol.tagName]
][key];
}
}
}
elementCSSText +=
CSSStyleDeclarationElementDefaultCSS[(<IElement>parentElement.element).tagName];
CSSStyleDeclarationElementDefaultCSS[
(<IElement>parentElement.element)[PropertySymbol.tagName]
];
}

for (const cssText of parentElement.cssTexts) {
elementCSSText += cssText.cssText;
}

const elementStyleAttribute = (<IElement>parentElement.element).attributes['style'];
const elementStyleAttribute = (<IElement>parentElement.element)[PropertySymbol.attributes][
'style'
];
if (elementStyleAttribute) {
elementCSSText += elementStyleAttribute.value;
elementCSSText += elementStyleAttribute[PropertySymbol.value];
}

CSSStyleDeclarationCSSParser.parse(elementCSSText, (name, value, important) => {
Expand All @@ -229,7 +265,7 @@ export default class CSSStyleDeclarationElementStyle {
parentFontSize,
parentSize: parentFontSize
});
if ((<IElement>parentElement.element).tagName === 'HTML') {
if ((<IElement>parentElement.element)[PropertySymbol.tagName] === 'HTML') {
rootFontSize = parsedValue;
} else if (parentElement !== targetElement) {
parentFontSize = parsedValue;
Expand Down Expand Up @@ -277,7 +313,7 @@ export default class CSSStyleDeclarationElementStyle {
return;
}

const ownerWindow = this.element.ownerDocument[PropertySymbol.defaultView];
const ownerWindow = this.element[PropertySymbol.ownerDocument][PropertySymbol.ownerWindow];

for (const rule of options.cssRules) {
if (rule.type === CSSRuleTypeEnum.styleRule) {
Expand Down Expand Up @@ -308,7 +344,7 @@ export default class CSSStyleDeclarationElementStyle {
new MediaQueryList({
ownerWindow,
media: (<CSSMediaRule>rule).conditionText,
rootFontSize: this.element.tagName === 'HTML' ? 16 : null
rootFontSize: this.element[PropertySymbol.tagName] === 'HTML' ? 16 : null
}).matches
) {
this.parseCSSRules({
Expand Down Expand Up @@ -362,7 +398,7 @@ export default class CSSStyleDeclarationElementStyle {
}): string {
if (
WindowBrowserSettingsReader.getSettings(
this.element.ownerDocument[PropertySymbol.defaultView]
this.element[PropertySymbol.ownerDocument][PropertySymbol.ownerWindow]
).disableComputedStyleRendering
) {
return options.value;
Expand All @@ -375,7 +411,7 @@ export default class CSSStyleDeclarationElementStyle {
while ((match = regexp.exec(options.value)) !== null) {
if (match[1] !== 'px') {
const valueInPixels = CSSMeasurementConverter.toPixels({
ownerWindow: this.element.ownerDocument[PropertySymbol.defaultView],
ownerWindow: this.element[PropertySymbol.ownerDocument][PropertySymbol.ownerWindow],
value: match[0],
rootFontSize: options.rootFontSize,
parentFontSize: options.parentFontSize,
Expand Down
Loading