mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-10 18:19:03 +00:00
LibWeb: Implement TextEncoderStream
Required by the server-side rendering mode of React Router, used by https://chatgpt.com/ Note that the imported tests do not have the worker variants to prevent freezing on macOS.
This commit is contained in:
parent
24d5f24749
commit
cae0ee6fa7
Notes:
github-actions[bot]
2025-02-07 16:05:51 +00:00
Author: https://github.com/Lubrsi
Commit: cae0ee6fa7
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3481
Reviewed-by: https://github.com/trflynn89 ✅
36 changed files with 1375 additions and 0 deletions
|
@ -0,0 +1,151 @@
|
|||
// testharness file with ShadowRealm utilities to be imported in the realm
|
||||
// hosting the ShadowRealm
|
||||
|
||||
/**
|
||||
* Convenience function for evaluating some async code in the ShadowRealm and
|
||||
* waiting for the result.
|
||||
*
|
||||
* In case of error, this function intentionally exposes the stack trace (if it
|
||||
* is available) to the hosting realm, for debugging purposes.
|
||||
*
|
||||
* @param {ShadowRealm} realm - the ShadowRealm to evaluate the code in
|
||||
* @param {string} asyncBody - the code to evaluate; will be put in the body of
|
||||
* an async function, and must return a value explicitly if a value is to be
|
||||
* returned to the hosting realm.
|
||||
*/
|
||||
globalThis.shadowRealmEvalAsync = function (realm, asyncBody) {
|
||||
return new Promise(realm.evaluate(`
|
||||
(resolve, reject) => {
|
||||
(async () => {
|
||||
${asyncBody}
|
||||
})().then(resolve, (e) => reject(e.toString() + "\\n" + (e.stack || "")));
|
||||
}
|
||||
`));
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenience adaptor function for fetch() that can be passed to
|
||||
* setShadowRealmGlobalProperties() (see testharness-shadowrealm-inner.js).
|
||||
* Used to adapt the hosting realm's fetch(), if present, to fetch a resource
|
||||
* and pass its text through the callable boundary to the ShadowRealm.
|
||||
*/
|
||||
globalThis.fetchAdaptor = (resource) => (resolve, reject) => {
|
||||
fetch(resource)
|
||||
.then(res => res.text())
|
||||
.then(resolve, (e) => reject(e.toString()));
|
||||
};
|
||||
|
||||
let workerMessagePortPromise;
|
||||
/**
|
||||
* Used when the hosting realm is a worker. This value is a Promise that
|
||||
* resolves to a function that posts a message to the worker's message port,
|
||||
* just like postMessage(). The message port is only available asynchronously in
|
||||
* SharedWorkers and ServiceWorkers.
|
||||
*/
|
||||
globalThis.getPostMessageFunc = async function () {
|
||||
if (typeof postMessage === "function") {
|
||||
return postMessage; // postMessage available directly in dedicated worker
|
||||
}
|
||||
|
||||
if (workerMessagePortPromise) {
|
||||
return await workerMessagePortPromise;
|
||||
}
|
||||
|
||||
throw new Error("getPostMessageFunc is intended for Worker scopes");
|
||||
}
|
||||
|
||||
// Port available asynchronously in shared worker, but not via an async func
|
||||
let savedResolver;
|
||||
if (globalThis.constructor.name === "SharedWorkerGlobalScope") {
|
||||
workerMessagePortPromise = new Promise((resolve) => {
|
||||
savedResolver = resolve;
|
||||
});
|
||||
addEventListener("connect", function (event) {
|
||||
const port = event.ports[0];
|
||||
savedResolver(port.postMessage.bind(port));
|
||||
});
|
||||
} else if (globalThis.constructor.name === "ServiceWorkerGlobalScope") {
|
||||
workerMessagePortPromise = new Promise((resolve) => {
|
||||
savedResolver = resolve;
|
||||
});
|
||||
addEventListener("message", (e) => {
|
||||
if (typeof e.data === "object" && e.data !== null && e.data.type === "connect") {
|
||||
const client = e.source;
|
||||
savedResolver(client.postMessage.bind(client));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Used when the hosting realm does not permit dynamic import, e.g. in
|
||||
* ServiceWorkers or AudioWorklets. Requires an adaptor function such as
|
||||
* fetchAdaptor() above, or an equivalent if fetch() is not present in the
|
||||
* hosting realm.
|
||||
*
|
||||
* @param {ShadowRealm} realm - the ShadowRealm in which to setup a
|
||||
* fakeDynamicImport() global function.
|
||||
* @param {function} adaptor - an adaptor function that does what fetchAdaptor()
|
||||
* does.
|
||||
*/
|
||||
globalThis.setupFakeDynamicImportInShadowRealm = function(realm, adaptor) {
|
||||
function fetchModuleTextExecutor(url) {
|
||||
return (resolve, reject) => {
|
||||
new Promise(adaptor(url))
|
||||
.then(text => realm.evaluate(text + ";\nundefined"))
|
||||
.then(resolve, (e) => reject(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
realm.evaluate(`
|
||||
(fetchModuleTextExecutor) => {
|
||||
globalThis.fakeDynamicImport = function (url) {
|
||||
return new Promise(fetchModuleTextExecutor(url));
|
||||
}
|
||||
}
|
||||
`)(fetchModuleTextExecutor);
|
||||
};
|
||||
|
||||
/**
|
||||
* Used when the hosting realm does not expose fetch(), i.e. in worklets. The
|
||||
* port on the other side of the channel needs to send messages starting with
|
||||
* 'fetchRequest::' and listen for messages starting with 'fetchResult::'. See
|
||||
* testharness-shadowrealm-audioworkletprocessor.js.
|
||||
*
|
||||
* @param {port} MessagePort - the message port on which to listen for fetch
|
||||
* requests
|
||||
*/
|
||||
globalThis.setupFakeFetchOverMessagePort = function (port) {
|
||||
port.addEventListener("message", (event) => {
|
||||
if (typeof event.data !== "string" || !event.data.startsWith("fetchRequest::")) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(event.data.slice("fetchRequest::".length))
|
||||
.then(res => res.text())
|
||||
.then(
|
||||
text => port.postMessage(`fetchResult::success::${text}`),
|
||||
error => port.postMessage(`fetchResult::fail::${error}`),
|
||||
);
|
||||
});
|
||||
port.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a message suitable for posting with postMessage() that will signal to
|
||||
* the test harness that the tests are finished and there was an error in the
|
||||
* setup code.
|
||||
*
|
||||
* @param {message} any - error
|
||||
*/
|
||||
globalThis.createSetupErrorResult = function (message) {
|
||||
return {
|
||||
type: "complete",
|
||||
tests: [],
|
||||
asserts: [],
|
||||
status: {
|
||||
status: 1, // TestsStatus.ERROR,
|
||||
message: String(message),
|
||||
stack: typeof message === "object" && message !== null && "stack" in message ? message.stack : undefined,
|
||||
},
|
||||
};
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue