Skip to content

Commit

Permalink
Merge pull request #377 from capricorn86/task/374-inner-text-should-n…
Browse files Browse the repository at this point in the history
…ot-include-script-or-style-content

#374@patch: HTMLElement.textContent should not return the content of …
  • Loading branch information
capricorn86 committed Feb 22, 2022
2 parents 084023a + 7fce956 commit bda5a7b
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 23 deletions.
28 changes: 21 additions & 7 deletions packages/happy-dom/src/nodes/html-element/HTMLElement.ts
Expand Up @@ -4,6 +4,7 @@ import CSSStyleDeclaration from '../../css/CSSStyleDeclaration';
import Attr from '../../attribute/Attr';
import FocusEvent from '../../event/events/FocusEvent';
import PointerEvent from '../../event/events/PointerEvent';
import Node from '../node/Node';

/**
* HTML Element.
Expand Down Expand Up @@ -49,21 +50,34 @@ export default class HTMLElement extends Element implements IHTMLElement {
}

/**
* Returns inner text.
* Returns inner text, which is the rendered appearance of text.
*
* @returns Text.
* @returns Inner text.
*/
public get innerText(): string {
return this.textContent;
let result = '';
for (const childNode of this.childNodes) {
if (childNode instanceof HTMLElement) {
if (childNode.tagName !== 'SCRIPT' && childNode.tagName !== 'STYLE') {
result += childNode.innerText;
}
} else if (
childNode.nodeType === Node.ELEMENT_NODE ||
childNode.nodeType === Node.TEXT_NODE
) {
result += childNode.textContent;
}
}
return result;
}

/**
* Sets inner text.
* Sets the inner text, which is the rendered appearance of text.
*
* @param text Text.
* @param innerText Inner text.
*/
public set innerText(text: string) {
this.textContent = text;
public set innerText(innerText: string) {
this.textContent = innerText;
}

/**
Expand Down
12 changes: 7 additions & 5 deletions packages/happy-dom/src/xml-parser/XMLParser.ts
Expand Up @@ -37,7 +37,6 @@ export default class XMLParser {
let parentUnnestableTagName = null;
let lastTextIndex = 0;
let match: RegExpExecArray;
let childLessIndex = 0;

while ((match = markupRegexp.exec(data))) {
const tagName = match[2].toLowerCase();
Expand Down Expand Up @@ -88,15 +87,18 @@ export default class XMLParser {
} else {
parent.appendChild(newElement);
}
lastTextIndex = childLessIndex = markupRegexp.lastIndex;
lastTextIndex = markupRegexp.lastIndex;

// Tags which contain non-parsed content
// For example: <script> JavaScript should not be parsed
if (ChildLessElements.includes(tagName)) {
while (markupRegexp.exec(data)[2].toLowerCase() != tagName) {
childLessIndex = markupRegexp.lastIndex;
let childLessMatch = null;
while ((childLessMatch = markupRegexp.exec(data))) {
if (childLessMatch[2] === match[2] && childLessMatch[1]) {
markupRegexp.lastIndex -= childLessMatch[0].length;
break;
}
}
markupRegexp.lastIndex = childLessIndex;
}
} else {
stack.pop();
Expand Down
13 changes: 8 additions & 5 deletions packages/happy-dom/test/nodes/html-element/HTMLElement.test.ts
Expand Up @@ -83,13 +83,16 @@ describe('HTMLElement', () => {
describe('get innerText()', () => {
it('Returns the as the textContent property.', () => {
const div = document.createElement('div');
const textNode1 = document.createTextNode('text1');
const textNode2 = document.createTextNode('text2');
const script = document.createElement('script');
const style = document.createElement('script');
element.appendChild(div);
element.appendChild(textNode2);
div.appendChild(textNode1);
element.appendChild(script);
element.appendChild(style);
element.appendChild(document.createTextNode('text2'));
div.appendChild(document.createTextNode('text1'));
script.appendChild(document.createTextNode('var key = "value";'));
style.appendChild(document.createTextNode('button { background: red; }'));
expect(element.innerText).toBe('text1text2');
expect(element.innerText).toBe(element.textContent);
});
});

Expand Down
44 changes: 38 additions & 6 deletions packages/happy-dom/test/xml-parser/XMLParser.test.ts
@@ -1,7 +1,8 @@
import XMLParser from '../../src/xml-parser/XMLParser';
import Window from '../../src/window/Window';
import Document from '../../src/nodes/document/Document';
import HTMLElement from '../../src/nodes/html-element/HTMLElement';
import IHTMLElement from '../../src/nodes/html-element/IHTMLElement';
import IHTMLTemplateElement from '../../src/nodes/html-template-element/IHTMLTemplateElement';
import XMLParserHTML from './data/XMLParserHTML';
import NamespaceURI from '../../src/config/NamespaceURI';
import DocumentType from '../../src/nodes/document-type/DocumentType';
Expand All @@ -28,7 +29,7 @@ describe('XMLParser', () => {
const root = XMLParser.parse(window.document, '<div></div>');
expect(root.childNodes.length).toBe(1);
expect(root.childNodes[0].childNodes.length).toBe(0);
expect((<HTMLElement>root.childNodes[0]).tagName).toBe('DIV');
expect((<IHTMLElement>root.childNodes[0]).tagName).toBe('DIV');
});

it('Parses HTML with a single <div> with attributes.', () => {
Expand All @@ -38,10 +39,10 @@ describe('XMLParser', () => {
);
expect(root.childNodes.length).toBe(1);
expect(root.childNodes[0].childNodes.length).toBe(0);
expect((<HTMLElement>root.childNodes[0]).tagName).toBe('DIV');
expect((<HTMLElement>root.childNodes[0]).id).toBe('id');
expect((<HTMLElement>root.childNodes[0]).className).toBe('class1 class2');
expect((<HTMLElement>root.childNodes[0]).attributes).toEqual({
expect((<IHTMLElement>root.childNodes[0]).tagName).toBe('DIV');
expect((<IHTMLElement>root.childNodes[0]).id).toBe('id');
expect((<IHTMLElement>root.childNodes[0]).className).toBe('class1 class2');
expect((<IHTMLElement>root.childNodes[0]).attributes).toEqual({
'0': {
name: 'class',
value: 'class1 class2',
Expand Down Expand Up @@ -154,6 +155,37 @@ describe('XMLParser', () => {
);
});

it('Does not parse the content of script, style and template elements.', () => {
const root = XMLParser.parse(
window.document,
`<div>
<script>if(1<Math['random']()){else if(Math['random']()>1){console.log("1")}</script>
<script><b></b></script>
<style><b></b></style>
<template><b></b></template>
</div>`
);

expect((<IHTMLElement>root.children[0].children[0]).innerText).toBe(
`if(1<Math['random']()){else if(Math['random']()>1){console.log("1")}`
);

expect((<IHTMLElement>root.children[0].children[1]).innerText).toBe('<b></b>');
expect((<IHTMLElement>root.children[0].children[2]).innerText).toBe('<b></b>');
expect((<IHTMLTemplateElement>root.children[0].children[3]).content.textContent).toBe(
'<b></b>'
);

expect(root.innerHTML.replace(/[\s]/gm, '')).toBe(
`<div>
<script>if(1<Math['random']()){else if(Math['random']()>1){console.log("1")}</script>
<script><b></b></script>
<style><b></b></style>
<template></template>
</div>`.replace(/[\s]/gm, '')
);
});

it('Parses an SVG with "xmlns" set to HTML.', () => {
const root = XMLParser.parse(
window.document,
Expand Down

0 comments on commit bda5a7b

Please sign in to comment.