Skip to content

Commit

Permalink
#450@trivial: Continue on Range implementation.
Browse files Browse the repository at this point in the history
  • Loading branch information
capricorn86 committed Jun 29, 2022
1 parent 1b95644 commit 3d9a227
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 75 deletions.
Expand Up @@ -24,6 +24,8 @@ export default class DOMImplementation {
public createDocument(): IDocument {
const documentClass = this._ownerDocument.constructor;
// @ts-ignore
documentClass._defaultView = this._ownerDocument.defaultView;
// @ts-ignore
return new documentClass();
}

Expand Down
3 changes: 3 additions & 0 deletions packages/happy-dom/src/dom-parser/DOMParser.ts
Expand Up @@ -96,12 +96,15 @@ export default class DOMParser {
private _createDocument(mimeType: string): IDocument {
switch (mimeType) {
case 'text/html':
HTMLDocument._defaultView = this._ownerDocument.defaultView;
return new HTMLDocument();
case 'image/svg+xml':
SVGDocument._defaultView = this._ownerDocument.defaultView;
return new SVGDocument();
case 'text/xml':
case 'application/xml':
case 'application/xhtml+xml':
XMLDocument._defaultView = this._ownerDocument.defaultView;
return new XMLDocument();
default:
throw new DOMException(`Unknown mime type "${mimeType}".`);
Expand Down
50 changes: 22 additions & 28 deletions packages/happy-dom/src/nodes/document/Document.ts
Expand Up @@ -45,29 +45,35 @@ import IHTMLBaseElement from '../html-base-element/IHTMLBaseElement';
* Document.
*/
export default class Document extends Node implements IDocument {
public static _defaultView: IWindow = null;
public onreadystatechange: (event: Event) => void = null;
public nodeType = Node.DOCUMENT_NODE;
public adoptedStyleSheets: CSSStyleSheet[] = [];
public implementation: DOMImplementation;
public readonly children: IHTMLCollection<IElement> = HTMLCollectionFactory.create();
public readonly readyState = DocumentReadyStateEnum.interactive;
public readonly isConnected: boolean = true;
public _readyStateManager: DocumentReadyStateManager = null;
public readonly defaultView: IWindow;
public readonly _readyStateManager: DocumentReadyStateManager;
public _activeElement: IHTMLElement = null;
protected _isFirstWrite = true;
protected _isFirstWriteAfterOpen = false;
private _defaultView: IWindow = null;
private _cookie = '';
private _selection: Selection = null;

/**
* Creates an instance of Document.
*
* @param defaultView Default view.
*/
constructor() {
super();

this.defaultView = (<typeof Document>this.constructor)._defaultView;
this.implementation = new DOMImplementation(this);

this._readyStateManager = new DocumentReadyStateManager(this.defaultView);

const doctype = this.implementation.createDocumentType('html', '', '');
const documentElement = this.createElement('html');
const bodyElement = this.createElement('body');
Expand Down Expand Up @@ -100,29 +106,6 @@ export default class Document extends Node implements IDocument {
return charset ? charset : 'UTF-8';
}

/**
* Returns default view.
*
* @returns Default view.
*/
public get defaultView(): IWindow {
return this._defaultView;
}

/**
* Sets a default view.
*
* @param defaultView Default view.
*/
public set defaultView(defaultView: IWindow) {
this._defaultView = defaultView;
this._readyStateManager = new DocumentReadyStateManager(defaultView);
this._readyStateManager.whenComplete().then(() => {
(<DocumentReadyStateEnum>this.readyState) = DocumentReadyStateEnum.complete;
this.dispatchEvent(new Event('readystatechange'));
});
}

/**
* Last element child.
*
Expand Down Expand Up @@ -272,7 +255,7 @@ export default class Document extends Node implements IDocument {
* @returns Location.
*/
public get location(): Location {
return this._defaultView.location;
return this.defaultView.location;
}

/**
Expand Down Expand Up @@ -419,6 +402,8 @@ export default class Document extends Node implements IDocument {
* @returns Cloned node.
*/
public cloneNode(deep = false): IDocument {
(<typeof Document>this.constructor)._defaultView = this.defaultView;

const clone = <Document>super.cloneNode(deep);

if (deep) {
Expand All @@ -429,8 +414,6 @@ export default class Document extends Node implements IDocument {
}
}

clone.defaultView = this.defaultView;

return clone;
}

Expand Down Expand Up @@ -832,4 +815,15 @@ export default class Document extends Node implements IDocument {

return returnValue;
}

/**
* Triggered by window when it is ready.
*/
public _onWindowReady(): void {
this._readyStateManager.whenComplete().then(() => {
(<DocumentReadyStateEnum>this.readyState) = DocumentReadyStateEnum.complete;
this.dispatchEvent(new Event('readystatechange'));
this.dispatchEvent(new Event('load', { bubbles: true }));
});
}
}
99 changes: 60 additions & 39 deletions packages/happy-dom/src/window/Window.ts
Expand Up @@ -96,6 +96,11 @@ import { Buffer } from 'buffer';
import Base64 from '../base64/Base64';
import IDocument from '../nodes/document/IDocument';

const ORIGINAL_SET_TIMEOUT = setTimeout;
const ORIGINAL_CLEAR_TIMEOUT = clearTimeout;
const ORIGINAL_SET_INTERVAL = setInterval;
const ORIGINAL_CLEAR_INTERVAL = clearInterval;

/**
* Browser window.
*
Expand Down Expand Up @@ -287,52 +292,21 @@ export default class Window extends EventTarget implements IWindow {
public Function;

// Private properties
private _setTimeout = setTimeout;
private _clearTimeout = clearTimeout;
private _setInterval = setInterval;
private _clearInterval = clearInterval;
private _setTimeout;
private _clearTimeout;
private _setInterval;
private _clearInterval;

/**
* Constructor.
*/
constructor() {
super();

const document = new HTMLDocument();

this.document = document;
this.document.defaultView = this;
this.document._readyStateManager.whenComplete().then(() => {
this.dispatchEvent(new Event('load'));
});

// We need to set the correct owner document when the class is constructed.
// To achieve this we will extend the original implementation with a class that sets the owner document.

ResponseImplementation._ownerDocument = document;
RequestImplementation._ownerDocument = document;
ImageImplementation._ownerDocument = document;
FileReaderImplementation._ownerDocument = document;
DOMParserImplementation._ownerDocument = document;
RangeImplementation._ownerDocument = document;
this.Response = class Response extends ResponseImplementation {
public static _ownerDocument: IDocument = document;
};
this.Request = class Request extends RequestImplementation {
public static _ownerDocument: IDocument = document;
};
this.Image = class Image extends ImageImplementation {
public static _ownerDocument: IDocument = document;
};
this.FileReader = class FileReader extends FileReaderImplementation {
public static _ownerDocument: IDocument = document;
};
this.DOMParser = class DOMParser extends DOMParserImplementation {
public static _ownerDocument: IDocument = document;
};
this.Range = class Range extends RangeImplementation {
public static _ownerDocument: IDocument = document;
};
this._setTimeout = ORIGINAL_SET_TIMEOUT;
this._clearTimeout = ORIGINAL_CLEAR_TIMEOUT;
this._setInterval = ORIGINAL_SET_INTERVAL;
this._clearInterval = ORIGINAL_CLEAR_INTERVAL;

// Non-implemented event types
for (const eventType of NonImplementedEventTypes) {
Expand Down Expand Up @@ -362,7 +336,54 @@ export default class Window extends EventTarget implements IWindow {
}
}

HTMLDocument._defaultView = this;

const document = new HTMLDocument();

this.document = document;

// We need to set the correct owner document when the class is constructed.
// To achieve this we will extend the original implementation with a class that sets the owner document.

ResponseImplementation._ownerDocument = document;
RequestImplementation._ownerDocument = document;
ImageImplementation._ownerDocument = document;
FileReaderImplementation._ownerDocument = document;
DOMParserImplementation._ownerDocument = document;
RangeImplementation._ownerDocument = document;

/* eslint-disable jsdoc/require-jsdoc */
class Response extends ResponseImplementation {
public static _ownerDocument: IDocument = document;
}
class Request extends RequestImplementation {
public static _ownerDocument: IDocument = document;
}
class Image extends ImageImplementation {
public static _ownerDocument: IDocument = document;
}
class FileReader extends FileReaderImplementation {
public static _ownerDocument: IDocument = document;
}
class DOMParser extends DOMParserImplementation {
public static _ownerDocument: IDocument = document;
}
class Range extends RangeImplementation {
public static _ownerDocument: IDocument = document;
}

/* eslint-enable jsdoc/require-jsdoc */

this.Response = Response;
this.Request = Request;
this.Image = Image;
this.FileReader = FileReader;
this.DOMParser = DOMParser;
this.Range = Range;

this._setupVMContext();

this.document._onWindowReady();
}

/**
Expand Down
Expand Up @@ -7,8 +7,7 @@ describe('DOMImplementation', () => {

beforeEach(() => {
ownerDocument = new Document();
domImplementation = new DOMImplementation();
domImplementation._ownerDocument = ownerDocument;
domImplementation = new DOMImplementation(ownerDocument);
});

describe('createHTMLDocument()', () => {
Expand Down
10 changes: 4 additions & 6 deletions packages/happy-dom/test/window/Window.test.ts
Expand Up @@ -8,8 +8,6 @@ import Window from '../../src/window/Window';
import IWindow from '../../src/window/IWindow';
import Navigator from '../../src/navigator/Navigator';
import Headers from '../../src/fetch/Headers';
import Response from '../../src/fetch/Response';
import Request from '../../src/fetch/Request';
import Selection from '../../src/selection/Selection';
import DOMException from '../../src/exception/DOMException';
import DOMExceptionNameEnum from '../../src/exception/DOMExceptionNameEnum';
Expand Down Expand Up @@ -86,7 +84,7 @@ describe('Window', () => {
describe('get Response()', () => {
it('Returns Response class.', () => {
expect(window.Response['_ownerDocument']).toBe(document);
expect(window.Response).toBe(Response);
expect(window.Response.name).toBe('Response');
});

for (const method of ['arrayBuffer', 'blob', 'buffer', 'json', 'text', 'textConverted']) {
Expand All @@ -101,7 +99,7 @@ describe('Window', () => {
describe('get Request()', () => {
it('Returns Request class.', () => {
expect(window.Request['_ownerDocument']).toBe(document);
expect(window.Request).toBe(Request);
expect(window.Request.name).toBe('Request');
});

for (const method of ['arrayBuffer', 'blob', 'buffer', 'json', 'text', 'textConverted']) {
Expand Down Expand Up @@ -440,7 +438,7 @@ describe('Window', () => {
});

setTimeout(() => {
expect(loadEvent.target).toBe(window);
expect(loadEvent.target).toBe(document);
done();
}, 1);
});
Expand Down Expand Up @@ -490,7 +488,7 @@ describe('Window', () => {
expect(resourceFetchCSSURL).toBe(cssURL);
expect(resourceFetchJSDocument).toBe(document);
expect(resourceFetchJSURL).toBe(jsURL);
expect(loadEvent.target).toBe(window);
expect(loadEvent.target).toBe(document);
expect(document.styleSheets.length).toBe(1);
expect(document.styleSheets[0].cssRules[0].cssText).toBe(cssResponse);

Expand Down

0 comments on commit 3d9a227

Please sign in to comment.