mirror of
https://github.com/niklasvh/html2canvas.git
synced 2023-08-10 21:13:10 +03:00
feat: add support for <video> elements (#2788)
This commit is contained in:
parent
46db86755f
commit
181d1b1103
@ -13,7 +13,8 @@ import {
|
||||
isStyleElement,
|
||||
isSVGElementNode,
|
||||
isTextareaElement,
|
||||
isTextNode
|
||||
isTextNode,
|
||||
isVideoElement
|
||||
} from './node-parser';
|
||||
import {isIdentToken, nonFunctionArgSeparator} from '../css/syntax/parser';
|
||||
import {TokenType} from '../css/syntax/tokenizer';
|
||||
@ -145,7 +146,9 @@ export class DocumentCloner {
|
||||
if (isCanvasElement(node)) {
|
||||
return this.createCanvasClone(node);
|
||||
}
|
||||
|
||||
if (isVideoElement(node)) {
|
||||
return this.createVideoClone(node);
|
||||
}
|
||||
if (isStyleElement(node)) {
|
||||
return this.createStyleClone(node);
|
||||
}
|
||||
@ -244,6 +247,32 @@ export class DocumentCloner {
|
||||
return clonedCanvas;
|
||||
}
|
||||
|
||||
createVideoClone(video: HTMLVideoElement): HTMLCanvasElement {
|
||||
const canvas = video.ownerDocument.createElement('canvas');
|
||||
|
||||
canvas.width = video.offsetWidth;
|
||||
canvas.height = video.offsetHeight;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
try {
|
||||
if (ctx) {
|
||||
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
||||
if (!this.options.allowTaint) {
|
||||
ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
}
|
||||
return canvas;
|
||||
} catch (e) {
|
||||
this.context.logger.info(`Unable to clone video as it is tainted`, video);
|
||||
}
|
||||
|
||||
const blankCanvas = video.ownerDocument.createElement('canvas');
|
||||
|
||||
blankCanvas.width = video.offsetWidth;
|
||||
blankCanvas.height = video.offsetHeight;
|
||||
return blankCanvas;
|
||||
}
|
||||
|
||||
appendChildNode(clone: HTMLElement | SVGElement, child: Node, copyStyles: boolean): void {
|
||||
if (
|
||||
!isElementNode(child) ||
|
||||
@ -257,6 +286,23 @@ export class DocumentCloner {
|
||||
}
|
||||
}
|
||||
|
||||
cloneChildNodes(node: Element, clone: HTMLElement | SVGElement, copyStyles: boolean): void {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cloneNode(node: Node, copyStyles: boolean): Node {
|
||||
if (isTextNode(node)) {
|
||||
return document.createTextNode(node.data);
|
||||
@ -290,19 +336,8 @@ export class DocumentCloner {
|
||||
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);
|
||||
}
|
||||
if (!isVideoElement(node)) {
|
||||
this.cloneChildNodes(node, clone, copyStyles);
|
||||
}
|
||||
|
||||
if (before) {
|
||||
|
@ -124,6 +124,7 @@ export const isHTMLElement = (node: Element): node is HTMLHtmlElement => node.ta
|
||||
export const isSVGElement = (node: Element): node is SVGSVGElement => node.tagName === 'svg';
|
||||
export const isBodyElement = (node: Element): node is HTMLBodyElement => node.tagName === 'BODY';
|
||||
export const isCanvasElement = (node: Element): node is HTMLCanvasElement => node.tagName === 'CANVAS';
|
||||
export const isVideoElement = (node: Element): node is HTMLVideoElement => node.tagName === 'VIDEO';
|
||||
export const isImageElement = (node: Element): node is HTMLImageElement => node.tagName === 'IMG';
|
||||
export const isIFrameElement = (node: Element): node is HTMLIFrameElement => node.tagName === 'IFRAME';
|
||||
export const isStyleElement = (node: Element): node is HTMLStyleElement => node.tagName === 'STYLE';
|
||||
|
BIN
tests/assets/cc0-video.mp4
Normal file
BIN
tests/assets/cc0-video.mp4
Normal file
Binary file not shown.
20
tests/reftests/images/video.html
Normal file
20
tests/reftests/images/video.html
Normal file
@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Video tests</title>
|
||||
<script type="text/javascript" src="../../test.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Same origin</h2>
|
||||
<video controls width="250">
|
||||
<source src="../../assets/cc0-video.mp4" type="video/mp4">
|
||||
Sorry, your browser doesn't support embedded videos.
|
||||
</video>
|
||||
<h2>Cross-origin (doesn't taint)</h2>
|
||||
<video controls width="250">
|
||||
<source src="http://localhost:8081/cors/tests/assets/cc0-video.mp4" type="video/mp4">
|
||||
Sorry, your browser doesn't support embedded videos.
|
||||
</video>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user