Skip to content

Commit

Permalink
fix: adopted stylesheets (#2785)
Browse files Browse the repository at this point in the history
  • Loading branch information
niklasvh authored Dec 31, 2021
1 parent 1cc853a commit 74696fa
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 15 deletions.
59 changes: 47 additions & 12 deletions src/dom/document-cloner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import {Bounds} from '../css/layout/bounds';
import {
isBodyElement,
isCanvasElement,
isCustomElement,
isElementNode,
isHTMLElementNode,
isIFrameElement,
isImageElement,
isScriptElement,
isSelectElement,
isSlotElement,
isStyleElement,
isSVGElementNode,
isTextareaElement,
Expand Down Expand Up @@ -63,7 +65,7 @@ export class DocumentCloner {
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> {
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -231,7 +244,20 @@ export class DocumentCloner {
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)) {
return document.createTextNode(node.data);
}
Expand Down Expand Up @@ -260,16 +286,22 @@ export class DocumentCloner {
const counters = this.counters.parse(new CSSParsedCounterDeclaration(this.context, style));
const before = this.resolvePseudoContent(node, clone, styleBefore, PseudoElementType.BEFORE);

for (let child = node.firstChild; child; child = child.nextSibling) {
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));
if (isCustomElement(node)) {
copyStyles = true;
}

for (
let child = node.shadowRoot ? node.shadowRoot.firstChild : node.firstChild;
child;
child = child.nextSibling
) {
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);
}
}

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

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);
}

Expand Down
2 changes: 2 additions & 0 deletions src/dom/node-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 isSelectElement = (node: Element): node is HTMLSelectElement => node.tagName === 'SELECT';
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;
4 changes: 4 additions & 0 deletions tests/reftests/webcomponents/autonomous-custom-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ class AutonomousCustomElement extends HTMLElement {
wrapper.appendChild(img);
wrapper.appendChild(info);
}

connectedCallback() {
this.shadowRoot.adoptedStyleSheets = [sheet];
}
}

customElements.define('autonomous-custom-element', AutonomousCustomElement);
7 changes: 4 additions & 3 deletions tests/reftests/webcomponents/webcomponents.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
<head>
<title>Web components tests</title>
<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>
<style>

</style>
</head>
<body>
<div>
Expand Down

0 comments on commit 74696fa

Please sign in to comment.