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 28, 2022
1 parent 33b84ca commit 1b95644
Show file tree
Hide file tree
Showing 15 changed files with 872 additions and 392 deletions.
13 changes: 11 additions & 2 deletions packages/happy-dom/src/dom-implementation/DOMImplementation.ts
Expand Up @@ -5,7 +5,16 @@ import IDocument from '../nodes/document/IDocument';
* The DOMImplementation interface represents an object providing methods which are not dependent on any particular document. Such an object is returned by the.
*/
export default class DOMImplementation {
public _ownerDocument: IDocument = null;
protected _ownerDocument: IDocument = null;

/**
* Constructor.
*
* @param ownerDocument
*/
constructor(ownerDocument: IDocument) {
this._ownerDocument = ownerDocument;
}

/**
* Creates and returns an XML Document.
Expand Down Expand Up @@ -37,7 +46,7 @@ export default class DOMImplementation {
publicId: string,
systemId: string
): DocumentType {
DocumentType.ownerDocument = this._ownerDocument;
DocumentType._ownerDocument = this._ownerDocument;
const documentType = new DocumentType();
documentType.name = qualifiedName;
documentType.publicId = publicId;
Expand Down
24 changes: 17 additions & 7 deletions packages/happy-dom/src/dom-parser/DOMParser.ts
@@ -1,10 +1,11 @@
import Document from '../nodes/document/Document';
import IDocument from '../nodes/document/IDocument';
import XMLParser from '../xml-parser/XMLParser';
import Node from '../nodes/node/Node';
import DOMException from '../exception/DOMException';
import HTMLDocument from '../nodes/html-document/HTMLDocument';
import XMLDocument from '../nodes/xml-document/XMLDocument';
import SVGDocument from '../nodes/svg-document/SVGDocument';
import IWindow from '../window/IWindow';

/**
* DOM parser.
Expand All @@ -13,7 +14,16 @@ import SVGDocument from '../nodes/svg-document/SVGDocument';
* https://developer.mozilla.org/en-US/docs/Web/API/DOMParser.
*/
export default class DOMParser {
public static _ownerDocument: Document = null;
// Owner document is set by a sub-class in the Window constructor
public static _ownerDocument: IDocument = null;
public readonly _ownerDocument: IDocument = null;

/**
* Constructor.
*/
constructor() {
this._ownerDocument = (<typeof DOMParser>this.constructor)._ownerDocument;
}

/**
* Parses HTML and returns a root element.
Expand All @@ -22,15 +32,15 @@ export default class DOMParser {
* @param mimeType Mime type.
* @returns Root element.
*/
public parseFromString(string: string, mimeType: string): Document {
public parseFromString(string: string, mimeType: string): IDocument {
if (!mimeType) {
throw new DOMException('Second parameter "mimeType" is mandatory.');
}

const ownerDocument = (<typeof DOMParser>(<unknown>this.constructor))._ownerDocument;
const ownerDocument = this._ownerDocument;
const newDocument = this._createDocument(mimeType);

newDocument.defaultView = ownerDocument.defaultView;
(<IWindow>newDocument.defaultView) = ownerDocument.defaultView;
newDocument.childNodes.length = 0;
newDocument.children.length = 0;

Expand Down Expand Up @@ -81,9 +91,9 @@ export default class DOMParser {
/**
*
* @param mimeType Mime type.
* @returns Document.
* @returns IDocument.
*/
private _createDocument(mimeType: string): Document {
private _createDocument(mimeType: string): IDocument {
switch (mimeType) {
case 'text/html':
return new HTMLDocument();
Expand Down
3 changes: 2 additions & 1 deletion packages/happy-dom/src/exception/DOMExceptionNameEnum.ts
Expand Up @@ -6,6 +6,7 @@ enum DOMExceptionNameEnum {
notSupportedError = 'NotSupportedError',
wrongDocumentError = 'WrongDocumentError',
invalidNodeTypeError = 'InvalidNodeTypeError',
invalidCharacterError = 'InvalidCharacterError'
invalidCharacterError = 'InvalidCharacterError',
notFoundError = 'NotFoundError'
}
export default DOMExceptionNameEnum;
22 changes: 16 additions & 6 deletions packages/happy-dom/src/fetch/Request.ts
Expand Up @@ -7,7 +7,20 @@ import IDocument from '../nodes/document/IDocument';
* Fetch request.
*/
export default class Request extends NodeFetch.Request implements IRequest {
// Owner document is set by a sub-class in the Window constructor
public static _ownerDocument: IDocument = null;
public readonly _ownerDocument: IDocument = null;

/**
* Constructor.
*
* @param input Input.
* @param [init] Init.
*/
constructor(input: NodeFetch.RequestInfo, init?: NodeFetch.RequestInit) {
super(input, init);
this._ownerDocument = (<typeof Request>this.constructor)._ownerDocument;
}

/**
* Returns array buffer.
Expand Down Expand Up @@ -105,8 +118,7 @@ export default class Request extends NodeFetch.Request implements IRequest {
* @returns Task ID.
*/
private _handlePromiseStart(): number {
const taskManager = (<typeof Request>this.constructor)._ownerDocument.defaultView.happyDOM
.asyncTaskManager;
const taskManager = this._ownerDocument.defaultView.happyDOM.asyncTaskManager;
return taskManager.startTask();
}

Expand All @@ -124,8 +136,7 @@ export default class Request extends NodeFetch.Request implements IRequest {
taskID: number,
response: unknown
): void {
const taskManager = (<typeof Request>this.constructor)._ownerDocument.defaultView.happyDOM
.asyncTaskManager;
const taskManager = this._ownerDocument.defaultView.happyDOM.asyncTaskManager;
if (taskManager.getTaskCount() === 0) {
reject(new Error('Failed to complete fetch request. Task was canceled.'));
} else {
Expand All @@ -141,8 +152,7 @@ export default class Request extends NodeFetch.Request implements IRequest {
* @param reject
*/
private _handlePromiseError(reject: (error: Error) => void, error: Error): void {
const taskManager = (<typeof Request>this.constructor)._ownerDocument.defaultView.happyDOM
.asyncTaskManager;
const taskManager = this._ownerDocument.defaultView.happyDOM.asyncTaskManager;
reject(error);
taskManager.cancelAll(error);
}
Expand Down
19 changes: 13 additions & 6 deletions packages/happy-dom/src/fetch/Response.ts
Expand Up @@ -7,7 +7,17 @@ import * as NodeFetch from 'node-fetch';
* Fetch response.
*/
export default class Response extends NodeFetch.Response implements IResponse {
// Owner document is set by a sub-class in the Window constructor
public static _ownerDocument: IDocument = null;
public readonly _ownerDocument: IDocument = null;

/**
* Constructor.
*/
constructor() {
super();
this._ownerDocument = (<typeof Response>this.constructor)._ownerDocument;
}

/**
* Returns array buffer.
Expand Down Expand Up @@ -105,8 +115,7 @@ export default class Response extends NodeFetch.Response implements IResponse {
* @returns Task ID.
*/
private _handlePromiseStart(): number {
const taskManager = (<typeof Response>this.constructor)._ownerDocument.defaultView.happyDOM
.asyncTaskManager;
const taskManager = this._ownerDocument.defaultView.happyDOM.asyncTaskManager;
return taskManager.startTask();
}

Expand All @@ -124,8 +133,7 @@ export default class Response extends NodeFetch.Response implements IResponse {
taskID: number,
response: unknown
): void {
const taskManager = (<typeof Response>this.constructor)._ownerDocument.defaultView.happyDOM
.asyncTaskManager;
const taskManager = this._ownerDocument.defaultView.happyDOM.asyncTaskManager;
if (taskManager.getTaskCount() === 0) {
reject(new Error('Failed to complete fetch request. Task was canceled.'));
} else {
Expand All @@ -141,8 +149,7 @@ export default class Response extends NodeFetch.Response implements IResponse {
* @param reject
*/
private _handlePromiseError(reject: (error: Error) => void, error: Error): void {
const taskManager = (<typeof Response>this.constructor)._ownerDocument.defaultView.happyDOM
.asyncTaskManager;
const taskManager = this._ownerDocument.defaultView.happyDOM.asyncTaskManager;
reject(error);
taskManager.cancelAll(error);
}
Expand Down
18 changes: 14 additions & 4 deletions packages/happy-dom/src/file/FileReader.ts
@@ -1,6 +1,6 @@
import WhatwgMIMEType from 'whatwg-mimetype';
import WhatwgEncoding from 'whatwg-encoding';
import Document from '../nodes/document/Document';
import IDocument from '../nodes/document/IDocument';
import ProgressEvent from '../event/events/ProgressEvent';
import DOMException from '../exception/DOMException';
import DOMExceptionNameEnum from '../exception/DOMExceptionNameEnum';
Expand All @@ -18,7 +18,8 @@ import FileReaderEventTypeEnum from './FileReaderEventTypeEnum';
* https://github.com/jsdom/jsdom/blob/master/lib/jsdom/living/file-api/FileReader-impl.js (MIT licensed).
*/
export default class FileReader extends EventTarget {
public static _ownerDocument: Document = null;
// Owner document is set by a sub-class in the Window constructor
public static _ownerDocument: IDocument = null;
public readonly error: Error = null;
public readonly result: Buffer | ArrayBuffer | string = null;
public readonly readyState: number = FileReaderReadyStateEnum.empty;
Expand All @@ -28,10 +29,19 @@ export default class FileReader extends EventTarget {
public readonly onloadstart: (event: ProgressEvent) => void = null;
public readonly onloadend: (event: ProgressEvent) => void = null;
public readonly onprogress: (event: ProgressEvent) => void = null;
public readonly _ownerDocument: IDocument = null;
private _isTerminated = false;
private _loadTimeout: NodeJS.Timeout = null;
private _parseTimeout: NodeJS.Timeout = null;

/**
* Constructor.
*/
constructor() {
super();
this._ownerDocument = (<typeof FileReader>this.constructor)._ownerDocument;
}

/**
* Reads as ArrayBuffer.
*
Expand Down Expand Up @@ -77,7 +87,7 @@ export default class FileReader extends EventTarget {
* Aborts the file reader.
*/
public abort(): void {
const window = (<typeof FileReader>this.constructor)._ownerDocument.defaultView;
const window = this._ownerDocument.defaultView;

window.clearTimeout(this._loadTimeout);
window.clearTimeout(this._parseTimeout);
Expand Down Expand Up @@ -108,7 +118,7 @@ export default class FileReader extends EventTarget {
* @param [encoding] Encoding.
*/
private _readFile(blob: Blob, format: FileReaderFormatEnum, encoding: string = null): void {
const window = (<typeof FileReader>this.constructor)._ownerDocument.defaultView;
const window = this._ownerDocument.defaultView;

if (this.readyState === FileReaderReadyStateEnum.loading) {
throw new DOMException(
Expand Down
28 changes: 16 additions & 12 deletions packages/happy-dom/src/nodes/document/Document.ts
Expand Up @@ -58,15 +58,15 @@ export default class Document extends Node implements IDocument {
protected _isFirstWriteAfterOpen = false;
private _defaultView: IWindow = null;
private _cookie = '';
private _selection: Selection = null;

/**
* Creates an instance of Document.
*/
constructor() {
super();

this.implementation = new DOMImplementation();
this.implementation._ownerDocument = this;
this.implementation = new DOMImplementation(this);

const doctype = this.implementation.createDocumentType('html', '', '');
const documentElement = this.createElement('html');
Expand Down Expand Up @@ -655,14 +655,15 @@ export default class Document extends Node implements IDocument {
customElementClass = this.defaultView.customElements.get(tagName);
}

const elementClass = customElementClass || ElementTag[tagName] || HTMLUnknownElement;
const elementClass: typeof Element =
customElementClass || ElementTag[tagName] || HTMLUnknownElement;

elementClass.ownerDocument = this;
elementClass._ownerDocument = this;

const element = new elementClass();
element.tagName = tagName;
element.ownerDocument = this;
element.namespaceURI = namespaceURI;
(<IDocument>element.ownerDocument) = this;
(<string>element.namespaceURI) = namespaceURI;
if (element instanceof Element && options && options.is) {
element._isValue = String(options.is);
}
Expand All @@ -679,7 +680,7 @@ export default class Document extends Node implements IDocument {
* @returns Text node.
*/
public createTextNode(data?: string): IText {
Text.ownerDocument = this;
Text._ownerDocument = this;
return new Text(data);
}

Expand All @@ -690,7 +691,7 @@ export default class Document extends Node implements IDocument {
* @returns Text node.
*/
public createComment(data?: string): IComment {
Comment.ownerDocument = this;
Comment._ownerDocument = this;
return new Comment(data);
}

Expand All @@ -700,7 +701,7 @@ export default class Document extends Node implements IDocument {
* @returns Document fragment.
*/
public createDocumentFragment(): IDocumentFragment {
DocumentFragment.ownerDocument = this;
DocumentFragment._ownerDocument = this;
return new DocumentFragment();
}

Expand All @@ -723,7 +724,7 @@ export default class Document extends Node implements IDocument {
* @returns Event.
*/
public createEvent(type: string): Event {
if (this.defaultView[type]) {
if (typeof this.defaultView[type] === 'function') {
return new this.defaultView[type]('init');
}
return new Event('init');
Expand Down Expand Up @@ -779,7 +780,7 @@ export default class Document extends Node implements IDocument {
* @returns Range.
*/
public createRange(): Range {
return new Range();
return new this.defaultView.Range();
}

/**
Expand All @@ -804,7 +805,10 @@ export default class Document extends Node implements IDocument {
* @returns Selection.
*/
public getSelection(): Selection {
return new Selection();
if (!this._selection) {
this._selection = new Selection(this);
}
return this._selection;
}

/**
Expand Down
6 changes: 4 additions & 2 deletions packages/happy-dom/src/nodes/node/Node.ts
Expand Up @@ -16,6 +16,9 @@ import NodeTypeEnum from './NodeTypeEnum';
* Node.
*/
export default class Node extends EventTarget implements INode {
// Owner document is set when the Node is created by the Document
public static _ownerDocument: IDocument = null;

// Public properties
public static readonly ELEMENT_NODE = NodeTypeEnum.elementNode;
public static readonly TEXT_NODE = NodeTypeEnum.textNode;
Expand All @@ -24,7 +27,6 @@ export default class Node extends EventTarget implements INode {
public static readonly DOCUMENT_TYPE_NODE = NodeTypeEnum.documentTypeNode;
public static readonly DOCUMENT_FRAGMENT_NODE = NodeTypeEnum.documentFragmentNode;
public static readonly PROCESSING_INSTRUCTION_NODE = NodeTypeEnum.processingInstructionNode;
public static ownerDocument: IDocument = null;
public readonly ELEMENT_NODE = NodeTypeEnum.elementNode;
public readonly TEXT_NODE = NodeTypeEnum.textNode;
public readonly COMMENT_NODE = NodeTypeEnum.commentNode;
Expand All @@ -47,7 +49,7 @@ export default class Node extends EventTarget implements INode {
*/
constructor() {
super();
this.ownerDocument = (<typeof Node>this.constructor).ownerDocument;
this.ownerDocument = (<typeof Node>this.constructor)._ownerDocument;
}

/**
Expand Down
9 changes: 9 additions & 0 deletions packages/happy-dom/src/range/IRangeBoundaryPoint.ts
@@ -0,0 +1,9 @@
import INode from '../nodes/node/INode';

/**
* Range boundary point.
*/
export default interface IRangeBoundaryPoint {
node: INode;
offset: number;
}

0 comments on commit 1b95644

Please sign in to comment.