Skip to content

Commit 74696fa

Browse files
authored
fix: adopted stylesheets (#2785)
1 parent 1cc853a commit 74696fa

File tree

4 files changed

+57
-15
lines changed

4 files changed

+57
-15
lines changed

src/dom/document-cloner.ts

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ import {Bounds} from '../css/layout/bounds';
22
import {
33
isBodyElement,
44
isCanvasElement,
5+
isCustomElement,
56
isElementNode,
67
isHTMLElementNode,
78
isIFrameElement,
89
isImageElement,
910
isScriptElement,
1011
isSelectElement,
12+
isSlotElement,
1113
isStyleElement,
1214
isSVGElementNode,
1315
isTextareaElement,
@@ -63,7 +65,7 @@ export class DocumentCloner {
6365
throw new Error('Cloned element does not have an owner document');
6466
}
6567

66-
this.documentElement = this.cloneNode(element.ownerDocument.documentElement) as HTMLElement;
68+
this.documentElement = this.cloneNode(element.ownerDocument.documentElement, false) as HTMLElement;
6769
}
6870

6971
toIFrame(ownerDocument: Document, windowSize: Bounds): Promise<HTMLIFrameElement> {
@@ -160,6 +162,17 @@ export class DocumentCloner {
160162
}
161163
}
162164

165+
if (isCustomElement(clone)) {
166+
return this.createCustomElementClone(clone);
167+
}
168+
169+
return clone;
170+
}
171+
172+
createCustomElementClone(node: HTMLElement): HTMLElement {
173+
const clone = document.createElement('html2canvascustomelement');
174+
copyCSSStyles(node.style, clone);
175+
163176
return clone;
164177
}
165178

@@ -231,7 +244,20 @@ export class DocumentCloner {
231244
return clonedCanvas;
232245
}
233246

234-
cloneNode(node: Node): Node {
247+
appendChildNode(clone: HTMLElement | SVGElement, child: Node, copyStyles: boolean): void {
248+
if (
249+
!isElementNode(child) ||
250+
(!isScriptElement(child) &&
251+
!child.hasAttribute(IGNORE_ATTRIBUTE) &&
252+
(typeof this.options.ignoreElements !== 'function' || !this.options.ignoreElements(child)))
253+
) {
254+
if (!this.options.copyStyles || !isElementNode(child) || !isStyleElement(child)) {
255+
clone.appendChild(this.cloneNode(child, copyStyles));
256+
}
257+
}
258+
}
259+
260+
cloneNode(node: Node, copyStyles: boolean): Node {
235261
if (isTextNode(node)) {
236262
return document.createTextNode(node.data);
237263
}
@@ -260,16 +286,22 @@ export class DocumentCloner {
260286
const counters = this.counters.parse(new CSSParsedCounterDeclaration(this.context, style));
261287
const before = this.resolvePseudoContent(node, clone, styleBefore, PseudoElementType.BEFORE);
262288

263-
for (let child = node.firstChild; child; child = child.nextSibling) {
264-
if (
265-
!isElementNode(child) ||
266-
(!isScriptElement(child) &&
267-
!child.hasAttribute(IGNORE_ATTRIBUTE) &&
268-
(typeof this.options.ignoreElements !== 'function' || !this.options.ignoreElements(child)))
269-
) {
270-
if (!this.options.copyStyles || !isElementNode(child) || !isStyleElement(child)) {
271-
clone.appendChild(this.cloneNode(child));
289+
if (isCustomElement(node)) {
290+
copyStyles = true;
291+
}
292+
293+
for (
294+
let child = node.shadowRoot ? node.shadowRoot.firstChild : node.firstChild;
295+
child;
296+
child = child.nextSibling
297+
) {
298+
if (isElementNode(child) && isSlotElement(child) && typeof child.assignedNodes === 'function') {
299+
const assignedNodes = child.assignedNodes() as ChildNode[];
300+
if (assignedNodes.length) {
301+
assignedNodes.forEach((assignedNode) => this.appendChildNode(clone, assignedNode, copyStyles));
272302
}
303+
} else {
304+
this.appendChildNode(clone, child, copyStyles);
273305
}
274306
}
275307

@@ -284,7 +316,10 @@ export class DocumentCloner {
284316

285317
this.counters.pop(counters);
286318

287-
if (style && (this.options.copyStyles || isSVGElementNode(node)) && !isIFrameElement(node)) {
319+
if (
320+
(style && (this.options.copyStyles || isSVGElementNode(node)) && !isIFrameElement(node)) ||
321+
copyStyles
322+
) {
288323
copyCSSStyles(style, clone);
289324
}
290325

src/dom/node-parser.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,5 @@ export const isScriptElement = (node: Element): node is HTMLScriptElement => nod
131131
export const isTextareaElement = (node: Element): node is HTMLTextAreaElement => node.tagName === 'TEXTAREA';
132132
export const isSelectElement = (node: Element): node is HTMLSelectElement => node.tagName === 'SELECT';
133133
export const isSlotElement = (node: Element): node is HTMLSlotElement => node.tagName === 'SLOT';
134+
// https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
135+
export const isCustomElement = (node: Element): node is HTMLElement => node.tagName.indexOf('-') > 0;

tests/reftests/webcomponents/autonomous-custom-element.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ class AutonomousCustomElement extends HTMLElement {
4040
wrapper.appendChild(img);
4141
wrapper.appendChild(info);
4242
}
43+
44+
connectedCallback() {
45+
this.shadowRoot.adoptedStyleSheets = [sheet];
46+
}
4347
}
4448

4549
customElements.define('autonomous-custom-element', AutonomousCustomElement);

tests/reftests/webcomponents/webcomponents.html

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
<head>
44
<title>Web components tests</title>
55
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
6+
<script>
7+
const sheet = new CSSStyleSheet();
8+
sheet.replaceSync('* { color: red !important; }')
9+
</script>
610
<script type="text/javascript" src="../../test.js"></script>
7-
<style>
8-
9-
</style>
1011
</head>
1112
<body>
1213
<div>

0 commit comments

Comments
 (0)