import type BrowserWindow from '../window/BrowserWindow.js'; import URL from '../url/URL.js'; import Fetch from './Fetch.js'; import SyncFetch from './SyncFetch.js'; import type { TRequestCredentials } from './types/TRequestCredentials.js'; import WindowBrowserContext from '../window/WindowBrowserContext.js'; import PreloadUtility from './preload/PreloadUtility.js'; import * as PropertySymbol from '../PropertySymbol.js'; import type { TRequestReferrerPolicy } from './types/TRequestReferrerPolicy.js'; import type IResourceFetchResponse from './types/IResourceFetchResponse.js'; /** * Helper class for performing fetch of resources. */ export default class ResourceFetch { private window: BrowserWindow; /** * Constructor. * * @param window Window. */ constructor(window: BrowserWindow) { this.window = window; } /** * Returns resource data asynchronously. * * @param url URL. * @param destination Destination. * @param [options] * @param [options.credentials] Credentials. * @param options.referrerPolicy * @returns Response. */ public async fetch( url: string | URL, destination: 'script' | 'style' | 'module', options?: { credentials?: TRequestCredentials; referrerPolicy?: TRequestReferrerPolicy } ): Promise { const browserFrame = new WindowBrowserContext(this.window).getBrowserFrame(); if (!browserFrame) { return { content: '', virtualServerFile: null }; } // Preloaded resource if (destination === 'script' || destination === 'style') { const preloadKey = PreloadUtility.getKey({ url: String(url), destination, mode: 'cors', credentialsMode: options?.credentials || 'same-origin' }); const preloadEntry = this.window.document[PropertySymbol.preloads].get(preloadKey); if (preloadEntry) { this.window.document[PropertySymbol.preloads].delete(preloadKey); const response = preloadEntry.response || (await preloadEntry.onResponseAvailable()); if (response && !response.ok) { throw new this.window.DOMException( `Failed to perform request to "${ new URL(url, this.window.location.href).href }". Status ${preloadEntry.response?.status || '0'} ${preloadEntry.response?.statusText || 'Unknown'}.` ); } return { content: preloadEntry.response?.[PropertySymbol.buffer]?.toString() || '', virtualServerFile: preloadEntry.response?.[PropertySymbol.virtualServerFile] || null }; } } const fetch = new Fetch({ browserFrame, window: this.window, url, disableSameOriginPolicy: destination === 'script' || destination === 'style', disablePreload: true, init: { credentials: options?.credentials, referrerPolicy: options?.referrerPolicy } }); const response = await fetch.send(); if (!response.ok) { throw new this.window.DOMException( `Failed to perform request to "${new URL(url, this.window.location.href).href}". Status ${ response.status } ${response.statusText}.` ); } return { content: await response.text(), virtualServerFile: response[PropertySymbol.virtualServerFile] || null }; } /** * Returns resource data synchronously. * * @param url URL. * @param destination Destination. * @param [options] Options. * @param [options.credentials] Credentials. * @param [options.referrerPolicy] Referrer policy. * @returns Response. */ public fetchSync( url: string, destination: 'script' | 'style' | 'module', options?: { credentials?: TRequestCredentials; referrerPolicy?: TRequestReferrerPolicy } ): IResourceFetchResponse { const browserFrame = new WindowBrowserContext(this.window).getBrowserFrame(); if (!browserFrame) { return { content: '', virtualServerFile: null }; } // Preloaded resource if (destination === 'script' || destination === 'style') { const preloadKey = PreloadUtility.getKey({ url: String(url), destination, mode: 'cors', credentialsMode: options?.credentials || 'same-origin' }); const preloadEntry = this.window.document[PropertySymbol.preloads].get(preloadKey); // We will only use this if the fetch for the resource is complete as it is async and this request is sync. if (preloadEntry && preloadEntry.response) { this.window.document[PropertySymbol.preloads].delete(preloadKey); const response = preloadEntry.response; if (!response.ok) { throw new this.window.DOMException( `Failed to perform request to "${ new URL(url, this.window.location.href).href }". Status ${preloadEntry.response.status} ${preloadEntry.response.statusText}.` ); } return { content: preloadEntry.response?.[PropertySymbol.buffer]?.toString() || '', virtualServerFile: preloadEntry.response?.[PropertySymbol.virtualServerFile] || null }; } } const fetch = new SyncFetch({ browserFrame, window: this.window, url, disableSameOriginPolicy: true, init: { credentials: options?.credentials, referrerPolicy: options?.referrerPolicy } }); const response = fetch.send(); if (!response.ok) { throw new this.window.DOMException( `Failed to perform request to "${new URL(url, this.window.location.href).href}". Status ${ response.status } ${response.statusText}.` ); } return { content: response.body?.toString() || '', virtualServerFile: response[PropertySymbol.virtualServerFile] || null }; } }