From 91689ec2b96f2563d68e6bb2c856471b09e0ba0b Mon Sep 17 00:00:00 2001 From: David Alsh Date: Wed, 19 Feb 2025 02:43:19 +0000 Subject: [PATCH] Get document from parentNode.ownerDocument Co-authored-by: Hydrophobefireman --- compat/src/portals.js | 3 +- src/component.js | 3 +- src/diff/children.js | 7 ++-- src/diff/index.js | 25 ++++++++------ src/internal.d.ts | 4 +-- src/render.js | 3 +- test/browser/getOwnerDocument.test.js | 50 +++++++++++++++++++++++++++ 7 files changed, 77 insertions(+), 18 deletions(-) create mode 100644 test/browser/getOwnerDocument.test.js diff --git a/compat/src/portals.js b/compat/src/portals.js index c480a3d3a3..67535fe30b 100644 --- a/compat/src/portals.js +++ b/compat/src/portals.js @@ -52,7 +52,8 @@ function Portal(props) { removeChild(child) { this.childNodes.splice(this.childNodes.indexOf(child) >>> 1, 1); _this._container.removeChild(child); - } + }, + ownerDocument: container.ownerDocument }; } diff --git a/src/component.js b/src/component.js index 967ebcac66..595fa7fe45 100644 --- a/src/component.js +++ b/src/component.js @@ -141,7 +141,8 @@ function renderComponent(component) { commitQueue, oldDom == NULL ? getDomSibling(oldVNode) : oldDom, !!(oldVNode._flags & MODE_HYDRATE), - refQueue + refQueue, + component._parentDom.ownerDocument ); newVNode._original = oldVNode._original; diff --git a/src/diff/children.js b/src/diff/children.js index c40b4d5b1e..3c17cb281a 100644 --- a/src/diff/children.js +++ b/src/diff/children.js @@ -39,6 +39,7 @@ import { getDomSibling } from '../component'; * siblings. In most cases, it starts out as `oldChildren[0]._dom`. * @param {boolean} isHydrating Whether or not we are in hydration * @param {any[]} refQueue an array of elements needed to invoke refs + * @param {Document} doc The owner document of the parentNode */ export function diffChildren( parentDom, @@ -51,7 +52,8 @@ export function diffChildren( commitQueue, oldDom, isHydrating, - refQueue + refQueue, + doc ) { let i, /** @type {VNode} */ @@ -104,7 +106,8 @@ export function diffChildren( commitQueue, oldDom, isHydrating, - refQueue + refQueue, + doc ); // Adjust DOM nodes diff --git a/src/diff/index.js b/src/diff/index.js index 30199e5fed..7ed77781f0 100644 --- a/src/diff/index.js +++ b/src/diff/index.js @@ -45,6 +45,7 @@ import options from '../options'; * siblings. In most cases, it starts out as `oldChildren[0]._dom`. * @param {boolean} isHydrating Whether or not we are in hydration * @param {any[]} refQueue an array of elements needed to invoke refs + * @param {Document} doc The owner document of the parentNode */ export function diff( parentDom, @@ -56,7 +57,8 @@ export function diff( commitQueue, oldDom, isHydrating, - refQueue + refQueue, + doc ) { /** @type {any} */ let tmp, @@ -275,7 +277,8 @@ export function diff( commitQueue, oldDom, isHydrating, - refQueue + refQueue, + doc ); c.base = newVNode._dom; @@ -332,7 +335,8 @@ export function diff( excessDomChildren, commitQueue, isHydrating, - refQueue + refQueue, + doc ); } @@ -381,6 +385,7 @@ export function commitRoot(commitQueue, root, refQueue) { * to invoke in commitRoot * @param {boolean} isHydrating Whether or not we are in hydration * @param {any[]} refQueue an array of elements needed to invoke refs + * @param {Document} doc The owner document of the parentNode * @returns {PreactElement} */ function diffElementNodes( @@ -392,7 +397,8 @@ function diffElementNodes( excessDomChildren, commitQueue, isHydrating, - refQueue + refQueue, + doc ) { let oldProps = oldVNode.props; let newProps = newVNode.props; @@ -435,14 +441,10 @@ function diffElementNodes( if (dom == NULL) { if (nodeType == NULL) { - return document.createTextNode(newProps); + return doc.createTextNode(newProps); } - dom = document.createElementNS( - namespace, - nodeType, - newProps.is && newProps - ); + dom = doc.createElementNS(namespace, nodeType, newProps.is && newProps); // we are creating a new node, so we can assume this is a new subtree (in // case we are hydrating), this deopts the hydrate @@ -543,7 +545,8 @@ function diffElementNodes( ? excessDomChildren[0] : oldVNode._children && getDomSibling(oldVNode, 0), isHydrating, - refQueue + refQueue, + doc ); // Remove children that are not part of any vnode. diff --git a/src/internal.d.ts b/src/internal.d.ts index 7733b0f279..c9f1fc6113 100644 --- a/src/internal.d.ts +++ b/src/internal.d.ts @@ -62,8 +62,7 @@ export type ComponentChild = | undefined; export type ComponentChildren = ComponentChild[] | ComponentChild; -export interface FunctionComponent

- extends preact.FunctionComponent

{ +export interface FunctionComponent

extends preact.FunctionComponent

{ // Internally, createContext uses `contextType` on a Function component to // implement the Consumer component contextType?: PreactContext; @@ -123,6 +122,7 @@ export interface PreactElement extends preact.ContainerNode { _children?: VNode | null; /** Event listeners to support event delegation */ _listeners?: Record void>; + ownerDocument: Document; } export interface PreactEvent extends Event { diff --git a/src/render.js b/src/render.js index 5080853a47..e974f3f1a8 100644 --- a/src/render.js +++ b/src/render.js @@ -61,7 +61,8 @@ export function render(vnode, parentDom, replaceNode) { ? oldVNode._dom : parentDom.firstChild, isHydrating, - refQueue + refQueue, + parentDom.ownerDocument ); // Flush all queued effects diff --git a/test/browser/getOwnerDocument.test.js b/test/browser/getOwnerDocument.test.js new file mode 100644 index 0000000000..a4d5aded38 --- /dev/null +++ b/test/browser/getOwnerDocument.test.js @@ -0,0 +1,50 @@ +import { createElement, render } from 'preact'; +import { setupScratch, teardown } from '../_util/helpers'; + +/** @jsx createElement */ + +describe('parentDom.ownerDocument', () => { + /** @type {HTMLDivElement} */ + let scratch; + + before(() => { + scratch = setupScratch(); + }); + + after(() => { + teardown(scratch); + }); + + it('should reference the correct document from the parent node', () => { + let iframe = document.createElement('iframe'); + + scratch.appendChild(iframe); + + let iframeDoc = iframe.contentDocument; + + iframeDoc.write( + '

' + ); + + iframeDoc.close(); + + let rootTextSpy = sinon.spy(document, 'createTextNode'); + let rootElementSpy = sinon.spy(document, 'createElement'); + + let iframeTextSpy = sinon.spy(iframeDoc, 'createTextNode'); + let iframeElementSpy = sinon.spy(iframeDoc, 'createElement'); + + let iframeRootNode = iframeDoc.querySelector('div'); + + render(Hello, iframeRootNode); + + expect(rootTextSpy).not.to.be.called; + expect(rootElementSpy).not.to.be.called; + expect(iframeTextSpy).to.be.called; + expect(iframeElementSpy).to.be.called; + + expect(iframeRootNode.textContent).to.be.equal('Hello'); + expect(iframeRootNode.firstChild.ownerDocument).to.be.equal(iframeDoc); + expect(iframeRootNode.firstChild.ownerDocument).to.not.be.equal(document); + }); +});