diff --git a/packages/happy-dom/README.md b/packages/happy-dom/README.md index 3da7a1530..5368b8172 100644 --- a/packages/happy-dom/README.md +++ b/packages/happy-dom/README.md @@ -289,7 +289,7 @@ Set it to "true" to disable CSS file loading in HTMLLinkElement. Defaults to "fa **disableIframePageLoading** -Set it to "true" to disable page loading in HTMLIframeElement. Defaults to "false". +Set it to "true" to disable page loading in HTMLIFrameElement. Defaults to "false". **enableFileSystemHttpRequests** diff --git a/packages/happy-dom/src/config/ElementTag.ts b/packages/happy-dom/src/config/ElementTag.ts index 034c460da..6253d833e 100644 --- a/packages/happy-dom/src/config/ElementTag.ts +++ b/packages/happy-dom/src/config/ElementTag.ts @@ -21,7 +21,7 @@ import HTMLButtonElement from '../nodes/html-button-element/HTMLButtonElement'; import HTMLAudioElement from '../nodes/html-audio-element/HTMLAudioElement'; import HTMLVideoElement from '../nodes/html-video-element/HTMLVideoElement'; import HTMLAnchorElement from '../nodes/html-anchor-element/HTMLAnchorElement'; -import HTMLIframeElement from '../nodes/html-iframe-element/HTMLIframeElement'; +import HTMLIFrameElement from '../nodes/html-iframe-element/HTMLIFrameElement'; export default { A: HTMLAnchorElement, @@ -95,7 +95,7 @@ export default { HR: HTMLElement, HTML: HTMLElement, I: HTMLElement, - IFRAME: HTMLIframeElement, + IFRAME: HTMLIFrameElement, INS: HTMLElement, KBD: HTMLElement, LEGEND: HTMLElement, diff --git a/packages/happy-dom/src/index.ts b/packages/happy-dom/src/index.ts index 88491aa06..bc870ffd8 100644 --- a/packages/happy-dom/src/index.ts +++ b/packages/happy-dom/src/index.ts @@ -82,6 +82,8 @@ import HTMLVideoElement from './nodes/html-video-element/HTMLVideoElement'; import IHTMLVideoElement from './nodes/html-video-element/IHTMLVideoElement'; import HTMLBaseElement from './nodes/html-base-element/HTMLBaseElement'; import IHTMLBaseElement from './nodes/html-base-element/IHTMLBaseElement'; +import HTMLIFrameElement from './nodes/html-iframe-element/HTMLIFrameElement'; +import IHTMLIFrameElement from './nodes/html-iframe-element/IHTMLIFrameElement'; import SVGElement from './nodes/svg-element/SVGElement'; import ISVGElement from './nodes/svg-element/ISVGElement'; import SVGGraphicsElement from './nodes/svg-element/SVGGraphicsElement'; @@ -211,6 +213,8 @@ export { IHTMLVideoElement, HTMLBaseElement, IHTMLBaseElement, + HTMLIFrameElement, + IHTMLIFrameElement, SVGElement, ISVGElement, SVGGraphicsElement, diff --git a/packages/happy-dom/src/nodes/html-iframe-element/HTMLIframeElement.ts b/packages/happy-dom/src/nodes/html-iframe-element/HTMLIFrameElement.ts similarity index 89% rename from packages/happy-dom/src/nodes/html-iframe-element/HTMLIframeElement.ts rename to packages/happy-dom/src/nodes/html-iframe-element/HTMLIFrameElement.ts index 566a6a2b9..baf24da8b 100644 --- a/packages/happy-dom/src/nodes/html-iframe-element/HTMLIframeElement.ts +++ b/packages/happy-dom/src/nodes/html-iframe-element/HTMLIFrameElement.ts @@ -6,22 +6,22 @@ import Window from '../../window/Window'; import IDocument from '../document/IDocument'; import HTMLElement from '../html-element/HTMLElement'; import INode from '../node/INode'; -import IframeCrossOriginWindow from './IframeCrossOriginWindow'; -import IHTMLIframeElement from './IHTMLIframeElement'; +import IFrameCrossOriginWindow from './IFrameCrossOriginWindow'; +import IHTMLIFrameElement from './IHTMLIFrameElement'; /** * HTML Iframe Element. * * Reference: - * https://developer.mozilla.org/en-US/docs/Web/API/HTMLIframeElement. + * https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement. */ -export default class HTMLIframeElement extends HTMLElement implements IHTMLIframeElement { +export default class HTMLIFrameElement extends HTMLElement implements IHTMLIFrameElement { // Events public onload: (event: Event) => void | null = null; public onerror: (event: Event) => void | null = null; // Private - #contentWindow: IWindow | IframeCrossOriginWindow | null = null; + #contentWindow: IWindow | IFrameCrossOriginWindow | null = null; /** * Returns source. @@ -163,7 +163,7 @@ export default class HTMLIframeElement extends HTMLElement implements IHTMLIfram * * @returns Content window. */ - public get contentWindow(): IWindow | IframeCrossOriginWindow | null { + public get contentWindow(): IWindow | IFrameCrossOriginWindow | null { return this.#contentWindow || null; } @@ -241,7 +241,7 @@ export default class HTMLIframeElement extends HTMLElement implements IHTMLIfram .text() .then((text) => { this.#contentWindow = isCORS - ? new IframeCrossOriginWindow(this.ownerDocument.defaultView, contentWindow) + ? new IFrameCrossOriginWindow(this.ownerDocument.defaultView, contentWindow) : contentWindow; contentWindow.document.write(text); this.dispatchEvent(new Event('load')); @@ -260,7 +260,7 @@ export default class HTMLIframeElement extends HTMLElement implements IHTMLIfram * @param [deep=false] "true" to clone deep. * @returns Cloned node. */ - public cloneNode(deep = false): IHTMLIframeElement { - return super.cloneNode(deep); + public cloneNode(deep = false): IHTMLIFrameElement { + return super.cloneNode(deep); } } diff --git a/packages/happy-dom/src/nodes/html-iframe-element/IframeCrossOriginWindow.ts b/packages/happy-dom/src/nodes/html-iframe-element/IFrameCrossOriginWindow.ts similarity index 60% rename from packages/happy-dom/src/nodes/html-iframe-element/IframeCrossOriginWindow.ts rename to packages/happy-dom/src/nodes/html-iframe-element/IFrameCrossOriginWindow.ts index 77821503f..40ce7ddfb 100644 --- a/packages/happy-dom/src/nodes/html-iframe-element/IframeCrossOriginWindow.ts +++ b/packages/happy-dom/src/nodes/html-iframe-element/IFrameCrossOriginWindow.ts @@ -1,15 +1,18 @@ -import Location from '../../location/Location'; import EventTarget from '../../event/EventTarget'; import IWindow from '../../window/IWindow'; +import DOMException from '../../exception/DOMException'; +import DOMExceptionNameEnum from '../../exception/DOMExceptionNameEnum'; +import Location from '../../location/Location'; /** * Browser window with limited access due to CORS restrictions in iframes. */ -export default class IframeCrossOriginWindow extends EventTarget { +export default class IFrameCrossOriginWindow extends EventTarget { public readonly self = this; public readonly window = this; public readonly parent: IWindow; public readonly top: IWindow; + public readonly location: Location; private _targetWindow: IWindow; @@ -24,18 +27,26 @@ export default class IframeCrossOriginWindow extends EventTarget { this.parent = parent; this.top = parent; + this.location = new Proxy( + {}, + { + get: () => { + throw new DOMException( + `Blocked a frame with origin "${this.parent.location.origin}" from accessing a cross-origin frame.`, + DOMExceptionNameEnum.securityError + ); + }, + set: () => { + throw new DOMException( + `Blocked a frame with origin "${this.parent.location.origin}" from accessing a cross-origin frame.`, + DOMExceptionNameEnum.securityError + ); + } + } + ); this._targetWindow = target; } - /** - * Returns location. - * - * @returns Location. - */ - public get location(): Location { - return this._targetWindow.location; - } - /** * Safely enables cross-origin communication between Window objects; e.g., between a page and a pop-up that it spawned, or between a page and an iframe embedded within it. * diff --git a/packages/happy-dom/src/nodes/html-iframe-element/IHTMLIframeElement.ts b/packages/happy-dom/src/nodes/html-iframe-element/IHTMLIFrameElement.ts similarity index 68% rename from packages/happy-dom/src/nodes/html-iframe-element/IHTMLIframeElement.ts rename to packages/happy-dom/src/nodes/html-iframe-element/IHTMLIFrameElement.ts index 1c326cd43..5b9c111d8 100644 --- a/packages/happy-dom/src/nodes/html-iframe-element/IHTMLIframeElement.ts +++ b/packages/happy-dom/src/nodes/html-iframe-element/IHTMLIFrameElement.ts @@ -2,15 +2,15 @@ import Event from '../../event/Event'; import IWindow from '../../window/IWindow'; import IDocument from '../document/IDocument'; import IHTMLElement from '../html-element/IHTMLElement'; -import IframeCrossOriginWindow from './IframeCrossOriginWindow'; +import IFrameCrossOriginWindow from './IFrameCrossOriginWindow'; /** * HTML Iframe Element. * * Reference: - * https://developer.mozilla.org/en-US/docs/Web/API/HTMLIframeElement. + * https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement. */ -export default interface IHTMLIframeElement extends IHTMLElement { +export default interface IHTMLIFrameElement extends IHTMLElement { src: string | null; allow: string | null; height: string | null; @@ -19,7 +19,7 @@ export default interface IHTMLIframeElement extends IHTMLElement { sandbox: string | null; srcdoc: string | null; readonly contentDocument: IDocument | null; - readonly contentWindow: IWindow | IframeCrossOriginWindow | null; + readonly contentWindow: IWindow | IFrameCrossOriginWindow | null; // Events onload: (event: Event) => void | null; diff --git a/packages/happy-dom/src/window/IWindow.ts b/packages/happy-dom/src/window/IWindow.ts index 20820b2f9..9d75971aa 100644 --- a/packages/happy-dom/src/window/IWindow.ts +++ b/packages/happy-dom/src/window/IWindow.ts @@ -24,7 +24,7 @@ import HTMLMediaElement from '../nodes/html-media-element/HTMLMediaElement'; import HTMLAudioElement from '../nodes/html-audio-element/HTMLAudioElement'; import HTMLVideoElement from '../nodes/html-video-element/HTMLVideoElement'; import HTMLBaseElement from '../nodes/html-base-element/HTMLBaseElement'; -import HTMLIframeElement from '../nodes/html-iframe-element/HTMLIframeElement'; +import HTMLIFrameElement from '../nodes/html-iframe-element/HTMLIFrameElement'; import SVGSVGElement from '../nodes/svg-element/SVGSVGElement'; import SVGElement from '../nodes/svg-element/SVGElement'; import HTMLScriptElement from '../nodes/html-script-element/HTMLScriptElement'; @@ -145,7 +145,7 @@ export default interface IWindow extends IEventTarget, NodeJS.Global { readonly HTMLAudioElement: typeof HTMLAudioElement; readonly HTMLVideoElement: typeof HTMLVideoElement; readonly HTMLBaseElement: typeof HTMLBaseElement; - readonly HTMLIframeElement: typeof HTMLIframeElement; + readonly HTMLIFrameElement: typeof HTMLIFrameElement; readonly HTMLDialogElement: typeof HTMLDialogElement; readonly Attr: typeof Attr; readonly NamedNodeMap: typeof NamedNodeMap; diff --git a/packages/happy-dom/src/window/Window.ts b/packages/happy-dom/src/window/Window.ts index 5c7ce8f14..10f65d080 100644 --- a/packages/happy-dom/src/window/Window.ts +++ b/packages/happy-dom/src/window/Window.ts @@ -25,7 +25,7 @@ import HTMLMediaElement from '../nodes/html-media-element/HTMLMediaElement'; import HTMLAudioElement from '../nodes/html-audio-element/HTMLAudioElement'; import HTMLVideoElement from '../nodes/html-video-element/HTMLVideoElement'; import HTMLBaseElement from '../nodes/html-base-element/HTMLBaseElement'; -import HTMLIframeElement from '../nodes/html-iframe-element/HTMLIframeElement'; +import HTMLIFrameElement from '../nodes/html-iframe-element/HTMLIFrameElement'; import HTMLDialogElement from '../nodes/html-dialog-element/HTMLDialogElement'; import SVGSVGElement from '../nodes/svg-element/SVGSVGElement'; import SVGElement from '../nodes/svg-element/SVGElement'; @@ -188,7 +188,7 @@ export default class Window extends EventTarget implements IWindow { public readonly HTMLAudioElement = HTMLAudioElement; public readonly HTMLVideoElement = HTMLVideoElement; public readonly HTMLBaseElement = HTMLBaseElement; - public readonly HTMLIframeElement = HTMLIframeElement; + public readonly HTMLIFrameElement = HTMLIFrameElement; public readonly HTMLDialogElement = HTMLDialogElement; public readonly Attr = Attr; public readonly NamedNodeMap = NamedNodeMap; diff --git a/packages/happy-dom/test/nodes/html-iframe-element/HTMLIframeElement.test.ts b/packages/happy-dom/test/nodes/html-iframe-element/HTMLIFrameElement.test.ts similarity index 84% rename from packages/happy-dom/test/nodes/html-iframe-element/HTMLIframeElement.test.ts rename to packages/happy-dom/test/nodes/html-iframe-element/HTMLIFrameElement.test.ts index bae297f1a..bd4d0b20d 100644 --- a/packages/happy-dom/test/nodes/html-iframe-element/HTMLIframeElement.test.ts +++ b/packages/happy-dom/test/nodes/html-iframe-element/HTMLIFrameElement.test.ts @@ -1,26 +1,28 @@ import Window from '../../../src/window/Window'; import IWindow from '../../../src/window/IWindow'; import IDocument from '../../../src/nodes/document/IDocument'; -import IHTMLIframeElement from '../../../src/nodes/html-iframe-element/IHTMLIframeElement'; +import IHTMLIFrameElement from '../../../src/nodes/html-iframe-element/IHTMLIFrameElement'; import IResponse from '../../../src/fetch/IResponse'; import ErrorEvent from '../../../src/event/events/ErrorEvent'; -import IframeCrossOriginWindow from '../../../src/nodes/html-iframe-element/IframeCrossOriginWindow'; +import IFrameCrossOriginWindow from '../../../src/nodes/html-iframe-element/IFrameCrossOriginWindow'; import MessageEvent from '../../../src/event/events/MessageEvent'; +import DOMExceptionNameEnum from '../../../src/exception/DOMExceptionNameEnum'; +import DOMException from '../../../src/exception/DOMException'; -describe('HTMLIframeElement', () => { +describe('HTMLIFrameElement', () => { let window: IWindow; let document: IDocument; - let element: IHTMLIframeElement; + let element: IHTMLIFrameElement; beforeEach(() => { window = new Window(); document = window.document; - element = document.createElement('iframe'); + element = document.createElement('iframe'); }); describe('Object.prototype.toString', () => { - it('Returns `[object HTMLIframeElement]`', () => { - expect(Object.prototype.toString.call(element)).toBe('[object HTMLIframeElement]'); + it('Returns `[object HTMLIFrameElement]`', () => { + expect(Object.prototype.toString.call(element)).toBe('[object HTMLIFrameElement]'); }); }); @@ -82,7 +84,7 @@ describe('HTMLIframeElement', () => { document.body.appendChild(element); }); - it('Returns instance of IframeCrossOriginWindow for URL with different origin.', (done) => { + it('Returns instance of IFrameCrossOriginWindow for URL with different origin.', (done) => { const iframeOrigin = 'https://other.origin.com'; const iframeSrc = iframeOrigin + '/iframe.html'; const documentOrigin = 'https://localhost:8080'; @@ -102,8 +104,13 @@ describe('HTMLIframeElement', () => { const message = 'test'; let triggeredEvent: MessageEvent | null = null; expect(fetchedURL).toBe(iframeSrc); - expect(element.contentWindow instanceof IframeCrossOriginWindow).toBe(true); - expect(element.contentWindow.location.href).toBe(iframeSrc); + expect(element.contentWindow instanceof IFrameCrossOriginWindow).toBe(true); + expect(() => element.contentWindow.location.href).toThrowError( + new DOMException( + `Blocked a frame with origin "${documentOrigin}" from accessing a cross-origin frame.`, + DOMExceptionNameEnum.securityError + ) + ); expect(element.contentWindow.self === element.contentWindow).toBe(true); expect(element.contentWindow.window === element.contentWindow).toBe(true); expect(element.contentWindow.parent === window).toBe(true);