diff --git a/package-lock.json b/package-lock.json index a57a3c83..678639bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4892,6 +4892,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -11798,7 +11799,6 @@ "version": "0.0.0", "license": "MIT", "dependencies": { - "entities": "^4.5.0", "webidl-conversions": "^7.0.0", "whatwg-mimetype": "^3.0.0" }, diff --git a/packages/happy-dom/package.json b/packages/happy-dom/package.json index f7877cc2..9a817ab1 100644 --- a/packages/happy-dom/package.json +++ b/packages/happy-dom/package.json @@ -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" }, diff --git a/packages/happy-dom/src/dom/DOMTokenList.ts b/packages/happy-dom/src/dom/DOMTokenList.ts index 9d3a1551..12ff8add 100644 --- a/packages/happy-dom/src/dom/DOMTokenList.ts +++ b/packages/happy-dom/src/dom/DOMTokenList.ts @@ -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'; diff --git a/packages/happy-dom/src/html-parser/HTMLParser.ts b/packages/happy-dom/src/html-parser/HTMLParser.ts index 0695dab0..ae5d54e3 100755 --- a/packages/happy-dom/src/html-parser/HTMLParser.ts +++ b/packages/happy-dom/src/html-parser/HTMLParser.ts @@ -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. @@ -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. */ @@ -235,7 +240,6 @@ export default class HTMLParser { // If "nextElement" is set to null, the tag is not allowed (,
and 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; @@ -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 @@ -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); } } @@ -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)) { @@ -452,6 +456,8 @@ export default class HTMLParser { ? MarkupReadStateEnum.plainTextContent : MarkupReadStateEnum.startOrEndTag; } + + this.startTagIndex = this.markupRegExp.lastIndex; } /** @@ -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); @@ -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; @@ -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; @@ -565,7 +576,7 @@ export default class HTMLParser { // Plain text elements such as @@ -367,14 +386,14 @@ describe('HTMLParser', () => { ` ); - expect((