From d81f929492a2731e1c0ef9281b007ac2f75fdd1c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 20 Apr 2021 15:52:49 +0200 Subject: [PATCH] Fix handling of non-Element nodes in DomUtil#insertHtml() The function was rewritten to make use of simple appendChild() / insertBefore() calls in combination with a DocumentFragment vs. inserting all children within the given `html` manually. This fixes the Twitter Embed functionality if user consent is required. --- ts/WoltLabSuite/Core/Dom/Util.ts | 39 +++++++++---------- .../files/js/WoltLabSuite/Core/Dom/Util.js | 35 +++++++++-------- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/ts/WoltLabSuite/Core/Dom/Util.ts b/ts/WoltLabSuite/Core/Dom/Util.ts index ae6c35a0a0..a630f6cb0c 100644 --- a/ts/WoltLabSuite/Core/Dom/Util.ts +++ b/ts/WoltLabSuite/Core/Dom/Util.ts @@ -206,7 +206,14 @@ const DomUtil = { setInnerHtml(element: Element, innerHtml: string): void { element.innerHTML = innerHtml; - const scripts = element.querySelectorAll("script"); + let container: Node & ParentNode; + if (element instanceof HTMLTemplateElement) { + container = element.content; + } else { + container = element; + } + + const scripts = container.querySelectorAll("script"); for (let i = 0, length = scripts.length; i < length; i++) { const script = scripts[i]; const newScript = document.createElement("script"); @@ -216,7 +223,7 @@ const DomUtil = { newScript.textContent = script.textContent; } - element.appendChild(newScript); + container.appendChild(newScript); script.remove(); } }, @@ -228,25 +235,25 @@ const DomUtil = { * @param insertMethod */ insertHtml(html: string, referenceElement: Element, insertMethod: string): void { - const element = document.createElement("div"); + const element = document.createElement("template"); this.setInnerHtml(element, html); - if (!element.childNodes.length) { - return; - } - - let node = element.childNodes[0] as Element; + const fragment = document.importNode(element.content, true); switch (insertMethod) { case "append": - referenceElement.appendChild(node); + referenceElement.appendChild(fragment); break; case "after": - this.insertAfter(node, referenceElement); + if (referenceElement.parentNode === null) { + throw new Error("The reference element has no parent, but the insert position was set to 'after'."); + } + + referenceElement.parentNode.insertBefore(fragment, referenceElement.nextSibling); break; case "prepend": - this.prepend(node, referenceElement); + referenceElement.insertBefore(fragment, referenceElement.firstChild); break; case "before": @@ -254,20 +261,12 @@ const DomUtil = { throw new Error("The reference element has no parent, but the insert position was set to 'before'."); } - referenceElement.parentNode.insertBefore(node, referenceElement); + referenceElement.parentNode.insertBefore(fragment, referenceElement); break; default: throw new Error("Unknown insert method '" + insertMethod + "'."); } - - let tmp; - while (element.childNodes.length) { - tmp = element.childNodes[0]; - - this.insertAfter(tmp, node); - node = tmp; - } }, /** diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Dom/Util.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Dom/Util.js index 7c58937aa8..6f07819176 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Dom/Util.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Dom/Util.js @@ -174,7 +174,14 @@ define(["require", "exports", "tslib", "../StringUtil"], function (require, expo */ setInnerHtml(element, innerHtml) { element.innerHTML = innerHtml; - const scripts = element.querySelectorAll("script"); + let container; + if (element instanceof HTMLTemplateElement) { + container = element.content; + } + else { + container = element; + } + const scripts = container.querySelectorAll("script"); for (let i = 0, length = scripts.length; i < length; i++) { const script = scripts[i]; const newScript = document.createElement("script"); @@ -184,7 +191,7 @@ define(["require", "exports", "tslib", "../StringUtil"], function (require, expo else { newScript.textContent = script.textContent; } - element.appendChild(newScript); + container.appendChild(newScript); script.remove(); } }, @@ -195,37 +202,31 @@ define(["require", "exports", "tslib", "../StringUtil"], function (require, expo * @param insertMethod */ insertHtml(html, referenceElement, insertMethod) { - const element = document.createElement("div"); + const element = document.createElement("template"); this.setInnerHtml(element, html); - if (!element.childNodes.length) { - return; - } - let node = element.childNodes[0]; + const fragment = document.importNode(element.content, true); switch (insertMethod) { case "append": - referenceElement.appendChild(node); + referenceElement.appendChild(fragment); break; case "after": - this.insertAfter(node, referenceElement); + if (referenceElement.parentNode === null) { + throw new Error("The reference element has no parent, but the insert position was set to 'after'."); + } + referenceElement.parentNode.insertBefore(fragment, referenceElement.nextSibling); break; case "prepend": - this.prepend(node, referenceElement); + referenceElement.insertBefore(fragment, referenceElement.firstChild); break; case "before": if (referenceElement.parentNode === null) { throw new Error("The reference element has no parent, but the insert position was set to 'before'."); } - referenceElement.parentNode.insertBefore(node, referenceElement); + referenceElement.parentNode.insertBefore(fragment, referenceElement); break; default: throw new Error("Unknown insert method '" + insertMethod + "'."); } - let tmp; - while (element.childNodes.length) { - tmp = element.childNodes[0]; - this.insertAfter(tmp, node); - node = tmp; - } }, /** * Returns true if `element` contains the `child` element. -- 2.20.1