Skip to content

Commit

Permalink
Merge branch 'master' into 1406-fix-issue-with-insertbefore-and-comme…
Browse files Browse the repository at this point in the history
…nt-node
  • Loading branch information
capricorn86 authored May 6, 2024
2 parents 676c634 + 9095b2d commit 84cc397
Show file tree
Hide file tree
Showing 12 changed files with 604 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ export default class BrowserFrameNavigator {
const width = frame.window.innerWidth;
const height = frame.window.innerHeight;
const devicePixelRatio = frame.window.devicePixelRatio;
const parentWindow = frame.window.parent !== frame.window ? frame.window.parent : null;
const topWindow = frame.window.top !== frame.window ? frame.window.top : null;

for (const childFrame of frame.childFrames) {
BrowserFrameFactory.destroyFrame(childFrame);
Expand All @@ -104,6 +106,8 @@ export default class BrowserFrameNavigator {
frame[PropertySymbol.asyncTaskManager] = new AsyncTaskManager();

(<BrowserWindow>frame.window) = new windowClass(frame, { url: targetURL.href, width, height });
(<BrowserWindow>frame.window.parent) = parentWindow;
(<BrowserWindow>frame.window.top) = topWindow;
(<number>frame.window.devicePixelRatio) = devicePixelRatio;

if (referrer) {
Expand Down
2 changes: 2 additions & 0 deletions packages/happy-dom/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ import DocumentFragment from './nodes/document-fragment/DocumentFragment.js';
import DocumentType from './nodes/document-type/DocumentType.js';
import Document from './nodes/document/Document.js';
import DOMRect from './nodes/element/DOMRect.js';
import DOMRectReadOnly from './nodes/element/DOMRectReadOnly.js';
import Element from './nodes/element/Element.js';
import HTMLCollection from './nodes/element/HTMLCollection.js';
import HTMLAnchorElement from './nodes/html-anchor-element/HTMLAnchorElement.js';
Expand Down Expand Up @@ -211,6 +212,7 @@ export {
DOMException,
DOMParser,
DOMRect,
DOMRectReadOnly,
DataTransfer,
DataTransferItem,
DataTransferItemList,
Expand Down
4 changes: 2 additions & 2 deletions packages/happy-dom/src/nodes/document/Document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,8 +318,8 @@ export default class Document extends Node {
/**
* Returns a collection of all form elements in a document.
*/
public get forms(): NodeList<HTMLFormElement> {
return this.querySelectorAll('form');
public get forms(): HTMLCollection<HTMLFormElement> {
return this.getElementsByTagName('form');
}

/**
Expand Down
63 changes: 40 additions & 23 deletions packages/happy-dom/src/nodes/element/DOMRect.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,47 @@
import DOMRectReadOnly, { IDOMRectInit } from './DOMRectReadOnly.js';
import * as PropertySymbol from '../../PropertySymbol.js';

/* eslint-disable jsdoc/require-jsdoc */

/**
* Bounding rect object.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMRect
*/
export default class DOMRect {
public x = 0;
public y = 0;
public width = 0;
public height = 0;
public top = 0;
public right = 0;
public bottom = 0;
public left = 0;

/**
* Constructor.
*
* @param [x] X position.
* @param [y] Y position.
* @param [width] Width.
* @param [height] Height.
*/
constructor(x?, y?, width?, height?) {
this.x = x || 0;
this.y = y || 0;
this.width = width || 0;
this.height = height || 0;
export default class DOMRect extends DOMRectReadOnly {
public set x(value: number) {
this[PropertySymbol.x] = value;
}

public get x(): number {
return this[PropertySymbol.x];
}

public set y(value: number) {
this[PropertySymbol.y] = value;
}

public get y(): number {
return this[PropertySymbol.y];
}

public set width(value: number) {
this[PropertySymbol.width] = value;
}

public get width(): number {
return this[PropertySymbol.width];
}

public set height(value: number) {
this[PropertySymbol.height] = value;
}

public get height(): number {
return this[PropertySymbol.height];
}

public static fromRect(other: IDOMRectInit): DOMRect {
return new DOMRect(other.x, other.y, other.width, other.height);
}
}
86 changes: 86 additions & 0 deletions packages/happy-dom/src/nodes/element/DOMRectReadOnly.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import * as PropertySymbol from '../../PropertySymbol.js';

/* eslint-disable jsdoc/require-jsdoc */

/**
* Bounding rect readonly object.
*
* @see https://drafts.fxtf.org/geometry/#DOMRect
*/
export default class DOMRectReadOnly implements IDOMRectInit {
protected [PropertySymbol.x]: number = 0;
protected [PropertySymbol.y]: number = 0;
protected [PropertySymbol.width]: number = 0;
protected [PropertySymbol.height]: number = 0;

/**
* Constructor.
*
* @param [x] X position.
* @param [y] Y position.
* @param [width] Width.
* @param [height] Height.
*/
constructor(x?: number | null, y?: number | null, width?: number | null, height?: number | null) {
this[PropertySymbol.x] = x !== undefined && x !== null ? Number(x) : 0;
this[PropertySymbol.y] = y !== undefined && y !== null ? Number(y) : 0;
this[PropertySymbol.width] = width !== undefined && width !== null ? Number(width) : 0;
this[PropertySymbol.height] = height !== undefined && height !== null ? Number(height) : 0;
}

public get x(): number {
return this[PropertySymbol.x];
}

public get y(): number {
return this[PropertySymbol.y];
}

public get width(): number {
return this[PropertySymbol.width];
}

public get height(): number {
return this[PropertySymbol.height];
}

public get top(): number {
return Math.min(this[PropertySymbol.y], this[PropertySymbol.y] + this[PropertySymbol.height]);
}

public get right(): number {
return Math.max(this[PropertySymbol.x], this[PropertySymbol.x] + this[PropertySymbol.width]);
}

public get bottom(): number {
return Math.max(this[PropertySymbol.y], this[PropertySymbol.y] + this[PropertySymbol.height]);
}

public get left(): number {
return Math.min(this[PropertySymbol.x], this[PropertySymbol.x] + this[PropertySymbol.width]);
}

public toJSON(): object {
return {
x: this.x,
y: this.y,
width: this.width,
height: this.height,
top: this.top,
right: this.right,
bottom: this.bottom,
left: this.left
};
}

public static fromRect(other: IDOMRectInit): DOMRectReadOnly {
return new DOMRectReadOnly(other.x, other.y, other.width, other.height);
}
}

export interface IDOMRectInit {
readonly x: number;
readonly y: number;
readonly width: number;
readonly height: number;
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,14 @@ export default class HTMLIFrameElementNamedNodeMap extends HTMLElementNamedNodeM
public override setNamedItem(item: Attr): Attr | null {
const replacedAttribute = super.setNamedItem(item);

if (item[PropertySymbol.name] === 'srcdoc') {
this.#pageLoader.loadPage();
}

// If the src attribute and the srcdoc attribute are both specified together, the srcdoc attribute takes priority.
if (
item[PropertySymbol.name] === 'src' &&
this[PropertySymbol.ownerElement][PropertySymbol.attributes]['srcdoc']?.value === undefined &&
item[PropertySymbol.value] &&
item[PropertySymbol.value] !== replacedAttribute?.[PropertySymbol.value]
) {
Expand All @@ -70,6 +76,21 @@ export default class HTMLIFrameElementNamedNodeMap extends HTMLElementNamedNodeM
return replacedAttribute || null;
}

/**
* @override
*/
public override [PropertySymbol.removeNamedItem](name: string): Attr | null {
const removedItem = super[PropertySymbol.removeNamedItem](name);
if (
removedItem &&
(removedItem[PropertySymbol.name] === 'srcdoc' || removedItem[PropertySymbol.name] === 'src')
) {
this.#pageLoader.loadPage();
}

return removedItem;
}

/**
*
* @param tokens
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export default class HTMLIFrameElementPageLoader {
#contentWindowContainer: { window: BrowserWindow | CrossOriginBrowserWindow | null };
#browserParentFrame: IBrowserFrame;
#browserIFrame: IBrowserFrame;
#srcdoc: string | null = null;

/**
* Constructor.
Expand All @@ -44,15 +45,43 @@ export default class HTMLIFrameElementPageLoader {
*/
public loadPage(): void {
if (!this.#element[PropertySymbol.isConnected]) {
if (this.#browserIFrame) {
BrowserFrameFactory.destroyFrame(this.#browserIFrame);
this.#browserIFrame = null;
}
this.#contentWindowContainer.window = null;
this.unloadPage();
return;
}

const srcdoc = this.#element.getAttribute('srcdoc');
const window = this.#element[PropertySymbol.ownerDocument][PropertySymbol.ownerWindow];

if (srcdoc !== null) {
if (this.#srcdoc === srcdoc) {
return;
}

this.unloadPage();

this.#browserIFrame = BrowserFrameFactory.createChildFrame(this.#browserParentFrame);
this.#browserIFrame.url = 'about:srcdoc';

this.#contentWindowContainer.window = this.#browserIFrame.window;

(<BrowserWindow>this.#browserIFrame.window.top) = this.#browserParentFrame.window.top;
(<BrowserWindow>this.#browserIFrame.window.parent) = this.#browserParentFrame.window;

this.#browserIFrame.window.document.open();
this.#browserIFrame.window.document.write(srcdoc);

this.#srcdoc = srcdoc;

this.#element[PropertySymbol.ownerDocument][PropertySymbol.ownerWindow].requestAnimationFrame(
() => this.#element.dispatchEvent(new Event('load'))
);
return;
}

if (this.#srcdoc !== null) {
this.unloadPage();
}

const originURL = this.#browserParentFrame.window.location;
const targetURL = BrowserFrameURL.getRelativeURL(this.#browserParentFrame, this.#element.src);

Expand Down Expand Up @@ -105,5 +134,6 @@ export default class HTMLIFrameElementPageLoader {
this.#browserIFrame = null;
}
this.#contentWindowContainer.window = null;
this.#srcdoc = null;
}
}
2 changes: 2 additions & 0 deletions packages/happy-dom/src/window/BrowserWindow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ import Plugin from '../navigator/Plugin.js';
import PluginArray from '../navigator/PluginArray.js';
import Fetch from '../fetch/Fetch.js';
import DOMRect from '../nodes/element/DOMRect.js';
import DOMRectReadOnly from '../nodes/element/DOMRectReadOnly.js';
import VMGlobalPropertyScript from './VMGlobalPropertyScript.js';
import VM from 'vm';
import { Buffer } from 'buffer';
Expand Down Expand Up @@ -372,6 +373,7 @@ export default class BrowserWindow extends EventTarget implements INodeJSGlobal
public readonly PluginArray = PluginArray;
public readonly FileList = FileList;
public readonly DOMRect = DOMRect;
public readonly DOMRectReadOnly = DOMRectReadOnly;
public readonly RadioNodeList = RadioNodeList;
public readonly ValidityState = ValidityState;
public readonly Headers = Headers;
Expand Down
1 change: 1 addition & 0 deletions packages/happy-dom/test/nodes/document/Document.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ describe('Document', () => {

const forms = document.forms;

expect(forms).toBeInstanceOf(HTMLCollection);
expect(forms.length).toBe(2);
expect(forms[0]).toBe(form1);
expect(forms[1]).toBe(form2);
Expand Down
Loading

0 comments on commit 84cc397

Please sign in to comment.