@@ -13,7 +13,8 @@ import {
13
13
isStyleElement ,
14
14
isSVGElementNode ,
15
15
isTextareaElement ,
16
- isTextNode
16
+ isTextNode ,
17
+ isVideoElement
17
18
} from './node-parser' ;
18
19
import { isIdentToken , nonFunctionArgSeparator } from '../css/syntax/parser' ;
19
20
import { TokenType } from '../css/syntax/tokenizer' ;
@@ -145,7 +146,9 @@ export class DocumentCloner {
145
146
if ( isCanvasElement ( node ) ) {
146
147
return this . createCanvasClone ( node ) ;
147
148
}
148
-
149
+ if ( isVideoElement ( node ) ) {
150
+ return this . createVideoClone ( node ) ;
151
+ }
149
152
if ( isStyleElement ( node ) ) {
150
153
return this . createStyleClone ( node ) ;
151
154
}
@@ -244,6 +247,32 @@ export class DocumentCloner {
244
247
return clonedCanvas ;
245
248
}
246
249
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
+
247
276
appendChildNode ( clone : HTMLElement | SVGElement , child : Node , copyStyles : boolean ) : void {
248
277
if (
249
278
! isElementNode ( child ) ||
@@ -257,6 +286,23 @@ export class DocumentCloner {
257
286
}
258
287
}
259
288
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
+
260
306
cloneNode ( node : Node , copyStyles : boolean ) : Node {
261
307
if ( isTextNode ( node ) ) {
262
308
return document . createTextNode ( node . data ) ;
@@ -290,19 +336,8 @@ export class DocumentCloner {
290
336
copyStyles = true ;
291
337
}
292
338
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 ) ;
306
341
}
307
342
308
343
if ( before ) {
0 commit comments