fix: adopted stylesheets (#2785)

This commit is contained in:
Niklas von Hertzen 2022-01-01 01:57:17 +08:00 committed by GitHub
parent 1cc853a318
commit 74696faf47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 57 additions and 15 deletions

View File

@ -2,12 +2,14 @@ import {Bounds} from '../css/layout/bounds';
import { import {
isBodyElement, isBodyElement,
isCanvasElement, isCanvasElement,
isCustomElement,
isElementNode, isElementNode,
isHTMLElementNode, isHTMLElementNode,
isIFrameElement, isIFrameElement,
isImageElement, isImageElement,
isScriptElement, isScriptElement,
isSelectElement, isSelectElement,
isSlotElement,
isStyleElement, isStyleElement,
isSVGElementNode, isSVGElementNode,
isTextareaElement, isTextareaElement,
@ -63,7 +65,7 @@ export class DocumentCloner {
throw new Error('Cloned element does not have an owner document'); throw new Error('Cloned element does not have an owner document');
} }
this.documentElement = this.cloneNode(element.ownerDocument.documentElement) as HTMLElement; this.documentElement = this.cloneNode(element.ownerDocument.documentElement, false) as HTMLElement;
} }
toIFrame(ownerDocument: Document, windowSize: Bounds): Promise<HTMLIFrameElement> { toIFrame(ownerDocument: Document, windowSize: Bounds): Promise<HTMLIFrameElement> {
@ -160,6 +162,17 @@ export class DocumentCloner {
} }
} }
if (isCustomElement(clone)) {
return this.createCustomElementClone(clone);
}
return clone;
}
createCustomElementClone(node: HTMLElement): HTMLElement {
const clone = document.createElement('html2canvascustomelement');
copyCSSStyles(node.style, clone);
return clone; return clone;
} }
@ -231,7 +244,20 @@ export class DocumentCloner {
return clonedCanvas; return clonedCanvas;
} }
cloneNode(node: Node): Node { appendChildNode(clone: HTMLElement | SVGElement, child: Node, copyStyles: boolean): void {
if (
!isElementNode(child) ||
(!isScriptElement(child) &&
!child.hasAttribute(IGNORE_ATTRIBUTE) &&
(typeof this.options.ignoreElements !== 'function' || !this.options.ignoreElements(child)))
) {
if (!this.options.copyStyles || !isElementNode(child) || !isStyleElement(child)) {
clone.appendChild(this.cloneNode(child, copyStyles));
}
}
}
cloneNode(node: Node, copyStyles: boolean): Node {
if (isTextNode(node)) { if (isTextNode(node)) {
return document.createTextNode(node.data); return document.createTextNode(node.data);
} }
@ -260,16 +286,22 @@ export class DocumentCloner {
const counters = this.counters.parse(new CSSParsedCounterDeclaration(this.context, style)); const counters = this.counters.parse(new CSSParsedCounterDeclaration(this.context, style));
const before = this.resolvePseudoContent(node, clone, styleBefore, PseudoElementType.BEFORE); const before = this.resolvePseudoContent(node, clone, styleBefore, PseudoElementType.BEFORE);
for (let child = node.firstChild; child; child = child.nextSibling) { if (isCustomElement(node)) {
if ( copyStyles = true;
!isElementNode(child) || }
(!isScriptElement(child) &&
!child.hasAttribute(IGNORE_ATTRIBUTE) && for (
(typeof this.options.ignoreElements !== 'function' || !this.options.ignoreElements(child))) let child = node.shadowRoot ? node.shadowRoot.firstChild : node.firstChild;
) { child;
if (!this.options.copyStyles || !isElementNode(child) || !isStyleElement(child)) { child = child.nextSibling
clone.appendChild(this.cloneNode(child)); ) {
if (isElementNode(child) && isSlotElement(child) && typeof child.assignedNodes === 'function') {
const assignedNodes = child.assignedNodes() as ChildNode[];
if (assignedNodes.length) {
assignedNodes.forEach((assignedNode) => this.appendChildNode(clone, assignedNode, copyStyles));
} }
} else {
this.appendChildNode(clone, child, copyStyles);
} }
} }
@ -284,7 +316,10 @@ export class DocumentCloner {
this.counters.pop(counters); this.counters.pop(counters);
if (style && (this.options.copyStyles || isSVGElementNode(node)) && !isIFrameElement(node)) { if (
(style && (this.options.copyStyles || isSVGElementNode(node)) && !isIFrameElement(node)) ||
copyStyles
) {
copyCSSStyles(style, clone); copyCSSStyles(style, clone);
} }

View File

@ -131,3 +131,5 @@ export const isScriptElement = (node: Element): node is HTMLScriptElement => nod
export const isTextareaElement = (node: Element): node is HTMLTextAreaElement => node.tagName === 'TEXTAREA'; export const isTextareaElement = (node: Element): node is HTMLTextAreaElement => node.tagName === 'TEXTAREA';
export const isSelectElement = (node: Element): node is HTMLSelectElement => node.tagName === 'SELECT'; export const isSelectElement = (node: Element): node is HTMLSelectElement => node.tagName === 'SELECT';
export const isSlotElement = (node: Element): node is HTMLSlotElement => node.tagName === 'SLOT'; export const isSlotElement = (node: Element): node is HTMLSlotElement => node.tagName === 'SLOT';
// https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
export const isCustomElement = (node: Element): node is HTMLElement => node.tagName.indexOf('-') > 0;

View File

@ -40,6 +40,10 @@ class AutonomousCustomElement extends HTMLElement {
wrapper.appendChild(img); wrapper.appendChild(img);
wrapper.appendChild(info); wrapper.appendChild(info);
} }
connectedCallback() {
this.shadowRoot.adoptedStyleSheets = [sheet];
}
} }
customElements.define('autonomous-custom-element', AutonomousCustomElement); customElements.define('autonomous-custom-element', AutonomousCustomElement);

View File

@ -3,10 +3,11 @@
<head> <head>
<title>Web components tests</title> <title>Web components tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script>
const sheet = new CSSStyleSheet();
sheet.replaceSync('* { color: red !important; }')
</script>
<script type="text/javascript" src="../../test.js"></script> <script type="text/javascript" src="../../test.js"></script>
<style>
</style>
</head> </head>
<body> <body>
<div> <div>