Skip to content

Commit

Permalink
feat: add support for <video> elements (#2788)
Browse files Browse the repository at this point in the history
  • Loading branch information
niklasvh authored Jan 2, 2022
1 parent 46db867 commit 181d1b1
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 15 deletions.
65 changes: 50 additions & 15 deletions src/dom/document-cloner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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) ||
Expand All @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions src/dom/node-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Binary file added tests/assets/cc0-video.mp4
Binary file not shown.
20 changes: 20 additions & 0 deletions tests/reftests/images/video.html
Original file line number Diff line number Diff line change
@@ -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>

0 comments on commit 181d1b1

Please sign in to comment.