fix: fetch-dependent interfaces in Web Workers (#42579)

This commit is contained in:
Shelley Vohr 2024-06-20 17:01:50 +02:00 коммит произвёл GitHub
Родитель edb939ae80
Коммит 37608933ae
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
6 изменённых файлов: 101 добавлений и 13 удалений

Просмотреть файл

@ -17,6 +17,14 @@ const { makeRequireFunction } = __non_webpack_require__('internal/modules/helper
global.module = new Module('electron/js2c/worker_init');
global.require = makeRequireFunction(global.module);
// See WebWorkerObserver::WorkerScriptReadyForEvaluation.
if ((globalThis as any).blinkfetch) {
const keys = ['fetch', 'Response', 'FormData', 'Request', 'Headers'];
for (const key of keys) {
(globalThis as any)[key] = (globalThis as any)[`blink${key}`];
}
}
// Set the __filename to the path of html file if it is file: protocol.
// NB. 'self' isn't defined in an AudioWorklet.
if (typeof self !== 'undefined' && self.location.protocol === 'file:') {

Просмотреть файл

@ -66,9 +66,25 @@ void WebWorkerObserver::WorkerScriptReadyForEvaluation(
std::shared_ptr<node::Environment> env =
node_bindings_->CreateEnvironment(worker_context, nullptr);
// We need to use the Blink implementation of fetch in web workers
// Node.js deletes the global fetch function when their fetch implementation
// is disabled, so we need to save and re-add it after the Node.js environment
// is loaded. See corresponding change in node/init.ts.
v8::Local<v8::Object> global = worker_context->Global();
v8::Local<v8::String> fetch_string = gin::StringToV8(env->isolate(), "fetch");
v8::MaybeLocal<v8::Value> fetch = global->Get(worker_context, fetch_string);
std::vector<std::string> keys = {"fetch", "Response", "FormData", "Request",
"Headers"};
for (const auto& key : keys) {
v8::MaybeLocal<v8::Value> value =
global->Get(worker_context, gin::StringToV8(isolate, key.c_str()));
if (!value.IsEmpty()) {
std::string blink_key = "blink" + key;
global
->Set(worker_context, gin::StringToV8(isolate, blink_key.c_str()),
value.ToLocalChecked())
.Check();
}
}
// Add Electron extended APIs.
electron_bindings_->BindTo(env->isolate(), env->process_object());
@ -76,16 +92,6 @@ void WebWorkerObserver::WorkerScriptReadyForEvaluation(
// Load everything.
node_bindings_->LoadEnvironment(env.get());
// We need to use the Blink implementation of fetch in WebWorker process
// Node.js deletes the global fetch function when their fetch implementation
// is disabled, so we need to save and re-add it after the Node.js environment
// is loaded.
if (!fetch.IsEmpty()) {
worker_context->Global()
->Set(worker_context, fetch_string, fetch.ToLocalChecked())
.Check();
}
// Make uv loop being wrapped by window context.
node_bindings_->set_uv_env(env.get());

Просмотреть файл

@ -1019,12 +1019,35 @@ describe('chromium features', () => {
});
it('Worker has node integration with nodeIntegrationInWorker', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, nodeIntegrationInWorker: true, contextIsolation: false } });
const w = new BrowserWindow({
show: false,
webPreferences: {
nodeIntegration: true,
nodeIntegrationInWorker: true,
contextIsolation: false
}
});
w.loadURL(`file://${fixturesPath}/pages/worker.html`);
const [, data] = await once(ipcMain, 'worker-result');
expect(data).to.equal('object function object function');
});
it('Worker has access to fetch-dependent interfaces with nodeIntegrationInWorker', async () => {
const w = new BrowserWindow({
show: false,
webPreferences: {
nodeIntegration: true,
nodeIntegrationInWorker: true,
contextIsolation: false
}
});
w.loadURL(`file://${fixturesPath}/pages/worker-fetch.html`);
const [, data] = await once(ipcMain, 'worker-fetch-result');
expect(data).to.equal('function function function function function');
});
describe('SharedWorker', () => {
it('can work', async () => {
const w = new BrowserWindow({ show: false });

12
spec/fixtures/pages/worker-fetch.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,12 @@
<html>
<body>
<script type="text/javascript" charset="utf-8">
const { ipcRenderer } = require('electron')
let worker = new Worker(`../workers/worker_node_fetch.js`)
worker.onmessage = function (event) {
ipcRenderer.send('worker-fetch-result', event.data)
worker.terminate()
}
</script>
</body>
</html>

7
spec/fixtures/workers/worker_node_fetch.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,7 @@
self.postMessage([
typeof fetch,
typeof Response,
typeof Request,
typeof Headers,
typeof FormData
].join(' '));

Просмотреть файл

@ -159,6 +159,38 @@ describe('node feature', () => {
});
});
describe('fetch', () => {
itremote('works correctly when nodeIntegration is enabled in the renderer', async (fixtures: string) => {
const file = require('node:path').join(fixtures, 'hello.txt');
expect(() => {
fetch('file://' + file);
}).to.not.throw();
expect(() => {
const formData = new FormData();
formData.append('username', 'Groucho');
}).not.to.throw();
expect(() => {
const request = new Request('https://example.com', {
method: 'POST',
body: JSON.stringify({ foo: 'bar' })
});
expect(request.method).to.equal('POST');
}).not.to.throw();
expect(() => {
const response = new Response('Hello, world!');
expect(response.status).to.equal(200);
}).not.to.throw();
expect(() => {
const headers = new Headers();
headers.append('Content-Type', 'text/xml');
}).not.to.throw();
}, [fixtures]);
});
it('does not hang when using the fs module in the renderer process', async () => {
const appPath = path.join(mainFixturesPath, 'apps', 'libuv-hang', 'main.js');
const appProcess = childProcess.spawn(process.execPath, [appPath], {