Skip to content

Commit bc6b865

Browse files
DustinBrettbubkoo
andauthored
fix: clone iframe nodes better (#352)
* fix: clone iframe nodes better * fix: switch cloned iframes from inline to blocks --------- Co-authored-by: 崖 <[email protected]>
1 parent 6dfc846 commit bc6b865

File tree

3 files changed

+60
-22
lines changed

3 files changed

+60
-22
lines changed

src/clone-node.ts

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Options } from './types'
22
import { clonePseudoElements } from './clone-pseudos'
3-
import { createImage, toArray } from './util'
3+
import { createImage, toArray, isInstanceOfElement } from './util'
44
import { getMimeType } from './mimes'
55
import { resourceToDataURL } from './dataurl'
66

@@ -49,15 +49,15 @@ async function cloneSingleNode<T extends HTMLElement>(
4949
node: T,
5050
options: Options,
5151
): Promise<HTMLElement> {
52-
if (node instanceof HTMLCanvasElement) {
52+
if (isInstanceOfElement(node, HTMLCanvasElement)) {
5353
return cloneCanvasElement(node)
5454
}
5555

56-
if (node instanceof HTMLVideoElement) {
56+
if (isInstanceOfElement(node, HTMLVideoElement)) {
5757
return cloneVideoElement(node, options)
5858
}
5959

60-
if (node instanceof HTMLIFrameElement) {
60+
if (isInstanceOfElement(node, HTMLIFrameElement)) {
6161
return cloneIFrameElement(node)
6262
}
6363

@@ -72,12 +72,23 @@ async function cloneChildren<T extends HTMLElement>(
7272
clonedNode: T,
7373
options: Options,
7474
): Promise<T> {
75-
const children =
76-
isSlotElement(nativeNode) && nativeNode.assignedNodes
77-
? toArray<T>(nativeNode.assignedNodes())
78-
: toArray<T>((nativeNode.shadowRoot ?? nativeNode).childNodes)
75+
let children: T[] = []
76+
77+
if (isSlotElement(nativeNode) && nativeNode.assignedNodes) {
78+
children = toArray<T>(nativeNode.assignedNodes())
79+
} else if (
80+
isInstanceOfElement(nativeNode, HTMLIFrameElement) &&
81+
nativeNode.contentDocument?.body
82+
) {
83+
children = toArray<T>(nativeNode.contentDocument.body.childNodes)
84+
} else {
85+
children = toArray<T>((nativeNode.shadowRoot ?? nativeNode).childNodes)
86+
}
7987

80-
if (children.length === 0 || nativeNode instanceof HTMLVideoElement) {
88+
if (
89+
children.length === 0 ||
90+
isInstanceOfElement(nativeNode, HTMLVideoElement)
91+
) {
8192
return clonedNode
8293
}
8394

@@ -114,9 +125,19 @@ function cloneCSSStyle<T extends HTMLElement>(nativeNode: T, clonedNode: T) {
114125
Math.floor(parseFloat(value.substring(0, value.length - 2))) - 0.1
115126
value = `${reducedFont}px`
116127
}
128+
129+
if (
130+
isInstanceOfElement(nativeNode, HTMLIFrameElement) &&
131+
name === 'display' &&
132+
value === 'inline'
133+
) {
134+
value = 'block'
135+
}
136+
117137
if (name === 'd' && clonedNode.getAttribute('d')) {
118138
value = `path(${clonedNode.getAttribute('d')})`
119139
}
140+
120141
targetStyle.setProperty(
121142
name,
122143
value,
@@ -127,17 +148,17 @@ function cloneCSSStyle<T extends HTMLElement>(nativeNode: T, clonedNode: T) {
127148
}
128149

129150
function cloneInputValue<T extends HTMLElement>(nativeNode: T, clonedNode: T) {
130-
if (nativeNode instanceof HTMLTextAreaElement) {
151+
if (isInstanceOfElement(nativeNode, HTMLTextAreaElement)) {
131152
clonedNode.innerHTML = nativeNode.value
132153
}
133154

134-
if (nativeNode instanceof HTMLInputElement) {
155+
if (isInstanceOfElement(nativeNode, HTMLInputElement)) {
135156
clonedNode.setAttribute('value', nativeNode.value)
136157
}
137158
}
138159

139160
function cloneSelectValue<T extends HTMLElement>(nativeNode: T, clonedNode: T) {
140-
if (nativeNode instanceof HTMLSelectElement) {
161+
if (isInstanceOfElement(nativeNode, HTMLSelectElement)) {
141162
const clonedSelect = clonedNode as any as HTMLSelectElement
142163
const selectedOption = Array.from(clonedSelect.children).find(
143164
(child) => nativeNode.value === child.getAttribute('value'),
@@ -150,7 +171,7 @@ function cloneSelectValue<T extends HTMLElement>(nativeNode: T, clonedNode: T) {
150171
}
151172

152173
function decorate<T extends HTMLElement>(nativeNode: T, clonedNode: T): T {
153-
if (clonedNode instanceof Element) {
174+
if (isInstanceOfElement(clonedNode, Element)) {
154175
cloneCSSStyle(nativeNode, clonedNode)
155176
clonePseudoElements(nativeNode, clonedNode)
156177
cloneInputValue(nativeNode, clonedNode)

src/embed-images.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Options } from './types'
22
import { embedResources } from './embed-resources'
3-
import { toArray } from './util'
3+
import { toArray, isInstanceOfElement } from './util'
44
import { isDataUrl, resourceToDataURL } from './dataurl'
55
import { getMimeType } from './mimes'
66

@@ -38,20 +38,19 @@ async function embedImageNode<T extends HTMLElement | SVGImageElement>(
3838
clonedNode: T,
3939
options: Options,
4040
) {
41+
const isImageElement = isInstanceOfElement(clonedNode, HTMLImageElement)
42+
4143
if (
42-
!(clonedNode instanceof HTMLImageElement && !isDataUrl(clonedNode.src)) &&
44+
!(isImageElement && !isDataUrl(clonedNode.src)) &&
4345
!(
44-
clonedNode instanceof SVGImageElement &&
46+
isInstanceOfElement(clonedNode, SVGImageElement) &&
4547
!isDataUrl(clonedNode.href.baseVal)
4648
)
4749
) {
4850
return
4951
}
5052

51-
const url =
52-
clonedNode instanceof HTMLImageElement
53-
? clonedNode.src
54-
: clonedNode.href.baseVal
53+
const url = isImageElement ? clonedNode.src : clonedNode.href.baseVal
5554

5655
const dataURL = await resourceToDataURL(url, getMimeType(url), options)
5756
await new Promise((resolve, reject) => {
@@ -67,7 +66,7 @@ async function embedImageNode<T extends HTMLElement | SVGImageElement>(
6766
image.loading = 'eager'
6867
}
6968

70-
if (clonedNode instanceof HTMLImageElement) {
69+
if (isImageElement) {
7170
clonedNode.srcset = ''
7271
clonedNode.src = dataURL
7372
} else {
@@ -89,7 +88,7 @@ export async function embedImages<T extends HTMLElement>(
8988
clonedNode: T,
9089
options: Options,
9190
) {
92-
if (clonedNode instanceof Element) {
91+
if (isInstanceOfElement(clonedNode, Element)) {
9392
await embedBackground(clonedNode, options)
9493
await embedImageNode(clonedNode, options)
9594
await embedChildren(clonedNode, options)

src/util.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,3 +222,21 @@ export async function nodeToDataURL(
222222
foreignObject.appendChild(node)
223223
return svgToDataURL(svg)
224224
}
225+
226+
export const isInstanceOfElement = <
227+
T extends typeof Element | typeof HTMLElement | typeof SVGImageElement,
228+
>(
229+
node: Element | HTMLElement | SVGImageElement,
230+
instance: T,
231+
): node is T['prototype'] => {
232+
if (node instanceof instance) return true
233+
234+
const nodePrototype = Object.getPrototypeOf(node)
235+
236+
if (nodePrototype === null) return false
237+
238+
return (
239+
nodePrototype.constructor.name === instance.name ||
240+
isInstanceOfElement(nodePrototype, instance)
241+
)
242+
}

0 commit comments

Comments
 (0)