Skip to content

Commit

Permalink
Merge pull request #1393 from capricorn86/1392-unable-to-correctly-cl…
Browse files Browse the repository at this point in the history
…onenode-through-nodeprototype

fix: [#1392] Adds support for using Node.prototype.cloneNode.call(ele…
  • Loading branch information
capricorn86 committed Apr 7, 2024
2 parents ff09e4f + 68ef1e5 commit e8f6107
Show file tree
Hide file tree
Showing 25 changed files with 247 additions and 169 deletions.
5 changes: 5 additions & 0 deletions packages/happy-dom/src/PropertySymbol.ts
Expand Up @@ -159,3 +159,8 @@ export const screen = Symbol('screen');
export const sessionStorage = Symbol('sessionStorage');
export const localStorage = Symbol('localStorage');
export const sandbox = Symbol('sandbox');
export const cloneNode = Symbol('cloneNode');
export const appendChild = Symbol('appendChild');
export const removeChild = Symbol('removeChild');
export const insertBefore = Symbol('insertBefore');
export const replaceChild = Symbol('replaceChild');
9 changes: 3 additions & 6 deletions packages/happy-dom/src/nodes/character-data/CharacterData.ts
Expand Up @@ -20,6 +20,7 @@ export default abstract class CharacterData
implements IChildNode, INonDocumentTypeChildNode
{
public [PropertySymbol.data] = '';
public cloneNode: (deep?: boolean) => CharacterData;

/**
* Constructor.
Expand Down Expand Up @@ -220,14 +221,10 @@ export default abstract class CharacterData
}

/**
* Clones a node.
*
* @override
* @param [deep=false] "true" to clone deep.
* @returns Cloned node.
*/
public cloneNode(deep = false): CharacterData {
const clone = <CharacterData>super.cloneNode(deep);
public override [PropertySymbol.cloneNode](deep = false): CharacterData {
const clone = <CharacterData>super[PropertySymbol.cloneNode](deep);
clone[PropertySymbol.data] = this[PropertySymbol.data];
return clone;
}
Expand Down
9 changes: 3 additions & 6 deletions packages/happy-dom/src/nodes/comment/Comment.ts
Expand Up @@ -7,6 +7,7 @@ import NodeTypeEnum from '../node/NodeTypeEnum.js';
*/
export default class Comment extends CharacterData {
public [PropertySymbol.nodeType] = NodeTypeEnum.commentNode;
public cloneNode: (deep?: boolean) => Comment;

/**
* Node name.
Expand All @@ -27,13 +28,9 @@ export default class Comment extends CharacterData {
}

/**
* Clones a node.
*
* @override
* @param [deep=false] "true" to clone deep.
* @returns Cloned node.
*/
public cloneNode(deep = false): Comment {
return <Comment>super.cloneNode(deep);
public override [PropertySymbol.cloneNode](deep = false): Comment {
return <Comment>super[PropertySymbol.cloneNode](deep);
}
}
21 changes: 6 additions & 15 deletions packages/happy-dom/src/nodes/document-fragment/DocumentFragment.ts
Expand Up @@ -17,6 +17,7 @@ export default class DocumentFragment extends Node {
public readonly [PropertySymbol.children]: HTMLCollection<Element> = new HTMLCollection();
public [PropertySymbol.rootNode]: Node = this;
public [PropertySymbol.nodeType] = NodeTypeEnum.documentFragmentNode;
public cloneNode: (deep?: boolean) => DocumentFragment;

/**
* Returns the document fragment children.
Expand Down Expand Up @@ -198,14 +199,10 @@ export default class DocumentFragment extends Node {
}

/**
* Clones a node.
*
* @override
* @param [deep=false] "true" to clone deep.
* @returns Cloned node.
*/
public cloneNode(deep = false): DocumentFragment {
const clone = <DocumentFragment>super.cloneNode(deep);
public override [PropertySymbol.cloneNode](deep = false): DocumentFragment {
const clone = <DocumentFragment>super[PropertySymbol.cloneNode](deep);

if (deep) {
for (const node of clone[PropertySymbol.childNodes]) {
Expand All @@ -221,29 +218,23 @@ export default class DocumentFragment extends Node {
/**
* @override
*/
public override appendChild(node: Node): Node {
public override [PropertySymbol.appendChild](node: Node): Node {
// We do not call super here as this will be handled by ElementUtility to improve performance by avoiding validation and other checks.
return ElementUtility.appendChild(this, node);
}

/**
* @override
*/
public override removeChild(node: Node): Node {
public override [PropertySymbol.removeChild](node: Node): Node {
// We do not call super here as this will be handled by ElementUtility to improve performance by avoiding validation and other checks.
return ElementUtility.removeChild(this, node);
}

/**
* @override
*/
public override insertBefore(newNode: Node, referenceNode: Node | null): Node {
if (arguments.length < 2) {
throw new TypeError(
`Failed to execute 'insertBefore' on 'Node': 2 arguments required, but only ${arguments.length} present.`
);
}

public override [PropertySymbol.insertBefore](newNode: Node, referenceNode: Node | null): Node {
// We do not call super here as this will be handled by ElementUtility to improve performance by avoiding validation and other checks.
return ElementUtility.insertBefore(this, newNode, referenceNode);
}
Expand Down
9 changes: 3 additions & 6 deletions packages/happy-dom/src/nodes/document-type/DocumentType.ts
Expand Up @@ -10,6 +10,7 @@ export default class DocumentType extends Node {
public [PropertySymbol.name] = '';
public [PropertySymbol.publicId] = '';
public [PropertySymbol.systemId] = '';
public cloneNode: (deep?: boolean) => DocumentType;

/**
* Returns name.
Expand Down Expand Up @@ -57,14 +58,10 @@ export default class DocumentType extends Node {
}

/**
* Clones a node.
*
* @override
* @param [deep=false] "true" to clone deep.
* @returns Cloned node.
*/
public cloneNode(deep = false): DocumentType {
const clone = <DocumentType>super.cloneNode(deep);
public override [PropertySymbol.cloneNode](deep = false): DocumentType {
const clone = <DocumentType>super[PropertySymbol.cloneNode](deep);
clone[PropertySymbol.name] = this[PropertySymbol.name];
clone[PropertySymbol.publicId] = this[PropertySymbol.publicId];
clone[PropertySymbol.systemId] = this[PropertySymbol.systemId];
Expand Down
21 changes: 6 additions & 15 deletions packages/happy-dom/src/nodes/document/Document.ts
Expand Up @@ -69,6 +69,7 @@ export default class Document extends Node {
public [PropertySymbol.referrer] = '';
public [PropertySymbol.defaultView]: BrowserWindow | null = null;
public [PropertySymbol.ownerWindow]: BrowserWindow;
public cloneNode: (deep?: boolean) => Document;

// Private properties
#selection: Selection = null;
Expand Down Expand Up @@ -800,14 +801,10 @@ export default class Document extends Node {
}

/**
* Clones a node.
*
* @override
* @param [deep=false] "true" to clone deep.
* @returns Cloned node.
*/
public cloneNode(deep = false): Document {
const clone = <Document>super.cloneNode(deep);
public override [PropertySymbol.cloneNode](deep = false): Document {
const clone = <Document>super[PropertySymbol.cloneNode](deep);

if (deep) {
for (const node of clone[PropertySymbol.childNodes]) {
Expand All @@ -823,29 +820,23 @@ export default class Document extends Node {
/**
* @override
*/
public override appendChild(node: Node): Node {
public override [PropertySymbol.appendChild](node: Node): Node {
// We do not call super here as this will be handled by ElementUtility to improve performance by avoiding validation and other checks.
return ElementUtility.appendChild(this, node);
}

/**
* @override
*/
public override removeChild(node: Node): Node {
public override [PropertySymbol.removeChild](node: Node): Node {
// We do not call super here as this will be handled by ElementUtility to improve performance by avoiding validation and other checks.
return ElementUtility.removeChild(this, node);
}

/**
* @override
*/
public override insertBefore(newNode: Node, referenceNode: Node | null): Node {
if (arguments.length < 2) {
throw new TypeError(
`Failed to execute 'insertBefore' on 'Node': 2 arguments required, but only ${arguments.length} present.`
);
}

public override [PropertySymbol.insertBefore](newNode: Node, referenceNode: Node | null): Node {
// We do not call super here as this will be handled by ElementUtility to improve performance by avoiding validation and other checks.
return ElementUtility.insertBefore(this, newNode, referenceNode);
}
Expand Down
21 changes: 6 additions & 15 deletions packages/happy-dom/src/nodes/element/Element.ts
Expand Up @@ -49,6 +49,7 @@ export default class Element
public static [PropertySymbol.localName]: string | null = null;
public static [PropertySymbol.namespaceURI]: string | null = null;
public static observedAttributes: string[];
public cloneNode: (deep?: boolean) => Element;

// Events
public oncancel: (event: Event) => void | null = null;
Expand Down Expand Up @@ -464,14 +465,10 @@ export default class Element
}

/**
* Clones a node.
*
* @override
* @param [deep=false] "true" to clone deep.
* @returns Cloned node.
*/
public cloneNode(deep = false): Element {
const clone = <Element>super.cloneNode(deep);
public override [PropertySymbol.cloneNode](deep = false): Element {
const clone = <Element>super[PropertySymbol.cloneNode](deep);

clone[PropertySymbol.tagName] = this[PropertySymbol.tagName];
clone[PropertySymbol.localName] = this[PropertySymbol.localName];
Expand Down Expand Up @@ -504,29 +501,23 @@ export default class Element
/**
* @override
*/
public override appendChild(node: Node): Node {
public override [PropertySymbol.appendChild](node: Node): Node {
// We do not call super here as this will be handled by ElementUtility to improve performance by avoiding validation and other checks.
return ElementUtility.appendChild(this, node);
}

/**
* @override
*/
public override removeChild(node: Node): Node {
public override [PropertySymbol.removeChild](node: Node): Node {
// We do not call super here as this will be handled by ElementUtility to improve performance by avoiding validation and other checks.
return ElementUtility.removeChild(this, node);
}

/**
* @override
*/
public override insertBefore(newNode: Node, referenceNode: Node | null): Node {
if (arguments.length < 2) {
throw new TypeError(
`Failed to execute 'insertBefore' on 'Node': 2 arguments required, but only ${arguments.length} present.`
);
}

public override [PropertySymbol.insertBefore](newNode: Node, referenceNode: Node | null): Node {
// We do not call super here as this will be handled by ElementUtility to improve performance by avoiding validation and other checks.
return ElementUtility.insertBefore(this, newNode, referenceNode);
}
Expand Down
Expand Up @@ -8,6 +8,7 @@ import * as PropertySymbol from '../../PropertySymbol.js';
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base.
*/
export default class HTMLBaseElement extends HTMLElement {
public cloneNode: (deep?: boolean) => HTMLBaseElement;
/**
* Returns href.
*
Expand Down Expand Up @@ -49,13 +50,9 @@ export default class HTMLBaseElement extends HTMLElement {
}

/**
* Clones a node.
*
* @override
* @param [deep=false] "true" to clone deep.
* @returns Cloned node.
*/
public cloneNode(deep = false): HTMLBaseElement {
return <HTMLBaseElement>super.cloneNode(deep);
public override [PropertySymbol.cloneNode](deep = false): HTMLBaseElement {
return <HTMLBaseElement>super[PropertySymbol.cloneNode](deep);
}
}
7 changes: 5 additions & 2 deletions packages/happy-dom/src/nodes/html-element/HTMLElement.ts
Expand Up @@ -21,6 +21,9 @@ import IDataset from '../element/IDataset.js';
* https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement.
*/
export default class HTMLElement extends Element {
// Public properties
public cloneNode: (deep?: boolean) => HTMLElement;

// Events
public oncopy: (event: Event) => void | null = null;
public oncut: (event: Event) => void | null = null;
Expand Down Expand Up @@ -486,8 +489,8 @@ export default class HTMLElement extends Element {
/**
* @override
*/
public cloneNode(deep = false): HTMLElement {
const clone = <HTMLElement>super.cloneNode(deep);
public override [PropertySymbol.cloneNode](deep = false): HTMLElement {
const clone = <HTMLElement>super[PropertySymbol.cloneNode](deep);

clone[PropertySymbol.accessKey] = this[PropertySymbol.accessKey];
clone[PropertySymbol.contentEditable] = this[PropertySymbol.contentEditable];
Expand Down
Expand Up @@ -21,6 +21,9 @@ import BrowserWindow from '../../window/BrowserWindow.js';
* https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement.
*/
export default class HTMLFormElement extends HTMLElement {
// Public properties
public cloneNode: (deep?: boolean) => HTMLFormElement;

// Internal properties.
public [PropertySymbol.elements]: HTMLFormControlsCollection = new HTMLFormControlsCollection();
public [PropertySymbol.length] = 0;
Expand Down Expand Up @@ -325,14 +328,10 @@ export default class HTMLFormElement extends HTMLElement {
}

/**
* Clones a node.
*
* @override
* @param [deep=false] "true" to clone deep.
* @returns Cloned node.
*/
public cloneNode(deep = false): HTMLFormElement {
return <HTMLFormElement>super.cloneNode(deep);
public override [PropertySymbol.cloneNode](deep = false): HTMLFormElement {
return <HTMLFormElement>super[PropertySymbol.cloneNode](deep);
}

/**
Expand Down
Expand Up @@ -18,13 +18,17 @@ import DOMTokenList from '../../dom-token-list/DOMTokenList.js';
* https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement.
*/
export default class HTMLIFrameElement extends HTMLElement {
// Public properties
public cloneNode: (deep?: boolean) => HTMLIFrameElement;

// Events
public onload: (event: Event) => void | null = null;
public onerror: (event: Event) => void | null = null;

// Internal properties
public override [PropertySymbol.attributes]: NamedNodeMap;
public [PropertySymbol.sandbox]: DOMTokenList = null;

// Private properties
#contentWindowContainer: { window: BrowserWindow | CrossOriginBrowserWindow | null } = {
window: null
Expand Down Expand Up @@ -226,13 +230,9 @@ export default class HTMLIFrameElement extends HTMLElement {
}

/**
* Clones a node.
*
* @override
* @param [deep=false] "true" to clone deep.
* @returns Cloned node.
*/
public cloneNode(deep = false): HTMLIFrameElement {
return <HTMLIFrameElement>super.cloneNode(deep);
public override [PropertySymbol.cloneNode](deep = false): HTMLIFrameElement {
return <HTMLIFrameElement>super[PropertySymbol.cloneNode](deep);
}
}
Expand Up @@ -15,6 +15,7 @@ export default class HTMLImageElement extends HTMLElement {
public [PropertySymbol.loading] = 'auto';
public [PropertySymbol.x] = 0;
public [PropertySymbol.y] = 0;
public cloneNode: (deep?: boolean) => HTMLImageElement;

/**
* Returns complete.
Expand Down Expand Up @@ -310,13 +311,9 @@ export default class HTMLImageElement extends HTMLElement {
}

/**
* Clones a node.
*
* @override
* @param [deep=false] "true" to clone deep.
* @returns Cloned node.
*/
public cloneNode(deep = false): HTMLImageElement {
return <HTMLImageElement>super.cloneNode(deep);
public override [PropertySymbol.cloneNode](deep = false): HTMLImageElement {
return <HTMLImageElement>super[PropertySymbol.cloneNode](deep);
}
}

0 comments on commit e8f6107

Please sign in to comment.