Skip to content

Commit

Permalink
capricorn86#204@trivial: Improves performance and fixes some minor bu…
Browse files Browse the repository at this point in the history
…gs related to blob urls.
  • Loading branch information
daveed07 committed Nov 9, 2022
1 parent 69b2150 commit 15e4296
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 95 deletions.
2 changes: 1 addition & 1 deletion packages/happy-dom/src/dom-token-list/DOMTokenList.ts
Expand Up @@ -199,7 +199,7 @@ export default class DOMTokenList implements IDOMTokenList {
* Updates indices.
*/
public _updateIndices(): void {
const attr = this._ownerElement.getAttribute('class');
const attr = this._ownerElement.getAttribute(this._attributeName);
const list = attr ? Array.from(new Set(attr.split(' '))) : [];

for (let i = list.length - 1, max = this.length; i < max; i++) {
Expand Down
17 changes: 6 additions & 11 deletions packages/happy-dom/src/nodes/element/Element.ts
Expand Up @@ -840,7 +840,9 @@ export default class Element extends Node implements IElement {

this._attributes[name] = attribute;

this._updateDomListIndices();
if (attribute.name === 'class' && this._classList) {
this._classList._updateIndices();
}

if (
this.attributeChangedCallback &&
Expand Down Expand Up @@ -936,7 +938,9 @@ export default class Element extends Node implements IElement {
this.ownerDocument['_cacheID']++;
}

this._updateDomListIndices();
if (attribute.name === 'class' && this._classList) {
this._classList._updateIndices();
}

if (
this.attributeChangedCallback &&
Expand Down Expand Up @@ -1033,13 +1037,4 @@ export default class Element extends Node implements IElement {
}
return name.toLowerCase();
}

/**
* Updates DOM list indices.
*/
protected _updateDomListIndices(): void {
if (this._classList) {
this._classList._updateIndices();
}
}
}
108 changes: 41 additions & 67 deletions packages/happy-dom/src/nodes/html-anchor-element/HTMLAnchorElement.ts
Expand Up @@ -3,6 +3,8 @@ import DOMTokenList from '../../dom-token-list/DOMTokenList';
import IDOMTokenList from '../../dom-token-list/IDOMTokenList';
import IHTMLAnchorElement from './IHTMLAnchorElement';
import { URL } from 'url';
import IAttr from '../attr/IAttr';
import HTMLAnchorElementUtility from './HTMLAnchorElementUtility';

/**
* HTML Anchor Element.
Expand All @@ -14,15 +16,6 @@ export default class HTMLAnchorElement extends HTMLElement implements IHTMLAncho
private _relList: DOMTokenList = null;
private _url: URL | null = null;

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

this._setUrl();
}

/**
* Returns download.
*
Expand All @@ -47,7 +40,6 @@ export default class HTMLAnchorElement extends HTMLElement implements IHTMLAncho
* @returns Hash.
*/
public get hash(): string {
this._reinitializeUrl();
return this._url?.hash ?? '';
}

Expand All @@ -57,8 +49,7 @@ export default class HTMLAnchorElement extends HTMLElement implements IHTMLAncho
* @param hash Hash.
*/
public set hash(hash: string) {
this._reinitializeUrl();
if (this._url) {
if (this._url && !HTMLAnchorElementUtility.isBlobURL(this._url)) {
this._url.hash = hash;
this.setAttributeNS(null, 'href', this._url.toString());
}
Expand All @@ -70,8 +61,6 @@ export default class HTMLAnchorElement extends HTMLElement implements IHTMLAncho
* @returns Href.
*/
public get href(): string | null {
this._reinitializeUrl();

if (this._url) {
return this._url.toString();
}
Expand All @@ -86,8 +75,6 @@ export default class HTMLAnchorElement extends HTMLElement implements IHTMLAncho
*/
public set href(href: string) {
this.setAttributeNS(null, 'href', href);

this._setUrl();
}

/**
Expand All @@ -114,7 +101,6 @@ export default class HTMLAnchorElement extends HTMLElement implements IHTMLAncho
* @returns Origin.
*/
public get origin(): string {
this._reinitializeUrl();
return this._url?.origin ?? '';
}

Expand Down Expand Up @@ -142,7 +128,6 @@ export default class HTMLAnchorElement extends HTMLElement implements IHTMLAncho
* @returns Protocol.
*/
public get protocol(): string {
this._reinitializeUrl();
return this._url?.protocol ?? '';
}

Expand All @@ -152,8 +137,7 @@ export default class HTMLAnchorElement extends HTMLElement implements IHTMLAncho
* @param protocol Protocol.
*/
public set protocol(protocol: string) {
this._reinitializeUrl();
if (this._url) {
if (this._url && !HTMLAnchorElementUtility.isBlobURL(this._url)) {
this._url.protocol = protocol;
this.setAttributeNS(null, 'href', this._url.toString());
}
Expand All @@ -165,7 +149,6 @@ export default class HTMLAnchorElement extends HTMLElement implements IHTMLAncho
* @returns Username.
*/
public get username(): string {
this._reinitializeUrl();
return this._url?.username ?? '';
}

Expand All @@ -175,8 +158,12 @@ export default class HTMLAnchorElement extends HTMLElement implements IHTMLAncho
* @param username Username.
*/
public set username(username: string) {
this._reinitializeUrl();
if (this._url && this._url.host && this._url.protocol != 'file') {
if (
this._url &&
!HTMLAnchorElementUtility.isBlobURL(this._url) &&
this._url.host &&
this._url.protocol != 'file'
) {
this._url.username = username;
this.setAttributeNS(null, 'href', this._url.toString());
}
Expand All @@ -188,7 +175,6 @@ export default class HTMLAnchorElement extends HTMLElement implements IHTMLAncho
* @returns Password.
*/
public get password(): string {
this._reinitializeUrl();
return this._url?.password ?? '';
}

Expand All @@ -198,8 +184,12 @@ export default class HTMLAnchorElement extends HTMLElement implements IHTMLAncho
* @param password Password.
*/
public set password(password: string) {
this._reinitializeUrl();
if (this._url && this._url.host && this._url.protocol != 'file') {
if (
this._url &&
!HTMLAnchorElementUtility.isBlobURL(this._url) &&
this._url.host &&
this._url.protocol != 'file'
) {
this._url.password = password;
this.setAttributeNS(null, 'href', this._url.toString());
}
Expand All @@ -211,7 +201,6 @@ export default class HTMLAnchorElement extends HTMLElement implements IHTMLAncho
* @returns Pathname.
*/
public get pathname(): string {
this._reinitializeUrl();
return this._url?.pathname ?? '';
}

Expand All @@ -221,8 +210,7 @@ export default class HTMLAnchorElement extends HTMLElement implements IHTMLAncho
* @param pathname Pathname.
*/
public set pathname(pathname: string) {
this._reinitializeUrl();
if (this._url) {
if (this._url && !HTMLAnchorElementUtility.isBlobURL(this._url)) {
this._url.pathname = pathname;
this.setAttributeNS(null, 'href', this._url.toString());
}
Expand All @@ -234,7 +222,6 @@ export default class HTMLAnchorElement extends HTMLElement implements IHTMLAncho
* @returns Port.
*/
public get port(): string {
this._reinitializeUrl();
return this._url?.port ?? '';
}

Expand All @@ -244,8 +231,12 @@ export default class HTMLAnchorElement extends HTMLElement implements IHTMLAncho
* @param port Port.
*/
public set port(port: string) {
this._reinitializeUrl();
if (this._url && this._url.host && this._url.protocol != 'file') {
if (
this._url &&
!HTMLAnchorElementUtility.isBlobURL(this._url) &&
this._url.host &&
this._url.protocol != 'file'
) {
this._url.port = port;
this.setAttributeNS(null, 'href', this._url.toString());
}
Expand All @@ -257,7 +248,6 @@ export default class HTMLAnchorElement extends HTMLElement implements IHTMLAncho
* @returns Host.
*/
public get host(): string {
this._reinitializeUrl();
return this._url?.host ?? '';
}

Expand All @@ -267,8 +257,7 @@ export default class HTMLAnchorElement extends HTMLElement implements IHTMLAncho
* @param host Host.
*/
public set host(host: string) {
this._reinitializeUrl();
if (this._url) {
if (this._url && !HTMLAnchorElementUtility.isBlobURL(this._url)) {
this._url.host = host;
this.setAttributeNS(null, 'href', this._url.toString());
}
Expand All @@ -280,7 +269,6 @@ export default class HTMLAnchorElement extends HTMLElement implements IHTMLAncho
* @returns Hostname.
*/
public get hostname(): string {
this._reinitializeUrl();
return this._url?.hostname ?? '';
}

Expand All @@ -290,8 +278,7 @@ export default class HTMLAnchorElement extends HTMLElement implements IHTMLAncho
* @param hostname Hostname.
*/
public set hostname(hostname: string) {
this._reinitializeUrl();
if (this._url) {
if (this._url && !HTMLAnchorElementUtility.isBlobURL(this._url)) {
this._url.hostname = hostname;
this.setAttributeNS(null, 'href', this._url.toString());
}
Expand Down Expand Up @@ -351,7 +338,6 @@ export default class HTMLAnchorElement extends HTMLElement implements IHTMLAncho
* @returns Search.
*/
public get search(): string {
this._reinitializeUrl();
return this._url?.search ?? '';
}

Expand All @@ -361,8 +347,7 @@ export default class HTMLAnchorElement extends HTMLElement implements IHTMLAncho
* @param search Search.
*/
public set search(search: string) {
this._reinitializeUrl();
if (this._url) {
if (this._url && !HTMLAnchorElementUtility.isBlobURL(this._url)) {
this._url.search = search;
this.setAttributeNS(null, 'href', this._url.toString());
}
Expand Down Expand Up @@ -423,43 +408,32 @@ export default class HTMLAnchorElement extends HTMLElement implements IHTMLAncho
}

/**
* Updates DOM list indices.
* @override
*/
protected override _updateDomListIndices(): void {
super._updateDomListIndices();
public override setAttributeNode(attribute: IAttr): IAttr {
const replacedAttribute = super.setAttributeNode(attribute);

if (this._relList) {
if (attribute.name === 'rel' && this._relList) {
this._relList._updateIndices();
}
}

/**
* Reinitialize URL from href attribute
*/
private _reinitializeUrl(): void {
// If element's url is non-null, its scheme is "blob", and it has an opaque path, then terminate these steps.
if (this._url?.protocol === 'blob' && this._url.pathname.length > 1) {
return;
} else if (attribute.name === 'href') {
this._url = HTMLAnchorElementUtility.getUrl(this.ownerDocument, attribute.value);
}

this._setUrl();
return replacedAttribute;
}

/**
* Initialize URL from href attribute
* @override
*/
private _setUrl(): void {
const hrefAttr = this.getAttributeNS(null, 'href');
if (!hrefAttr) {
this._url = null;
}

const documentUrl = this.ownerDocument?.location?.href;
public override removeAttributeNode(attribute: IAttr): IAttr {
super.removeAttributeNode(attribute);

try {
this._url = new URL(hrefAttr.trim(), documentUrl);
} catch (TypeError) {
if (attribute.name === 'rel' && this._relList) {
this._relList._updateIndices();
} else if (attribute.name === 'href') {
this._url = null;
}

return attribute;
}
}
@@ -0,0 +1,48 @@
import IDocument from '../document/IDocument';
import { URL } from 'url';

/**
* HTML Anchor Element utility.
*/
export default class HTMLAnchorElementUtility {
/**
* Returns "true" if it is a blob URL.
*
* According to spec, if element's url is non-null, its scheme is "blob", and it has an opaque path, then the process of updating properties on the URL should be terminated.
*
* @see https://html.spec.whatwg.org/multipage/links.html#reinitialise-url
* @param url
* @param url URL.
* @returns "true" if blob URL.
*/
public static isBlobURL(url: URL): boolean {
return (
url && url.protocol === 'blob:' && url.pathname.length > 1 && url.pathname.includes('://')
);
}

/**
* Returns URL.
*
* @see https://html.spec.whatwg.org/multipage/links.html#dom-hyperlink-href
* @see https://html.spec.whatwg.org/multipage/links.html#hyperlink
* @param document Document.
* @param href Href.
* @returns URL.
*/
public static getUrl(document: IDocument, href: string | null): URL {
if (!href) {
return null;
}

const documentUrl = document.location.href;

try {
return new URL(href.trim(), documentUrl);
} catch (TypeError) {
// Ignore error
}

return null;
}
}

0 comments on commit 15e4296

Please sign in to comment.