Skip to content

Commit

Permalink
chore: [#1101] Continues on implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
capricorn86 committed Feb 29, 2024
1 parent 7c48e4e commit 6e64250
Show file tree
Hide file tree
Showing 22 changed files with 669 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export default class BrowserFrameNavigator {
formData?: FormData;
}): Promise<IResponse | null> {
const { windowClass, frame, url, formData, method, goToOptions } = options;
const referrer = goToOptions?.referrer || frame.window.location.origin;
const targetURL = BrowserFrameURL.getRelativeURL(frame, url);

if (!frame.window) {
Expand Down Expand Up @@ -105,8 +106,8 @@ export default class BrowserFrameNavigator {
(<IBrowserWindow>frame.window) = new windowClass(frame, { url: targetURL.href, width, height });
(<number>frame.window.devicePixelRatio) = devicePixelRatio;

if (goToOptions?.referrer) {
frame.window.document[PropertySymbol.referrer] = goToOptions.referrer;
if (referrer) {
frame.window.document[PropertySymbol.referrer] = referrer;
}

if (targetURL.protocol === 'about:') {
Expand Down Expand Up @@ -139,8 +140,8 @@ export default class BrowserFrameNavigator {

try {
response = await frame.window.fetch(targetURL.href, {
referrer: goToOptions?.referrer,
referrerPolicy: goToOptions?.referrerPolicy,
referrer,
referrerPolicy: goToOptions?.referrerPolicy || 'origin',
signal: abortController.signal,
method: method || (formData ? 'POST' : 'GET'),
headers: goToOptions?.hard ? { 'Cache-Control': 'no-cache' } : undefined,
Expand Down
142 changes: 142 additions & 0 deletions packages/happy-dom/src/config/IElementTagNameMap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import IHTMLAnchorElement from '../nodes/html-anchor-element/IHTMLAnchorElement.js';
import IHTMLElement from '../nodes/html-element/IHTMLElement.js';
import IHTMLAudioElement from '../nodes/html-audio-element/IHTMLAudioElement.js';
import IHTMLBaseElement from '../nodes/html-base-element/IHTMLBaseElement.js';
import IHTMLTemplateElement from '../nodes/html-template-element/IHTMLTemplateElement.js';
import IHTMLFormElement from '../nodes/html-form-element/IHTMLFormElement.js';
import IHTMLInputElement from '../nodes/html-input-element/IHTMLInputElement.js';
import IHTMLTextAreaElement from '../nodes/html-text-area-element/IHTMLTextAreaElement.js';
import IHTMLScriptElement from '../nodes/html-script-element/IHTMLScriptElement.js';
import IHTMLImageElement from '../nodes/html-image-element/IHTMLImageElement.js';
import IHTMLLinkElement from '../nodes/html-link-element/IHTMLLinkElement.js';
import IHTMLStyleElement from '../nodes/html-style-element/IHTMLStyleElement.js';
import IHTMLLabelElement from '../nodes/html-label-element/IHTMLLabelElement.js';
import IHTMLSlotElement from '../nodes/html-slot-element/IHTMLSlotElement.js';
import IHTMLMetaElement from '../nodes/html-meta-element/IHTMLMetaElement.js';
import IHTMLButtonElement from '../nodes/html-button-element/IHTMLButtonElement.js';
import IHTMLDialogElement from '../nodes/html-dialog-element/IHTMLDialogElement.js';
import IHTMLIFrameElement from '../nodes/html-iframe-element/IHTMLIFrameElement.js';
import IHTMLOptGroupElement from '../nodes/html-opt-group-element/IHTMLOptGroupElement.js';
import IHTMLOptionElement from '../nodes/html-option-element/IHTMLOptionElement.js';
import IHTMLSelectElement from '../nodes/html-select-element/IHTMLSelectElement.js';
import IHTMLVideoElement from '../nodes/html-video-element/IHTMLVideoElement.js';

export default interface IElementTagNameMap {
a: IHTMLAnchorElement;
abbr: IHTMLElement;
address: IHTMLElement;
area: IHTMLElement;
article: IHTMLElement;
aside: IHTMLElement;
audio: IHTMLAudioElement;
b: IHTMLElement;
base: IHTMLBaseElement;
bdi: IHTMLElement;
bdo: IHTMLElement;
blockquaote: IHTMLElement;
body: IHTMLElement;
template: IHTMLTemplateElement;
form: IHTMLFormElement;
input: IHTMLInputElement;
textarea: IHTMLTextAreaElement;
script: IHTMLScriptElement;
img: IHTMLImageElement;
link: IHTMLLinkElement;
style: IHTMLStyleElement;
label: IHTMLLabelElement;
slot: IHTMLSlotElement;
meta: IHTMLMetaElement;
blockquote: IHTMLElement;
br: IHTMLElement;
button: IHTMLButtonElement;
canvas: IHTMLElement;
caption: IHTMLElement;
cite: IHTMLElement;
code: IHTMLElement;
col: IHTMLElement;
colgroup: IHTMLElement;
data: IHTMLElement;
datalist: IHTMLElement;
dd: IHTMLElement;
del: IHTMLElement;
details: IHTMLElement;
dfn: IHTMLElement;
dialog: IHTMLDialogElement;
div: IHTMLElement;
dl: IHTMLElement;
dt: IHTMLElement;
em: IHTMLElement;
embed: IHTMLElement;
fieldset: IHTMLElement;
figcaption: IHTMLElement;
figure: IHTMLElement;
footer: IHTMLElement;
h1: IHTMLElement;
h2: IHTMLElement;
h3: IHTMLElement;
h4: IHTMLElement;
h5: IHTMLElement;
h6: IHTMLElement;
head: IHTMLElement;
header: IHTMLElement;
hgroup: IHTMLElement;
hr: IHTMLElement;
html: IHTMLElement;
i: IHTMLElement;
iframe: IHTMLIFrameElement;
ins: IHTMLElement;
kbd: IHTMLElement;
legend: IHTMLElement;
li: IHTMLElement;
main: IHTMLElement;
map: IHTMLElement;
mark: IHTMLElement;
math: IHTMLElement;
menu: IHTMLElement;
menuitem: IHTMLElement;
meter: IHTMLElement;
nav: IHTMLElement;
noscript: IHTMLElement;
object: IHTMLElement;
ol: IHTMLElement;
optgroup: IHTMLOptGroupElement;
option: IHTMLOptionElement;
output: IHTMLElement;
p: IHTMLElement;
param: IHTMLElement;
picture: IHTMLElement;
pre: IHTMLElement;
progress: IHTMLElement;
q: IHTMLElement;
rb: IHTMLElement;
rp: IHTMLElement;
rt: IHTMLElement;
rtc: IHTMLElement;
ruby: IHTMLElement;
s: IHTMLElement;
samp: IHTMLElement;
section: IHTMLElement;
select: IHTMLSelectElement;
small: IHTMLElement;
source: IHTMLElement;
span: IHTMLElement;
strong: IHTMLElement;
sub: IHTMLElement;
summary: IHTMLElement;
sup: IHTMLElement;
table: IHTMLElement;
tbody: IHTMLElement;
td: IHTMLElement;
tfoot: IHTMLElement;
th: IHTMLElement;
thead: IHTMLElement;
time: IHTMLElement;
title: IHTMLElement;
tr: IHTMLElement;
track: IHTMLElement;
u: IHTMLElement;
ul: IHTMLElement;
var: IHTMLElement;
video: IHTMLVideoElement;
wbr: IHTMLElement;
}
67 changes: 67 additions & 0 deletions packages/happy-dom/src/config/ISVGElementTagNameMap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import ISVGSVGElement from '../nodes/svg-element/ISVGSVGElement.js';
import ISVGElement from '../nodes/svg-element/ISVGElement.js';

export default interface ISVGElementTagNameMap {
svg: ISVGSVGElement;
animate: ISVGElement;
animateMotion: ISVGElement;
animateTransform: ISVGElement;
circle: ISVGElement;
clipPath: ISVGElement;
defs: ISVGElement;
desc: ISVGElement;
ellipse: ISVGElement;
feBlend: ISVGElement;
feColorMatrix: ISVGElement;
feComponentTransfer: ISVGElement;
feComposite: ISVGElement;
feConvolveMatrix: ISVGElement;
feDiffuseLighting: ISVGElement;
feDisplacementMap: ISVGElement;
feDistantLight: ISVGElement;
feDropShadow: ISVGElement;
feFlood: ISVGElement;
feFuncA: ISVGElement;
feFuncB: ISVGElement;
feFuncG: ISVGElement;
feFuncR: ISVGElement;
feGaussianBlur: ISVGElement;
feImage: ISVGElement;
feMerge: ISVGElement;
feMergeNode: ISVGElement;
feMorphology: ISVGElement;
feOffset: ISVGElement;
fePointLight: ISVGElement;
feSpecularLighting: ISVGElement;
feSpotLight: ISVGElement;
feTile: ISVGElement;
feTurbulence: ISVGElement;
filter: ISVGElement;
foreignObject: ISVGElement;
g: ISVGElement;
image: ISVGElement;
line: ISVGElement;
linearGradient: ISVGElement;
marker: ISVGElement;
mask: ISVGElement;
metadata: ISVGElement;
mpath: ISVGElement;
path: ISVGElement;
pattern: ISVGElement;
polygon: ISVGElement;
polyline: ISVGElement;
radialGradient: ISVGElement;
rect: ISVGElement;
script: ISVGElement;
set: ISVGElement;
stop: ISVGElement;
style: ISVGElement;
switch: ISVGElement;
symbol: ISVGElement;
text: ISVGElement;
textPath: ISVGElement;
title: ISVGElement;
tspan: ISVGElement;
use: ISVGElement;
view: ISVGElement;
}
6 changes: 3 additions & 3 deletions packages/happy-dom/src/fetch/Fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export default class Fetch {
* @returns Response.
*/
public async send(): Promise<IResponse> {
FetchRequestReferrerUtility.prepareRequest(this.#window.location, this.request);
FetchRequestReferrerUtility.prepareRequest(new URL(this.#window.location.href), this.request);
FetchRequestValidationUtility.validateSchema(this.request);

if (this.request.signal.aborted) {
Expand Down Expand Up @@ -247,7 +247,7 @@ export default class Fetch {
private async compliesWithCrossOriginPolicy(): Promise<boolean> {
if (
this.disableCrossOriginPolicy ||
!FetchCORSUtility.isCORS(this.#window.location, this.request[PropertySymbol.url])
!FetchCORSUtility.isCORS(this.#window.location.href, this.request[PropertySymbol.url])
) {
return true;
}
Expand Down Expand Up @@ -714,7 +714,7 @@ export default class Fetch {
if (
this.request.credentials === 'omit' ||
(this.request.credentials === 'same-origin' &&
FetchCORSUtility.isCORS(this.#window.location, locationURL))
FetchCORSUtility.isCORS(this.#window.location.href, locationURL))
) {
headers.delete('authorization');
headers.delete('www-authenticate');
Expand Down
6 changes: 3 additions & 3 deletions packages/happy-dom/src/fetch/SyncFetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export default class SyncFetch {
* @returns Response.
*/
public send(): ISyncResponse {
FetchRequestReferrerUtility.prepareRequest(this.#window.location, this.request);
FetchRequestReferrerUtility.prepareRequest(new URL(this.#window.location.href), this.request);
FetchRequestValidationUtility.validateSchema(this.request);

if (this.request.signal.aborted) {
Expand Down Expand Up @@ -231,7 +231,7 @@ export default class SyncFetch {
private compliesWithCrossOriginPolicy(): boolean {
if (
this.disableCrossOriginPolicy ||
!FetchCORSUtility.isCORS(this.#window.location, this.request[PropertySymbol.url])
!FetchCORSUtility.isCORS(this.#window.location.href, this.request[PropertySymbol.url])
) {
return true;
}
Expand Down Expand Up @@ -512,7 +512,7 @@ export default class SyncFetch {
if (
this.request.credentials === 'omit' ||
(this.request.credentials === 'same-origin' &&
FetchCORSUtility.isCORS(this.#window.location, locationURL))
FetchCORSUtility.isCORS(this.#window.location.href, locationURL))
) {
headers.delete('authorization');
headers.delete('www-authenticate');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Headers from '../Headers.js';
import Request from '../Request.js';
import IHeaders from '../types/IHeaders.js';
import FetchCORSUtility from './FetchCORSUtility.js';
import { URL } from 'url';

const FORBIDDEN_HEADER_NAMES = [
'accept-charset',
Expand Down Expand Up @@ -77,10 +78,8 @@ export default class FetchRequestHeaderUtility {
request: Request;
}): { [key: string]: string } {
const headers = new Headers(options.request.headers);
const isCORS = FetchCORSUtility.isCORS(
options.window.location,
options.request[PropertySymbol.url]
);
const originURL = new URL(options.window.location.href);
const isCORS = FetchCORSUtility.isCORS(originURL, options.request[PropertySymbol.url]);

// TODO: Maybe we need to add support for OPTIONS request with 'Access-Control-Allow-*' headers?
if (
Expand All @@ -107,7 +106,7 @@ export default class FetchRequestHeaderUtility {
(options.request.credentials === 'same-origin' && !isCORS)
) {
const cookies = options.browserFrame.page.context.cookieContainer.getCookies(
options.window.location,
originURL,
false
);
if (cookies.length > 0) {
Expand Down
4 changes: 2 additions & 2 deletions packages/happy-dom/src/form-data/FormData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export default class FormData implements Iterable<[string, string | File]> {
this.append(node.name, file);
}
}
} else {
} else if (node.value) {
this.append(node.name, node.value);
}
}
Expand All @@ -73,7 +73,7 @@ export default class FormData implements Iterable<[string, string | File]> {
*
* @param callback Callback.
*/
public forEach(callback: (key: string, value: string | File, thisArg: FormData) => void): void {
public forEach(callback: (value: string | File, key: string, thisArg: FormData) => void): void {
for (const entry of this.#entries) {
callback.call(this, entry.value, entry.name, this);
}
Expand Down
17 changes: 13 additions & 4 deletions packages/happy-dom/src/nodes/document-fragment/DocumentFragment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import ElementUtility from '../element/ElementUtility.js';
import HTMLCollection from '../element/HTMLCollection.js';
import INodeList from '../node/INodeList.js';
import NodeTypeEnum from '../node/NodeTypeEnum.js';
import IElementTagNameMap from '../../config/IElementTagNameMap.js';
import ISVGElementTagNameMap from '../../config/ISVGElementTagNameMap.js';

/**
* DocumentFragment.
Expand Down Expand Up @@ -113,22 +115,29 @@ export default class DocumentFragment extends Node implements IDocumentFragment
}

/**
* Query CSS selector to find matching nodes.
* Query CSS selector to find matching elments.
*
* @param selector CSS selector.
* @returns Matching elements.
*/
public querySelectorAll(selector: string): INodeList<IElement> {
public querySelectorAll<
E extends keyof IElementTagNameMap,
S extends keyof ISVGElementTagNameMap
>(
selector: E | S | string
): INodeList<IElementTagNameMap[E] | ISVGElementTagNameMap[S] | IElement> {
return QuerySelector.querySelectorAll(this, selector);
}

/**
* Query CSS Selector to find matching node.
* Query CSS Selector to find a matching element.
*
* @param selector CSS selector.
* @returns Matching element.
*/
public querySelector(selector: string): IElement {
public querySelector<E extends keyof IElementTagNameMap, S extends keyof ISVGElementTagNameMap>(
selector: E | S | string
): IElementTagNameMap[E] | ISVGElementTagNameMap[S] | IElement | null {
return QuerySelector.querySelector(this, selector);
}

Expand Down
Loading

0 comments on commit 6e64250

Please sign in to comment.