@@ -13,7 +13,8 @@ import {
1313 isStyleElement ,
1414 isSVGElementNode ,
1515 isTextareaElement ,
16- isTextNode
16+ isTextNode ,
17+ isVideoElement
1718} from './node-parser' ;
1819import { isIdentToken , nonFunctionArgSeparator } from '../css/syntax/parser' ;
1920import { TokenType } from '../css/syntax/tokenizer' ;
@@ -145,7 +146,9 @@ export class DocumentCloner {
145146 if ( isCanvasElement ( node ) ) {
146147 return this . createCanvasClone ( node ) ;
147148 }
148-
149+ if ( isVideoElement ( node ) ) {
150+ return this . createVideoClone ( node ) ;
151+ }
149152 if ( isStyleElement ( node ) ) {
150153 return this . createStyleClone ( node ) ;
151154 }
@@ -244,6 +247,32 @@ export class DocumentCloner {
244247 return clonedCanvas ;
245248 }
246249
250+ createVideoClone ( video : HTMLVideoElement ) : HTMLCanvasElement {
251+ const canvas = video . ownerDocument . createElement ( 'canvas' ) ;
252+
253+ canvas . width = video . offsetWidth ;
254+ canvas . height = video . offsetHeight ;
255+ const ctx = canvas . getContext ( '2d' ) ;
256+
257+ try {
258+ if ( ctx ) {
259+ ctx . drawImage ( video , 0 , 0 , canvas . width , canvas . height ) ;
260+ if ( ! this . options . allowTaint ) {
261+ ctx . getImageData ( 0 , 0 , canvas . width , canvas . height ) ;
262+ }
263+ }
264+ return canvas ;
265+ } catch ( e ) {
266+ this . context . logger . info ( `Unable to clone video as it is tainted` , video ) ;
267+ }
268+
269+ const blankCanvas = video . ownerDocument . createElement ( 'canvas' ) ;
270+
271+ blankCanvas . width = video . offsetWidth ;
272+ blankCanvas . height = video . offsetHeight ;
273+ return blankCanvas ;
274+ }
275+
247276 appendChildNode ( clone : HTMLElement | SVGElement , child : Node , copyStyles : boolean ) : void {
248277 if (
249278 ! isElementNode ( child ) ||
@@ -257,6 +286,23 @@ export class DocumentCloner {
257286 }
258287 }
259288
289+ cloneChildNodes ( node : Element , clone : HTMLElement | SVGElement , copyStyles : boolean ) : void {
290+ for (
291+ let child = node . shadowRoot ? node . shadowRoot . firstChild : node . firstChild ;
292+ child ;
293+ child = child . nextSibling
294+ ) {
295+ if ( isElementNode ( child ) && isSlotElement ( child ) && typeof child . assignedNodes === 'function' ) {
296+ const assignedNodes = child . assignedNodes ( ) as ChildNode [ ] ;
297+ if ( assignedNodes . length ) {
298+ assignedNodes . forEach ( ( assignedNode ) => this . appendChildNode ( clone , assignedNode , copyStyles ) ) ;
299+ }
300+ } else {
301+ this . appendChildNode ( clone , child , copyStyles ) ;
302+ }
303+ }
304+ }
305+
260306 cloneNode ( node : Node , copyStyles : boolean ) : Node {
261307 if ( isTextNode ( node ) ) {
262308 return document . createTextNode ( node . data ) ;
@@ -290,19 +336,8 @@ export class DocumentCloner {
290336 copyStyles = true ;
291337 }
292338
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 ) ) ;
302- }
303- } else {
304- this . appendChildNode ( clone , child , copyStyles ) ;
305- }
339+ if ( ! isVideoElement ( node ) ) {
340+ this . cloneChildNodes ( node , clone , copyStyles ) ;
306341 }
307342
308343 if ( before ) {
0 commit comments