mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
fix: extension service workers not starting beyond first app launch (#50611)
* fix: extension service worker not starting beyond first app launch * fix: set preference only for extensions with service workers
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
#include "base/time/time.h"
|
||||
#include "content/public/browser/browser_context.h"
|
||||
#include "extensions/browser/extension_file_task_runner.h"
|
||||
#include "extensions/browser/extension_pref_names.h"
|
||||
#include "extensions/browser/extension_prefs.h"
|
||||
#include "extensions/browser/extension_registry.h"
|
||||
#include "extensions/browser/pref_names.h"
|
||||
@@ -27,6 +28,7 @@
|
||||
#include "extensions/common/error_utils.h"
|
||||
#include "extensions/common/file_util.h"
|
||||
#include "extensions/common/manifest_constants.h"
|
||||
#include "extensions/common/manifest_handlers/background_info.h"
|
||||
|
||||
namespace extensions {
|
||||
|
||||
@@ -143,6 +145,19 @@ void ElectronExtensionLoader::FinishExtensionLoad(
|
||||
std::pair<scoped_refptr<const Extension>, std::string> result) {
|
||||
scoped_refptr<const Extension> extension = result.first;
|
||||
if (extension) {
|
||||
ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(browser_context_);
|
||||
|
||||
if (BackgroundInfo::IsServiceWorkerBased(extension.get())) {
|
||||
// Tell Chromium that it needs to start the extension's service worker.
|
||||
// Chromium usually does this only when an extension is first installed
|
||||
// because Chrome will restart the service worker when the browser
|
||||
// relaunches. In Electron, we make a fresh install on every app start,
|
||||
// so we need to run the fresh install logic again.
|
||||
extension_prefs->UpdateExtensionPref(
|
||||
extension.get()->id(), extensions::kPrefHasStartedServiceWorker,
|
||||
base::Value(false));
|
||||
}
|
||||
|
||||
extension_registrar_->AddExtension(extension);
|
||||
|
||||
// Write extension install time to ExtensionPrefs.
|
||||
@@ -152,7 +167,6 @@ void ElectronExtensionLoader::FinishExtensionLoad(
|
||||
// Implementation for writing the pref was based on
|
||||
// PreferenceAPIBase::SetExtensionControlledPref.
|
||||
{
|
||||
ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(browser_context_);
|
||||
ExtensionPrefs::ScopedDictionaryUpdate update(
|
||||
extension_prefs, extension.get()->id(),
|
||||
extensions::pref_names::kPrefPreferences);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { app, session, webFrameMain, BrowserWindow, ipcMain, WebContents, Extension, Session } from 'electron/main';
|
||||
import { app, session, webFrameMain, BrowserWindow, ipcMain, WebContents, Extension, Session, ServiceWorkerInfo, ServiceWorkersRunningStatusChangedEventParams } from 'electron/main';
|
||||
|
||||
import { expect } from 'chai';
|
||||
import * as WebSocket from 'ws';
|
||||
@@ -10,7 +10,7 @@ import * as http from 'node:http';
|
||||
import * as path from 'node:path';
|
||||
|
||||
import { emittedNTimes, emittedUntil } from './lib/events-helpers';
|
||||
import { ifit, listen, waitUntil } from './lib/spec-helpers';
|
||||
import { ifit, listen, startRemoteControlApp, waitUntil } from './lib/spec-helpers';
|
||||
import { expectWarningMessages } from './lib/warning-helpers';
|
||||
import { closeAllWindows, closeWindow, cleanupWebContents } from './lib/window-helpers';
|
||||
|
||||
@@ -816,6 +816,99 @@ describe('chrome extensions', () => {
|
||||
expect(scope).equals(extension.url);
|
||||
});
|
||||
|
||||
it('launches background service worker', async () => {
|
||||
const customSession = session.fromPartition(`persist:${uuid.v4()}`);
|
||||
const launchPromise = new Promise<void>(resolve => {
|
||||
customSession.serviceWorkers.on('running-status-changed', ({ runningStatus }) => {
|
||||
if (runningStatus === 'running') resolve();
|
||||
});
|
||||
});
|
||||
const extension = await customSession.extensions.loadExtension(path.join(fixtures, 'extensions', 'mv3-service-worker'));
|
||||
await launchPromise;
|
||||
const serviceWorkers = customSession.serviceWorkers.getAllRunning();
|
||||
expect(Object.values(serviceWorkers).some(worker => worker.scope === extension.url)).equals(true);
|
||||
});
|
||||
|
||||
it('launches background service worker when the extension is loaded again without restarting the app', async () => {
|
||||
const customSession = session.fromPartition(`persist:${uuid.v4()}`);
|
||||
const extensionPath = path.join(fixtures, 'extensions', 'mv3-service-worker');
|
||||
|
||||
const isWorkerRunning = (extension: Extension) => {
|
||||
const serviceWorkers = customSession.serviceWorkers.getAllRunning();
|
||||
return Object.values(serviceWorkers).some(worker => worker.scope === extension.url);
|
||||
};
|
||||
|
||||
const loadAndUnloadExtension = async () => {
|
||||
const launchPromise = new Promise<void>(resolve => {
|
||||
customSession.serviceWorkers.on('running-status-changed', ({ runningStatus }) => {
|
||||
if (runningStatus === 'running') resolve();
|
||||
});
|
||||
});
|
||||
const extension = await customSession.extensions.loadExtension(extensionPath);
|
||||
await launchPromise;
|
||||
expect(isWorkerRunning(extension)).equals(true);
|
||||
|
||||
const stopPromise = new Promise<void>(resolve => {
|
||||
customSession.serviceWorkers.on('running-status-changed', ({ runningStatus }) => {
|
||||
if (runningStatus === 'stopped') resolve();
|
||||
});
|
||||
});
|
||||
customSession.extensions.removeExtension(extension.id);
|
||||
await stopPromise;
|
||||
expect(isWorkerRunning(extension)).equals(false);
|
||||
};
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
await loadAndUnloadExtension();
|
||||
}
|
||||
});
|
||||
|
||||
// Regression test for https://github.com/electron/electron/issues/41613
|
||||
it('launches background service worker after restarting the app', async () => {
|
||||
const partition = `persist:${uuid.v4()}`;
|
||||
const extensionPath = path.join(fixtures, 'extensions', 'mv3-service-worker');
|
||||
|
||||
const runRemoteControlApp = async () => {
|
||||
const rc = await startRemoteControlApp();
|
||||
|
||||
const exitPromise = once(rc.process, 'exit');
|
||||
|
||||
const { workerScopes, extensionUrl } = await rc.remotely(async (partition: string, extensionPath: string) => {
|
||||
const { session } = require('electron/main');
|
||||
const { setTimeout } = require('node:timers/promises');
|
||||
|
||||
const customSession = session.fromPartition(partition);
|
||||
|
||||
const launchPromise = new Promise<void>(resolve => {
|
||||
customSession.serviceWorkers.on('running-status-changed', ({ runningStatus }: ServiceWorkersRunningStatusChangedEventParams) => {
|
||||
if (runningStatus === 'running') resolve();
|
||||
});
|
||||
});
|
||||
const extension = await customSession.extensions.loadExtension(extensionPath);
|
||||
await launchPromise;
|
||||
const serviceWorkers = customSession.serviceWorkers.getAllRunning();
|
||||
|
||||
const workerScopes = Object.values(serviceWorkers).map(worker => (worker as ServiceWorkerInfo).scope);
|
||||
const extensionUrl = extension.url;
|
||||
|
||||
// Give Chromium some time to update extensions::kPrefHasStartedServiceWorker on disk
|
||||
await setTimeout(500);
|
||||
|
||||
global.setTimeout(() => require('electron').app.quit());
|
||||
|
||||
return { workerScopes, extensionUrl };
|
||||
}, partition, extensionPath);
|
||||
|
||||
await exitPromise;
|
||||
|
||||
expect(workerScopes.includes(extensionUrl)).equals(true);
|
||||
};
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
await runRemoteControlApp();
|
||||
}
|
||||
});
|
||||
|
||||
it('can run chrome extension APIs', async () => {
|
||||
const customSession = session.fromPartition(`persist:${uuid.v4()}`);
|
||||
const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } });
|
||||
|
||||
Reference in New Issue
Block a user