Skip to content

Commit

Permalink
chore: [#1615] Continues on implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
capricorn86 committed Dec 13, 2024
1 parent dbe90fa commit 6782f57
Show file tree
Hide file tree
Showing 27 changed files with 936 additions and 454 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion packages/happy-dom/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@
"test:debug": "vitest run --inspect-brk --no-file-parallelism"
},
"dependencies": {
"entities": "^4.5.0",
"webidl-conversions": "^7.0.0",
"whatwg-mimetype": "^3.0.0"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/happy-dom/src/dom/DOMTokenList.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import ClassMethodBinder from '../ClassMethodBinder.js';
import ClassMethodBinder from '../utilities/ClassMethodBinder.js';
import Element from '../nodes/element/Element.js';
import * as PropertySymbol from '../PropertySymbol.js';

Expand Down
37 changes: 24 additions & 13 deletions packages/happy-dom/src/html-parser/HTMLParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import HTMLLinkElement from '../nodes/html-link-element/HTMLLinkElement.js';
import Node from '../nodes/node/Node.js';
import DocumentFragment from '../nodes/document-fragment/DocumentFragment.js';
import HTMLElementConfig from '../config/HTMLElementConfig.js';
import * as Entities from 'entities';
import HTMLElementConfigContentModelEnum from '../config/HTMLElementConfigContentModelEnum.js';
import SVGElementConfig from '../config/SVGElementConfig.js';
import StringUtility from '../StringUtility.js';
import StringUtility from '../utilities/StringUtility.js';
import BrowserWindow from '../window/BrowserWindow.js';
import DocumentType from '../nodes/document-type/DocumentType.js';
import HTMLHeadElement from '../nodes/html-head-element/HTMLHeadElement.js';
import HTMLBodyElement from '../nodes/html-body-element/HTMLBodyElement.js';
import HTMLHtmlElement from '../nodes/html-html-element/HTMLHtmlElement.js';
import XMLEncodeUtility from '../utilities/XMLEncodeUtility.js';

/**
* Markup RegExp.
Expand Down Expand Up @@ -55,6 +55,11 @@ const ATTRIBUTE_REGEXP =
*/
const DOCUMENT_TYPE_ATTRIBUTE_REGEXP = /"([^"]+)"/gm;

/**
* Space RegExp.
*/
const SPACE_REGEXP = /\s+/;

