mirror of
https://github.com/electron/electron.git
synced 2026-01-08 23:18:06 -05:00
* chore: upgrade Node.js to v24.10.0 * chore: fixup crypto patch * chore: fixup crypto test patch * src: prepare for v8 sandboxing https://github.com/nodejs/node/pull/58376 * esm: fix module.exports export on CJS modules https://github.com/nodejs/node/pull/57366 * chore: fixup lazyload fs patch * esm: Source Phase Imports for WebAssembly https://github.com/nodejs/node/pull/56919 * module: remove --experimental-default-type https://github.com/nodejs/node/pull/56092 * lib,src: refactor assert to load error source from memory https://github.com/nodejs/node/pull/59751 * src: add source location to v8::TaskRunner https://github.com/nodejs/node/pull/54077 * src: remove dependency on wrapper-descriptor-based CppHeap https://github.com/nodejs/node/pull/54077 * src: do not use soon-to-be-deprecated V8 API https://github.com/nodejs/node/pull/53174 * src: stop using deprecated fields of v8::FastApiCallbackOptions https://github.com/nodejs/node/pull/54077 * test: update v8-stats test for V8 12.6 https://github.com/nodejs/node/pull/54077 * esm: unflag --experimental-wasm-modules https://github.com/nodejs/node/pull/57038 * test: adapt assert tests to stack trace changes https://github.com/nodejs/node/pull/58070 * src,test: unregister the isolate after disposal and before freeing https://github.com/nodejs/node/pull/58070 * src: use cppgc to manage ContextifyContext https://github.com/nodejs/node/pull/56522 * src: replace uses of FastApiTypedArray https://github.com/nodejs/node/pull/58070 * module: integrate TypeScript into compile cache https://github.com/nodejs/node/pull/56629 * deps: update ada to 3.2.7 https://github.com/nodejs/node/pull/59336 * src: make minor cleanups in encoding_binding.cc https://github.com/nodejs/node/pull/57448 * src: switch from `Get/SetPrototype` to `Get/SetPrototypeV2` https://github.com/nodejs/node/pull/55453 * src: use non-deprecated Get/SetPrototype methods https://github.com/nodejs/node/pull/59671 * src: simplify string_bytes with views https://github.com/nodejs/node/pull/54876 * src: improve utf8 string generation performance https://github.com/nodejs/node/pull/54873 * src: use non-deprecated Utf8LengthV2() method https://github.com/nodejs/node/pull/58070 * src: use non-deprecated WriteUtf8V2() method https://github.com/nodejs/node/pull/58070 * src: refactor WriteUCS2 and remove flags argument https://github.com/nodejs/node/pull/58163 * src: use String::WriteV2() in TwoByteValue https://github.com/nodejs/node/pull/58164 * node-api: use WriteV2 in napi_get_value_string_utf16 https://github.com/nodejs/node/pull/58165 * node-api: use WriteOneByteV2 in napi_get_value_string_latin1 https://github.com/nodejs/node/pull/58325 * src: migrate WriteOneByte to WriteOneByteV2 https://github.com/nodejs/node/pull/59634 * fs: introduce dirent\.parentPath https://github.com/nodejs/node/pull/50976 * src: avoid copy by using std::views::keys https://github.com/nodejs/node/pull/56080 * chore: fixup patch indices * fix: errant use of context->GetIsolate() * fix: tweak BoringSSL compat patch for new changes * fix: add back missing isolate dtor declaration * fixup! esm: fix module.exports export on CJS modules * cli: remove --no-experimental-fetch flag https://github.com/nodejs/node/pull/52611/files * esm: Source Phase Imports for WebAssembly https://github.com/nodejs/node/pull/56919 * fixup! src: prepare for v8 sandboxing * chore: bump @types/node to v24 * chore: fix const assignment in crypto test * fix: sandbox pointer patch issues * chore: rework source phase import patch * src: add percentage support to --max-old-space-size https://github.com/nodejs/node/pull/59082 * chore: fixup crypto tests * chore: HostImportModuleWithPhaseDynamically todo * fix: cjs esm failures * fix: v8::Object::Wrappable issues -b72a615754-490bac2496-4896a0dd69* chore: remove deleted specs * src: use v8::ExternalMemoryAccounter https://github.com/nodejs/node/pull/58070 * fs: port SonicBoom module to fs module as FastUtf8Stream https://github.com/nodejs/node/pull/58897 * chore: tweak sandboxed pr patch * test: disable parallel/test-os-checked-function * test: use WHATWG URL instead of url.parse * fix: OPENSSL_secure_zalloc doesn't work in BoringSSL * chore: fix accidental extra line * 7017517: [defer-import-eval] Parse import defer syntax https://chromium-review.googlesource.com/c/v8/v8/+/7017517
191 lines
8.0 KiB
TypeScript
191 lines
8.0 KiB
TypeScript
import { ProtocolRequest, session } from 'electron/main';
|
|
|
|
import { createReadStream } from 'fs';
|
|
import { Readable } from 'stream';
|
|
import { ReadableStream } from 'stream/web';
|
|
|
|
import type { ReadableStreamDefaultReader } from 'stream/web';
|
|
|
|
// Global protocol APIs.
|
|
const { registerSchemesAsPrivileged, getStandardSchemes, Protocol } = process._linkedBinding('electron_browser_protocol');
|
|
|
|
const ERR_FAILED = -2;
|
|
const ERR_UNEXPECTED = -9;
|
|
|
|
const isBuiltInScheme = (scheme: string) => ['http', 'https', 'file'].includes(scheme);
|
|
|
|
function makeStreamFromPipe (pipe: any): ReadableStream<Uint8Array> {
|
|
const buf = new Uint8Array(1024 * 1024 /* 1 MB */);
|
|
return new ReadableStream({
|
|
async pull (controller) {
|
|
try {
|
|
const rv = await pipe.read(buf);
|
|
if (rv > 0) {
|
|
controller.enqueue(buf.slice(0, rv));
|
|
} else {
|
|
controller.close();
|
|
}
|
|
} catch (e) {
|
|
controller.error(e);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function makeStreamFromFileInfo ({
|
|
filePath,
|
|
offset = 0,
|
|
length = -1
|
|
}: {
|
|
filePath: string;
|
|
offset?: number;
|
|
length?: number;
|
|
}): ReadableStream<Uint8Array> {
|
|
// Node's Readable.toWeb produces a WHATWG ReadableStream whose chunks are Uint8Array.
|
|
return Readable.toWeb(createReadStream(filePath, {
|
|
start: offset,
|
|
end: length >= 0 ? offset + length : undefined
|
|
})) as ReadableStream<Uint8Array>;
|
|
}
|
|
|
|
function convertToRequestBody (uploadData: ProtocolRequest['uploadData']): RequestInit['body'] {
|
|
if (!uploadData) return null;
|
|
// Optimization: skip creating a stream if the request is just a single buffer.
|
|
if (uploadData.length === 1 && (uploadData[0] as any).type === 'rawData') {
|
|
return uploadData[0].bytes as any;
|
|
}
|
|
|
|
const chunks = [...uploadData] as any[]; // TODO: refine ProtocolRequest types
|
|
// Use Node's web stream types explicitly to avoid DOM lib vs Node lib structural mismatches.
|
|
// Generic <Uint8Array> ensures reader.read() returns value?: Uint8Array consistent with enqueue.
|
|
let current: ReadableStreamDefaultReader<Uint8Array> | null = null;
|
|
return new ReadableStream<Uint8Array>({
|
|
async pull (controller) {
|
|
if (current) {
|
|
const { done, value } = await current.read();
|
|
// (done => value === undefined) as per WHATWG spec
|
|
if (done) {
|
|
current = null;
|
|
return this.pull!(controller);
|
|
} else {
|
|
controller.enqueue(value);
|
|
}
|
|
} else {
|
|
if (!chunks.length) { return controller.close(); }
|
|
const chunk = chunks.shift()!;
|
|
if (chunk.type === 'rawData') {
|
|
controller.enqueue(chunk.bytes as Uint8Array);
|
|
} else if (chunk.type === 'file') {
|
|
current = makeStreamFromFileInfo(chunk).getReader();
|
|
return this.pull!(controller);
|
|
} else if (chunk.type === 'stream') {
|
|
current = makeStreamFromPipe(chunk.body).getReader();
|
|
return this.pull!(controller);
|
|
} else if (chunk.type === 'blob') {
|
|
// Note that even though `getBlobData()` is a `Session` API, it doesn't
|
|
// actually use the `Session` context. Its implementation solely relies
|
|
// on global variables which allows us to implement this feature without
|
|
// knowledge of the `Session` associated with the current request by
|
|
// always pulling `Blob` data out of the default `Session`.
|
|
controller.enqueue(await session.defaultSession.getBlobData(chunk.blobUUID));
|
|
} else {
|
|
throw new Error(`Unknown upload data chunk type: ${chunk.type}`);
|
|
}
|
|
}
|
|
}
|
|
}) as RequestInit['body'];
|
|
}
|
|
|
|
function validateResponse (res: Response) {
|
|
if (!res || typeof res !== 'object') return false;
|
|
|
|
if (res.type === 'error') return true;
|
|
|
|
const exists = (key: string) => Object.hasOwn(res, key);
|
|
|
|
if (exists('status') && typeof res.status !== 'number') return false;
|
|
if (exists('statusText') && typeof res.statusText !== 'string') return false;
|
|
if (exists('headers') && typeof res.headers !== 'object') return false;
|
|
|
|
if (exists('body')) {
|
|
if (typeof res.body !== 'object') return false;
|
|
if (res.body !== null && !(res.body instanceof ReadableStream)) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
Protocol.prototype.handle = function (this: Electron.Protocol, scheme: string, handler: (req: Request) => Response | Promise<Response>) {
|
|
const register = isBuiltInScheme(scheme) ? this.interceptProtocol : this.registerProtocol;
|
|
const success = register.call(this, scheme, async (preq: ProtocolRequest, cb: any) => {
|
|
try {
|
|
const body = convertToRequestBody(preq.uploadData);
|
|
const headers = new Headers(preq.headers);
|
|
if (headers.get('origin') === 'null') {
|
|
headers.delete('origin');
|
|
}
|
|
const req = new Request(preq.url, {
|
|
headers,
|
|
method: preq.method,
|
|
referrer: preq.referrer,
|
|
body,
|
|
duplex: body instanceof ReadableStream ? 'half' : undefined
|
|
} as any);
|
|
const res = await handler(req);
|
|
if (!validateResponse(res)) {
|
|
return cb({ error: ERR_UNEXPECTED });
|
|
} else if (res.type === 'error') {
|
|
cb({ error: ERR_FAILED });
|
|
} else {
|
|
cb({
|
|
data: res.body ? Readable.fromWeb(res.body as ReadableStream<ArrayBufferView>) : null,
|
|
headers: res.headers ? Object.fromEntries(res.headers) : {},
|
|
statusCode: res.status,
|
|
statusText: res.statusText,
|
|
mimeType: (res as any).__original_resp?._responseHead?.mimeType
|
|
});
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
cb({ error: ERR_UNEXPECTED });
|
|
}
|
|
});
|
|
if (!success) throw new Error(`Failed to register protocol: ${scheme}`);
|
|
};
|
|
|
|
Protocol.prototype.unhandle = function (this: Electron.Protocol, scheme: string) {
|
|
const unregister = isBuiltInScheme(scheme) ? this.uninterceptProtocol : this.unregisterProtocol;
|
|
if (!unregister.call(this, scheme)) { throw new Error(`Failed to unhandle protocol: ${scheme}`); }
|
|
};
|
|
|
|
Protocol.prototype.isProtocolHandled = function (this: Electron.Protocol, scheme: string) {
|
|
const isRegistered = isBuiltInScheme(scheme) ? this.isProtocolIntercepted : this.isProtocolRegistered;
|
|
return isRegistered.call(this, scheme);
|
|
};
|
|
|
|
const protocol = {
|
|
registerSchemesAsPrivileged,
|
|
getStandardSchemes,
|
|
registerStringProtocol: (...args) => session.defaultSession.protocol.registerStringProtocol(...args),
|
|
registerBufferProtocol: (...args) => session.defaultSession.protocol.registerBufferProtocol(...args),
|
|
registerStreamProtocol: (...args) => session.defaultSession.protocol.registerStreamProtocol(...args),
|
|
registerFileProtocol: (...args) => session.defaultSession.protocol.registerFileProtocol(...args),
|
|
registerHttpProtocol: (...args) => session.defaultSession.protocol.registerHttpProtocol(...args),
|
|
registerProtocol: (...args) => session.defaultSession.protocol.registerProtocol(...args),
|
|
unregisterProtocol: (...args) => session.defaultSession.protocol.unregisterProtocol(...args),
|
|
isProtocolRegistered: (...args) => session.defaultSession.protocol.isProtocolRegistered(...args),
|
|
interceptStringProtocol: (...args) => session.defaultSession.protocol.interceptStringProtocol(...args),
|
|
interceptBufferProtocol: (...args) => session.defaultSession.protocol.interceptBufferProtocol(...args),
|
|
interceptStreamProtocol: (...args) => session.defaultSession.protocol.interceptStreamProtocol(...args),
|
|
interceptFileProtocol: (...args) => session.defaultSession.protocol.interceptFileProtocol(...args),
|
|
interceptHttpProtocol: (...args) => session.defaultSession.protocol.interceptHttpProtocol(...args),
|
|
interceptProtocol: (...args) => session.defaultSession.protocol.interceptProtocol(...args),
|
|
uninterceptProtocol: (...args) => session.defaultSession.protocol.uninterceptProtocol(...args),
|
|
isProtocolIntercepted: (...args) => session.defaultSession.protocol.isProtocolIntercepted(...args),
|
|
handle: (...args) => session.defaultSession.protocol.handle(...args),
|
|
unhandle: (...args) => session.defaultSession.protocol.unhandle(...args),
|
|
isProtocolHandled: (...args) => session.defaultSession.protocol.isProtocolHandled(...args)
|
|
} as typeof Electron.protocol;
|
|
|
|
export default protocol;
|