Skip to content

Commit 13a5ce7

Browse files
authored
Merge pull request #4851 from preactjs/support-alternate-content-doc
Support alternative contentDocument
2 parents 2712cb5 + af1bd0f commit 13a5ce7

File tree

7 files changed

+57
-19
lines changed

7 files changed

+57
-19
lines changed

compat/src/portals.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ function Portal(props) {
4646
parentNode: container,
4747
childNodes: [],
4848
_children: { _mask: root._mask },
49+
ownerDocument: container.ownerDocument,
4950
insertBefore(child, before) {
5051
this.childNodes.push(child);
5152
_this._container.insertBefore(child, before);

src/component.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,22 +126,24 @@ function renderComponent(component) {
126126
commitQueue = [],
127127
refQueue = [];
128128

129-
if (component._parentDom) {
129+
const parentDom = component._parentDom;
130+
if (parentDom) {
130131
const newVNode = assign({}, oldVNode);
131132
newVNode._original = oldVNode._original + 1;
132133
if (options.vnode) options.vnode(newVNode);
133134

134135
diff(
135-
component._parentDom,
136+
parentDom,
136137
newVNode,
137138
oldVNode,
138139
component._globalContext,
139-
component._parentDom.namespaceURI,
140+
parentDom.namespaceURI,
140141
oldVNode._flags & MODE_HYDRATE ? [oldDom] : NULL,
141142
commitQueue,
142143
oldDom == NULL ? getDomSibling(oldVNode) : oldDom,
143144
!!(oldVNode._flags & MODE_HYDRATE),
144-
refQueue
145+
refQueue,
146+
parentDom.ownerDocument
145147
);
146148

147149
newVNode._original = oldVNode._original;

src/diff/children.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ import { getDomSibling } from '../component';
3939
* siblings. In most cases, it starts out as `oldChildren[0]._dom`.
4040
* @param {boolean} isHydrating Whether or not we are in hydration
4141
* @param {any[]} refQueue an array of elements needed to invoke refs
42+
* @param {Document} doc The document object to use for creating elements
43+
* @returns {PreactElement} The next sibling DOM element to insert new elements
4244
*/
4345
export function diffChildren(
4446
parentDom,
@@ -51,7 +53,8 @@ export function diffChildren(
5153
commitQueue,
5254
oldDom,
5355
isHydrating,
54-
refQueue
56+
refQueue,
57+
doc
5558
) {
5659
let i,
5760
/** @type {VNode} */
@@ -104,7 +107,8 @@ export function diffChildren(
104107
commitQueue,
105108
oldDom,
106109
isHydrating,
107-
refQueue
110+
refQueue,
111+
doc
108112
);
109113

110114
// Adjust DOM nodes

src/diff/index.js

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import options from '../options';
4545
* siblings. In most cases, it starts out as `oldChildren[0]._dom`.
4646
* @param {boolean} isHydrating Whether or not we are in hydration
4747
* @param {any[]} refQueue an array of elements needed to invoke refs
48+
* @param {Document} doc The document object to use for creating elements
4849
*/
4950
export function diff(
5051
parentDom,
@@ -56,7 +57,8 @@ export function diff(
5657
commitQueue,
5758
oldDom,
5859
isHydrating,
59-
refQueue
60+
refQueue,
61+
doc
6062
) {
6163
/** @type {any} */
6264
let tmp,
@@ -278,7 +280,8 @@ export function diff(
278280
commitQueue,
279281
oldDom,
280282
isHydrating,
281-
refQueue
283+
refQueue,
284+
doc
282285
);
283286

284287
// We successfully rendered this VNode, unset any stored hydration/bailout state:
@@ -366,7 +369,8 @@ export function diff(
366369
excessDomChildren,
367370
commitQueue,
368371
isHydrating,
369-
refQueue
372+
refQueue,
373+
doc
370374
);
371375
}
372376

@@ -431,6 +435,7 @@ function cloneNode(node) {
431435
* to invoke in commitRoot
432436
* @param {boolean} isHydrating Whether or not we are in hydration
433437
* @param {any[]} refQueue an array of elements needed to invoke refs
438+
* @param {Document} doc The document object to use for creating elements
434439
* @returns {PreactElement}
435440
*/
436441
function diffElementNodes(
@@ -442,7 +447,8 @@ function diffElementNodes(
442447
excessDomChildren,
443448
commitQueue,
444449
isHydrating,
445-
refQueue
450+
refQueue,
451+
doc
446452
) {
447453
let oldProps = oldVNode.props;
448454
let newProps = newVNode.props;
@@ -485,14 +491,10 @@ function diffElementNodes(
485491

486492
if (dom == NULL) {
487493
if (nodeType == NULL) {
488-
return document.createTextNode(newProps);
494+
return doc.createTextNode(newProps);
489495
}
490496

491-
dom = document.createElementNS(
492-
namespace,
493-
nodeType,
494-
newProps.is && newProps
495-
);
497+
dom = doc.createElementNS(namespace, nodeType, newProps.is && newProps);
496498

497499
// we are creating a new node, so we can assume this is a new subtree (in
498500
// case we are hydrating), this deopts the hydrate
@@ -592,7 +594,8 @@ function diffElementNodes(
592594
? excessDomChildren[0]
593595
: oldVNode._children && getDomSibling(oldVNode, 0),
594596
isHydrating,
595-
refQueue
597+
refQueue,
598+
doc
596599
);
597600

598601
// Remove children that are not part of any vnode.

src/internal.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ export interface PreactElement extends preact.ContainerNode {
105105
addEventListener?: Element['addEventListener'];
106106
removeEventListener?: Element['removeEventListener'];
107107

108+
// Used to match DOM nodes to VNodes during hydration
109+
readonly ownerDocument: Document;
110+
108111
// Setting styles
109112
readonly style?: CSSStyleDeclaration;
110113

src/render.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export function render(vnode, parentDom) {
3232
// List of effects that need to be called after diffing.
3333
let commitQueue = [],
3434
refQueue = [];
35+
3536
diff(
3637
parentDom,
3738
// Determine the new vnode tree and store it on the DOM element on
@@ -48,7 +49,8 @@ export function render(vnode, parentDom) {
4849
commitQueue,
4950
oldVNode ? oldVNode._dom : parentDom.firstChild,
5051
isHydrating,
51-
refQueue
52+
refQueue,
53+
parentDom.ownerDocument
5254
);
5355

5456
// Flush all queued effects

test/browser/render.test.js

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
} from '../_util/helpers';
1414
import { clearLog, getLog, logCall } from '../_util/logCall';
1515
import { useState } from 'preact/hooks';
16-
import { vi } from 'vitest';
16+
import { expect, vi } from 'vitest';
1717

1818
/** @jsx createElement */
1919

@@ -2012,4 +2012,27 @@ describe('render()', () => {
20122012
render(<App />, scratch);
20132013
expect(scratch.innerHTML).to.equal('hello world');
20142014
});
2015+
2016+
describe('Alternative document', () => {
2017+
it('Renders in an iframe', () => {
2018+
const iframe = document.createElement('iframe');
2019+
scratch.appendChild(iframe);
2020+
2021+
const rootCreateElementSpy = vi.spyOn(document, 'createElementNS');
2022+
const iframeCreateElementSpy = vi.spyOn(
2023+
iframe.contentDocument,
2024+
'createElementNS'
2025+
);
2026+
2027+
const iframeBody = iframe.contentDocument.getElementsByTagName('body')[0];
2028+
2029+
render(<div>Hello world</div>, iframeBody);
2030+
2031+
expect(iframe.contentDocument.body.innerHTML).to.equal(
2032+
'<div>Hello world</div>'
2033+
);
2034+
expect(rootCreateElementSpy).not.toBeCalled();
2035+
expect(iframeCreateElementSpy).toBeCalled();
2036+
});
2037+
});
20152038
});

0 commit comments

Comments
 (0)