/**
* Space in the beginning of string RegExp.
*/
Expand Down Expand Up @@ -235,7 +240,6 @@ export default class HTMLParser {
// If "nextElement" is set to null, the tag is not allowed (<html>, <head> and <body> are not allowed in an HTML fragment or to be nested).
this.currentNode = this.rootNode;
this.readState = MarkupReadStateEnum.startOrEndTag;
this.startTagIndex = this.markupRegExp.lastIndex;
}
}
break;
Expand Down Expand Up @@ -283,7 +287,7 @@ export default class HTMLParser {
: text;

if (htmlText) {
const textNode = document.createTextNode(Entities.decodeHTML(htmlText));
const textNode = document.createTextNode(XMLEncodeUtility.decodeTextContent(htmlText));
if (
this.currentNode === head &&
level === HTMLDocumentStructureLevelEnum.additionalHeadWithoutBody
Expand All @@ -306,7 +310,7 @@ export default class HTMLParser {
}
}
} else {
const textNode = document.createTextNode(Entities.decodeHTML(text));
const textNode = document.createTextNode(XMLEncodeUtility.decodeTextContent(text));
this.currentNode[PropertySymbol.appendChild](textNode, true);
}
}
Expand Down Expand Up @@ -339,11 +343,11 @@ export default class HTMLParser {
const name =
attributeMatch[1] || attributeMatch[3] || attributeMatch[6] || attributeMatch[9] || '';
const rawValue = attributeMatch[2] || attributeMatch[4] || attributeMatch[7] || '';
const value = rawValue ? Entities.decodeHTMLAttribute(rawValue) : '';
const value = rawValue ? XMLEncodeUtility.decodeAttributeValue(rawValue) : '';
const attributes = this.nextElement[PropertySymbol.attributes];

if (this.nextElement[PropertySymbol.namespaceURI] === NamespaceURI.svg) {
// In the SVG namespaces (when not parsing as XML), the attribute "xmlns" should be set to the "http://www.w3.org/2000/xmlns/" namespace.
// In the SVG namespace, the attribute "xmlns" should be set to the "http://www.w3.org/2000/xmlns/" namespace.
const namespaceURI = name === 'xmlns' ? NamespaceURI.xmlns : null;

if (!attributes.getNamedItemNS(namespaceURI, name)) {
Expand Down Expand Up @@ -452,6 +456,8 @@ export default class HTMLParser {
? MarkupReadStateEnum.plainTextContent
: MarkupReadStateEnum.startOrEndTag;
}

this.startTagIndex = this.markupRegExp.lastIndex;
}

/**
Expand All @@ -460,9 +466,14 @@ export default class HTMLParser {
* @param tagName Tag name.
*/
private parseEndTag(tagName: string): void {
// We close all tags up until the first tag that matches the end tag.
const index = this.tagNameStack.lastIndexOf(StringUtility.asciiUpperCase(tagName));
// SVG elements are case-sensitive.
const name =
this.currentNode[PropertySymbol.namespaceURI] === NamespaceURI.html
? StringUtility.asciiUpperCase(tagName)
: SVGElementConfig[StringUtility.asciiLowerCase(tagName)]?.localName || tagName;
const index = this.tagNameStack.lastIndexOf(name);

// We close all tags up until the first tag that matches the end tag.
if (index !== -1) {
this.nodeStack.splice(index, this.nodeStack.length - index);
this.tagNameStack.splice(index, this.tagNameStack.length - index);
Expand All @@ -477,7 +488,7 @@ export default class HTMLParser {
*/
private parseComment(comment: string): void {
const document = this.window.document;
const commentNode = document.createComment(Entities.decodeHTML(comment));
const commentNode = document.createComment(XMLEncodeUtility.decodeTextContent(comment));

if (this.documentStructure) {
const level = this.documentStructure.level;
Expand Down Expand Up @@ -511,7 +522,7 @@ export default class HTMLParser {
* @param text Text.
*/
private parseDocumentType(text: string): void {
const decodedText = Entities.decodeHTML(text);
const decodedText = XMLEncodeUtility.decodeTextContent(text);

if (this.documentStructure) {
const { doctype } = this.documentStructure.nodes;
Expand Down Expand Up @@ -565,7 +576,7 @@ export default class HTMLParser {

// Plain text elements such as <script> and <style> should only contain text.
this.currentNode[PropertySymbol.appendChild](
document.createTextNode(Entities.decodeHTML(text)),
document.createTextNode(XMLEncodeUtility.decodeTextContent(text)),
true
);

Expand Down Expand Up @@ -663,7 +674,7 @@ export default class HTMLParser {
return null;
}

const docTypeSplit = value.split(' ');
const docTypeSplit = value.split(SPACE_REGEXP);

if (docTypeSplit.length <= 1) {
return null;
Expand Down
17 changes: 10 additions & 7 deletions packages/happy-dom/src/html-serializer/HTMLSerializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import DocumentType from '../nodes/document-type/DocumentType.js';
import HTMLTemplateElement from '../nodes/html-template-element/HTMLTemplateElement.js';
import NodeTypeEnum from '../nodes/node/NodeTypeEnum.js';
import ProcessingInstruction from '../nodes/processing-instruction/ProcessingInstruction.js';
import * as Entities from 'entities';
import DocumentFragment from '../nodes/document-fragment/DocumentFragment.js';
import ShadowRoot from '../nodes/shadow-root/ShadowRoot.js';
import HTMLElementConfig from '../config/HTMLElementConfig.js';
import HTMLElementConfigContentModelEnum from '../config/HTMLElementConfigContentModelEnum.js';
import XMLEncodeUtility from '../utilities/XMLEncodeUtility.js';

/**
* Serializes a node into HTML.
Expand Down Expand Up @@ -63,11 +63,13 @@ export default class HTMLSerializer {
switch (root[PropertySymbol.nodeType]) {
case NodeTypeEnum.elementNode:
const element = <Element>root;
const prefix = element[PropertySymbol.prefix];
const localName = element[PropertySymbol.localName];
const config = HTMLElementConfig[element[PropertySymbol.localName]];
const tagName = prefix ? `${prefix}:${localName}` : localName;

if (config?.contentModel === HTMLElementConfigContentModelEnum.noDescendants) {
return `<${localName}${this.getAttributes(element)}>`;
return `<${tagName}${this.getAttributes(element)}>`;
}

let innerHTML = '';
Expand All @@ -92,15 +94,15 @@ export default class HTMLSerializer {
}

const childNodes =
localName === 'template'
tagName === 'template'
? (<DocumentFragment>(<HTMLTemplateElement>root).content)[PropertySymbol.nodeArray]
: (<DocumentFragment>root)[PropertySymbol.nodeArray];

for (const node of childNodes) {
innerHTML += this.serializeToString(node);
}

return `<${localName}${this.getAttributes(element)}>${innerHTML}</${localName}>`;
return `<${tagName}${this.getAttributes(element)}>${innerHTML}</${tagName}>`;
case Node.DOCUMENT_FRAGMENT_NODE:
case Node.DOCUMENT_NODE:
let html = '';
Expand All @@ -120,7 +122,7 @@ export default class HTMLSerializer {
return root.textContent;
}
}
return Entities.escapeText(root.textContent);
return XMLEncodeUtility.encodeTextContent(root.textContent);
case NodeTypeEnum.documentTypeNode:
const doctype = <DocumentType>root;
const identifier = doctype.publicId ? ' PUBLIC' : doctype.systemId ? ' SYSTEM' : '';
Expand All @@ -144,11 +146,12 @@ export default class HTMLSerializer {
const namedItems = (<Element>element)[PropertySymbol.attributes][PropertySymbol.namedItems];

if (!namedItems.has('is') && element[PropertySymbol.isValue]) {
attributeString += ' is="' + Entities.escapeAttribute(element[PropertySymbol.isValue]) + '"';
attributeString +=
' is="' + XMLEncodeUtility.encodeAttributeValue(element[PropertySymbol.isValue]) + '"';
}

for (const attribute of namedItems.values()) {
const escapedValue = Entities.escapeAttribute(attribute[PropertySymbol.value]);
const escapedValue = XMLEncodeUtility.encodeAttributeValue(attribute[PropertySymbol.value]);
attributeString += ' ' + attribute[PropertySymbol.name] + '="' + escapedValue + '"';
}

Expand Down
2 changes: 1 addition & 1 deletion packages/happy-dom/src/nodes/document/Document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ import HTMLTitleElement from '../html-title-element/HTMLTitleElement.js';
import WindowBrowserContext from '../../window/WindowBrowserContext.js';
import NodeFactory from '../NodeFactory.js';
import SVGElementConfig from '../../config/SVGElementConfig.js';
import StringUtility from '../../StringUtility.js';
import StringUtility from '../../utilities/StringUtility.js';
import HTMLParser from '../../html-parser/HTMLParser.js';

const PROCESSING_INSTRUCTION_TARGET_REGEXP = /^[a-z][a-z0-9-]+$/;
Expand Down
2 changes: 1 addition & 1 deletion packages/happy-dom/src/nodes/element/HTMLCollection.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import ClassMethodBinder from '../../ClassMethodBinder.js';
import ClassMethodBinder from '../../utilities/ClassMethodBinder.js';
import * as PropertySymbol from '../../PropertySymbol.js';
import Element from './Element.js';

Expand Down
2 changes: 1 addition & 1 deletion packages/happy-dom/src/nodes/element/NamedNodeMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import DOMException from '../../exception/DOMException.js';
import DOMExceptionNameEnum from '../../exception/DOMExceptionNameEnum.js';
import Element from './Element.js';
import NamespaceURI from '../../config/NamespaceURI.js';
import StringUtility from '../../StringUtility.js';
import StringUtility from '../../utilities/StringUtility.js';

/**
* Named Node Map.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable filenames/match-exported */

import ClassMethodBinder from '../../ClassMethodBinder.js';
import ClassMethodBinder from '../../utilities/ClassMethodBinder.js';
import * as PropertySymbol from '../../PropertySymbol.js';
import NamedNodeMap from './NamedNodeMap.js';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import THTMLFormControlElement from './THTMLFormControlElement.js';
import QuerySelector from '../../query-selector/QuerySelector.js';
import RadioNodeList from './RadioNodeList.js';
import WindowBrowserContext from '../../window/WindowBrowserContext.js';
import ClassMethodBinder from '../../ClassMethodBinder.js';
import ClassMethodBinder from '../../utilities/ClassMethodBinder.js';
import Node from '../node/Node.js';
import Element from '../element/Element.js';
import EventTarget from '../../event/EventTarget.js';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import TextTrack from './TextTrack.js';
import EventTarget from '../../event/EventTarget.js';
import * as PropertySymbol from '../../PropertySymbol.js';
import ClassMethodBinder from '../../ClassMethodBinder.js';
import ClassMethodBinder from '../../utilities/ClassMethodBinder.js';

/**
* TextTrackList.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import HTMLCollection from '../element/HTMLCollection.js';
import NodeTypeEnum from '../node/NodeTypeEnum.js';
import QuerySelector from '../../query-selector/QuerySelector.js';
import NodeList from '../node/NodeList.js';
import ClassMethodBinder from '../../ClassMethodBinder.js';
import ClassMethodBinder from '../../utilities/ClassMethodBinder.js';
import Node from '../node/Node.js';
import Element from '../element/Element.js';
import EventTarget from '../../event/EventTarget.js';
Expand Down
2 changes: 1 addition & 1 deletion packages/happy-dom/src/nodes/node/NodeList.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import ClassMethodBinder from '../../ClassMethodBinder.js';
import ClassMethodBinder from '../../utilities/ClassMethodBinder.js';
import * as PropertySymbol from '../../PropertySymbol.js';
import Node from './Node.js';

Expand Down
2 changes: 1 addition & 1 deletion packages/happy-dom/src/storage/Storage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import ClassMethodBinder from '../ClassMethodBinder.js';
import ClassMethodBinder from '../utilities/ClassMethodBinder.js';
import * as PropertySymbol from '../PropertySymbol.js';

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/happy-dom/src/svg/SVGLengthList.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import ClassMethodBinder from '../ClassMethodBinder.js';
import ClassMethodBinder from '../utilities/ClassMethodBinder.js';
import DOMExceptionNameEnum from '../exception/DOMExceptionNameEnum.js';
import * as PropertySymbol from '../PropertySymbol.js';
import BrowserWindow from '../window/BrowserWindow.js';
Expand Down
2 changes: 1 addition & 1 deletion packages/happy-dom/src/svg/SVGNumberList.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import ClassMethodBinder from '../ClassMethodBinder.js';
import ClassMethodBinder from '../utilities/ClassMethodBinder.js';
import DOMExceptionNameEnum from '../exception/DOMExceptionNameEnum.js';
import * as PropertySymbol from '../PropertySymbol.js';
import BrowserWindow from '../window/BrowserWindow.js';
Expand Down
2 changes: 1 addition & 1 deletion packages/happy-dom/src/svg/SVGPointList.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import ClassMethodBinder from '../ClassMethodBinder.js';
import ClassMethodBinder from '../utilities/ClassMethodBinder.js';
import DOMExceptionNameEnum from '../exception/DOMExceptionNameEnum.js';
import * as PropertySymbol from '../PropertySymbol.js';
import BrowserWindow from '../window/BrowserWindow.js';
Expand Down
2 changes: 1 addition & 1 deletion packages/happy-dom/src/svg/SVGStringList.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import ClassMethodBinder from '../ClassMethodBinder.js';
import ClassMethodBinder from '../utilities/ClassMethodBinder.js';
import DOMExceptionNameEnum from '../exception/DOMExceptionNameEnum.js';
import * as PropertySymbol from '../PropertySymbol.js';
import BrowserWindow from '../window/BrowserWindow.js';
Expand Down
2 changes: 1 addition & 1 deletion packages/happy-dom/src/svg/SVGTransformList.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import ClassMethodBinder from '../ClassMethodBinder.js';
import ClassMethodBinder from '../utilities/ClassMethodBinder.js';
import DOMExceptionNameEnum from '../exception/DOMExceptionNameEnum.js';
import * as PropertySymbol from '../PropertySymbol.js';
import BrowserWindow from '../window/BrowserWindow.js';
Expand Down
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 6782f57

Please sign in to comment.