Skip to content

Commit

Permalink
Merge pull request #1006 from capricorn86/task/728-incorrect-implemen…
Browse files Browse the repository at this point in the history
…tation-of-namednodemapelementattributes

#728@patch: Improves support for NamedNodeMap, which is used as the E…
  • Loading branch information
capricorn86 committed Aug 2, 2023
2 parents e9038c7 + f9c9ee9 commit c89516a
Show file tree
Hide file tree
Showing 38 changed files with 1,053 additions and 722 deletions.
1 change: 0 additions & 1 deletion packages/happy-dom/bin/change-file-extension.cjs
Expand Up @@ -64,7 +64,6 @@ async function renameFiles(files, args) {
for (const file of newFiles) {
writePromises.push(
FS.promises.readFile(file.oldPath).then((content) => {
debugger;
return FS.promises
.writeFile(
file.newPath,
Expand Down
@@ -1,10 +1,11 @@
import IElement from '../../nodes/element/IElement.js';
import Attr from '../../nodes/attr/Attr.js';
import IAttr from '../../nodes/attr/IAttr.js';
import CSSRule from '../CSSRule.js';
import DOMExceptionNameEnum from '../../exception/DOMExceptionNameEnum.js';
import DOMException from '../../exception/DOMException.js';
import CSSStyleDeclarationElementStyle from './element-style/CSSStyleDeclarationElementStyle.js';
import CSSStyleDeclarationPropertyManager from './property-manager/CSSStyleDeclarationPropertyManager.js';
import NamedNodeMap from '../../named-node-map/NamedNodeMap.js';

/**
* CSS Style Declaration.
Expand Down Expand Up @@ -77,17 +78,21 @@ export default abstract class AbstractCSSStyleDeclaration {

if (this._ownerElement) {
const style = new CSSStyleDeclarationPropertyManager({ cssText });
if (!this._ownerElement['_attributes']['style']) {
Attr._ownerDocument = this._ownerElement.ownerDocument;
this._ownerElement['_attributes']['style'] = new Attr();
this._ownerElement['_attributes']['style'].name = 'style';
let styleAttribute = <IAttr>this._ownerElement.attributes['style'];

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

if (this._ownerElement.isConnected) {
this._ownerElement.ownerDocument['_cacheID']++;
}

this._ownerElement['_attributes']['style'].value = style.toString();
styleAttribute.value = style.toString();
} else {
this._style = new CSSStyleDeclarationPropertyManager({ cssText });
}
Expand Down Expand Up @@ -130,20 +135,25 @@ export default abstract class AbstractCSSStyleDeclaration {
if (!stringValue) {
this.removeProperty(name);
} else if (this._ownerElement) {
if (!this._ownerElement['_attributes']['style']) {
Attr._ownerDocument = this._ownerElement.ownerDocument;
this._ownerElement['_attributes']['style'] = new Attr();
this._ownerElement['_attributes']['style'].name = 'style';
}
let styleAttribute = <IAttr>this._ownerElement.attributes['style'];

const style = this._elementStyle.getElementStyle();
style.set(name, stringValue, !!priority);
if (!styleAttribute) {
styleAttribute = this._ownerElement.ownerDocument.createAttribute('style');

// We use "_setNamedItemWithoutConsequences" here to avoid triggering setting "Element.style.cssText" when setting the "style" attribute.
(<NamedNodeMap>this._ownerElement.attributes)._setNamedItemWithoutConsequences(
styleAttribute
);
}

if (this._ownerElement.isConnected) {
this._ownerElement.ownerDocument['_cacheID']++;
}

this._ownerElement['_attributes']['style'].value = style.toString();
const style = this._elementStyle.getElementStyle();
style.set(name, stringValue, !!priority);

styleAttribute.value = style.toString();
} else {
this._style.set(name, stringValue, !!priority);
}
Expand All @@ -168,14 +178,16 @@ export default abstract class AbstractCSSStyleDeclaration {
const style = this._elementStyle.getElementStyle();
style.remove(name);
const newCSSText = style.toString();
if (newCSSText) {
if (this._ownerElement.isConnected) {
this._ownerElement.ownerDocument['_cacheID']++;
}

this._ownerElement['_attributes']['style'].value = newCSSText;
if (this._ownerElement.isConnected) {
this._ownerElement.ownerDocument['_cacheID']++;
}

if (newCSSText) {
(<IAttr>this._ownerElement.attributes['style']).value = newCSSText;
} else {
delete this._ownerElement['_attributes']['style'];
// We use "_removeNamedItemWithoutConsequences" here to avoid triggering setting "Element.style.cssText" when setting the "style" attribute.
(<NamedNodeMap>this._ownerElement.attributes)._removeNamedItemWithoutConsequences('style');
}
} else {
this._style.remove(name);
Expand Down
Expand Up @@ -63,7 +63,7 @@ export default class CSSStyleDeclarationElementStyle {
return this.getComputedElementStyle();
}

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

if (cssText) {
if (this.cache.propertyManager && this.cache.cssText === cssText) {
Expand Down Expand Up @@ -182,8 +182,9 @@ export default class CSSStyleDeclarationElementStyle {
elementCSSText += cssText.cssText;
}

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

CSSStyleDeclarationCSSParser.parse(elementCSSText, (name, value, important) => {
Expand Down
52 changes: 26 additions & 26 deletions packages/happy-dom/src/named-node-map/INamedNodeMap.ts
Expand Up @@ -6,65 +6,65 @@ import IAttr from '../nodes/attr/IAttr.js';
* Reference:
* https://developer.mozilla.org/en-US/docs/Web/API/NamedNodeMap.
*/
export default interface INamedNodeMap extends Iterable<IAttr> {
export default interface INamedNodeMap {
[index: number]: IAttr;
[Symbol.toStringTag]: string;
readonly length: number;

/**
* Returns attribute by index.
* Returns item by index.
*
* @param index Index.
*/
item: (index: number) => IAttr;
item: (index: number) => IAttr | null;

/**
* Returns attribute by name.
* Returns named item.
*
* @param qualifiedName Name.
* @returns Attribute.
* @param name Name.
* @returns Itme.
*/
getNamedItem: (qualifiedName: string) => IAttr;
getNamedItem(name: string): IAttr | null;

/**
* Returns attribute by name and namespace.
* Returns item by name and namespace.
*
* @param namespace Namespace.
* @param localName Local name of the attribute.
* @returns Attribute.
* @returns Item.
*/
getNamedItemNS: (namespace: string, localName: string) => IAttr;
getNamedItemNS(namespace: string, localName: string): IAttr | null;

/**
* Adds a new attribute node.
* Sets named item.
*
* @param attr Attribute.
* @returns Replaced attribute.
* @param item Item.
* @returns Replaced item.
*/
setNamedItem: (attr: IAttr) => IAttr;
setNamedItem(item: IAttr): IAttr | null;

/**
* Adds a new namespaced attribute node.
* Adds a new namespaced item.
*
* @param attr Attribute.
* @returns Replaced attribute.
* @param item Item.
* @returns Replaced item.
*/
setNamedItemNS: (attr: IAttr) => IAttr;
setNamedItemNS(item: IAttr): IAttr | null;

/**
* Removes an attribute.
* Removes an item.
*
* @param qualifiedName Name of the attribute.
* @returns Removed attribute.
* @param name Name of item.
* @returns Removed item.
*/
removeNamedItem: (qualifiedName: string) => IAttr;
removeNamedItem(name: string): IAttr | null;

/**
* Removes a namespaced attribute.
* Removes a namespaced item.
*
* @param namespace Namespace.
* @param localName Local name of the attribute.
* @returns Removed attribute.
* @param localName Local name of the item.
* @returns Removed item.
*/
removeNamedItemNS: (namespace: string, localName: string) => IAttr;
removeNamedItemNS(namespace: string, localName: string): IAttr | null;
}

0 comments on commit c89516a

Please sign in to comment.