Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Register service worker before spawning the worker thread #1606

Merged
merged 9 commits into from
Jul 15, 2024
2 changes: 1 addition & 1 deletion packages/php-wasm/web/src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export type { LoaderOptions as PHPWebLoaderOptions } from './load-runtime';

export { loadWebRuntime } from './load-runtime';
export { getPHPLoaderModule } from './get-php-loader-module';
export { registerServiceWorker } from './register-service-worker';
export { registerServiceWorker, setPhpApi } from './register-service-worker';
export { setupPostMessageRelay } from './setup-post-message-relay';

export { spawnPHPWorkerThread } from './worker-thread/spawn-php-worker-thread';
Expand Down
28 changes: 23 additions & 5 deletions packages/php-wasm/web/src/lib/register-service-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@ import { PhpWasmError } from '@php-wasm/util';
import { responseTo } from '@php-wasm/web-service-worker';
import { Remote } from 'comlink';

export interface Client extends Remote<PHPWorker> {}

let phpApi: Client;

/**
* Sets the PHP API client.
*
* @param {Client} api The PHP API client.
*/
export function setPhpApi(api: Client) {
phpApi = api;
}

/**
* Run this in the main application to register the service worker or
* reload the registered worker if the app expects a different version
Expand All @@ -13,11 +26,7 @@ import { Remote } from 'comlink';
* mismatched with the actual version, the service worker
* will be re-registered.
*/
export async function registerServiceWorker<Client extends Remote<PHPWorker>>(
phpApi: Client,
scope: string,
scriptUrl: string
) {
export async function registerServiceWorker(scope: string, scriptUrl: string) {
const sw = navigator.serviceWorker;
if (!sw) {
/**
Expand Down Expand Up @@ -45,6 +54,13 @@ export async function registerServiceWorker<Client extends Remote<PHPWorker>>(
// the update:
await registration.update();

registration.addEventListener('worker-api', (event: CustomEventInit) => {
if (!event.detail.workerApi) {
return;
}
phpApi = event.detail.workerApi;
});

// Proxy the service worker messages to the web worker:
navigator.serviceWorker.addEventListener(
'message',
Expand All @@ -69,4 +85,6 @@ export async function registerServiceWorker<Client extends Remote<PHPWorker>>(
);

sw.startMessages();

return registration;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export async function spawnPHPWorkerThread(
workerUrl: string,
startupOptions: Record<string, string | string[]> = {}
) {
workerUrl = addQueryParams(workerUrl, startupOptions);
const worker = new Worker(workerUrl, { type: 'module' });
return new Promise<Worker>((resolve, reject) => {
worker.onerror = (e) => {
Expand All @@ -21,6 +20,10 @@ export async function spawnPHPWorkerThread(
(error as any).filename = e.filename;
reject(error);
};
worker.postMessage({
type: 'startup-options',
startupOptions,
});
// There is no way to know when the worker script has started
// executing, so we use a message to signal that.
function onStartup(event: { data: string }) {
Expand All @@ -32,23 +35,3 @@ export async function spawnPHPWorkerThread(
worker.addEventListener('message', onStartup);
});
}

function addQueryParams(
url: string | URL,
searchParams: Record<string, string | string[]>
): string {
if (!Object.entries(searchParams).length) {
return url + '';
}
const urlWithOptions = new URL(url);
for (const [key, value] of Object.entries(searchParams)) {
if (Array.isArray(value)) {
for (const item of value) {
urlWithOptions.searchParams.append(key, item);
}
} else {
urlWithOptions.searchParams.set(key, value);
}
}
return urlWithOptions.toString();
}
38 changes: 21 additions & 17 deletions packages/playground/remote/src/lib/boot-playground-remote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
} from '@php-wasm/universal';
import {
registerServiceWorker,
setPhpApi,
spawnPHPWorkerThread,
exposeAPI,
consumeAPI,
Expand Down Expand Up @@ -71,7 +72,11 @@ export async function bootPlaygroundRemote() {
);
const withNetworking = query.get('networking') === 'yes';
const sapiName = query.get('sapi-name') || undefined;
const workerApi = consumeAPI<PlaygroundWorkerEndpoint>(

const scope = Math.random().toFixed(16);
await registerServiceWorker(scope, serviceWorkerUrl + '');

const phpApi = consumeAPI<PlaygroundWorkerEndpoint>(
await spawnPHPWorkerThread(workerUrl, {
wpVersion,
phpVersion,
Expand All @@ -80,25 +85,28 @@ export async function bootPlaygroundRemote() {
storage: query.get('storage') || '',
...(sapiName ? { sapiName } : {}),
'site-slug': query.get('site-slug') || 'wordpress',
scope,
})
);
// Set PHP API in the service worker
setPhpApi(phpApi);

const wpFrame = document.querySelector('#wp') as HTMLIFrameElement;
const webApi: WebClientMixin = {
async onDownloadProgress(fn) {
return workerApi.onDownloadProgress(fn);
return phpApi.onDownloadProgress(fn);
},
async journalFSEvents(root: string, callback) {
return workerApi.journalFSEvents(root, callback);
return phpApi.journalFSEvents(root, callback);
},
async replayFSJournal(events: FilesystemOperation[]) {
return workerApi.replayFSJournal(events);
return phpApi.replayFSJournal(events);
},
async addEventListener(event, listener) {
return await workerApi.addEventListener(event, listener);
return await phpApi.addEventListener(event, listener);
},
async removeEventListener(event, listener) {
return await workerApi.removeEventListener(event, listener);
return await phpApi.removeEventListener(event, listener);
},
async setProgress(options: ProgressBarOptions) {
if (!bar) {
Expand Down Expand Up @@ -183,7 +191,7 @@ export async function bootPlaygroundRemote() {
* @returns
*/
async onMessage(callback: MessageListener) {
return await workerApi.onMessage(callback);
return await phpApi.onMessage(callback);
},
/**
* Ditto for this function.
Expand All @@ -195,11 +203,11 @@ export async function bootPlaygroundRemote() {
options: BindOpfsOptions,
onProgress?: SyncProgressCallback
) {
return await workerApi.bindOpfs(options, onProgress);
return await phpApi.bindOpfs(options, onProgress);
},
};

await workerApi.isConnected();
await phpApi.isConnected();

// If onDownloadProgress is not explicitly re-exposed here,
// Comlink will throw an error and claim the callback
Expand All @@ -208,22 +216,18 @@ export async function bootPlaygroundRemote() {
// https://github.com/GoogleChromeLabs/comlink/issues/426#issuecomment-578401454
// @TODO: Handle the callback conversion automatically and don't explicitly re-expose
// the onDownloadProgress method
const [setAPIReady, setAPIError, playground] = exposeAPI(webApi, workerApi);
const [setAPIReady, setAPIError, playground] = exposeAPI(webApi, phpApi);

try {
await workerApi.isReady();
await registerServiceWorker(
workerApi,
await workerApi.scope,
serviceWorkerUrl + ''
);
await phpApi.isReady();

setupPostMessageRelay(
wpFrame,
getOrigin((await playground.absoluteUrl)!)
);
setupMountListener(playground);
if (withNetworking) {
await setupFetchNetworkTransport(workerApi);
await setupFetchNetworkTransport(phpApi);
}

setAPIReady();
Expand Down
2 changes: 1 addition & 1 deletion packages/playground/remote/src/lib/worker-thread.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import {
import { wpVersionToStaticAssetsDirectory } from '@wp-playground/wordpress-builds';
import { logger } from '@php-wasm/logger';

const scope = Math.random().toFixed(16);
const scope = startupOptions.scope;

// post message to parent
self.postMessage('worker-script-started');
Expand Down
34 changes: 22 additions & 12 deletions packages/playground/remote/src/lib/worker-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export type ReceivedStartupOptions = {
storage?: string;
phpExtensions?: string[];
siteSlug?: string;
scope?: string;
};

export type ParsedStartupOptions = {
Expand All @@ -30,20 +31,28 @@ export type ParsedStartupOptions = {
storage: string;
phpExtensions: string[];
siteSlug: string;
scope: string;
};

export const receivedParams: ReceivedStartupOptions = {};
const url = self?.location?.href;
if (typeof url !== 'undefined') {
const params = new URL(self.location.href).searchParams;
receivedParams.wpVersion = params.get('wpVersion') || undefined;
receivedParams.phpVersion = params.get('phpVersion') || undefined;
receivedParams.storage = params.get('storage') || undefined;
// Default to CLI to support the WP-CLI Blueprint step
receivedParams.sapiName = params.get('sapiName') || 'cli';
receivedParams.phpExtensions = params.getAll('php-extension');
receivedParams.siteSlug = params.get('site-slug') || undefined;
}
const getReceivedParams = async () => {
return new Promise<ReceivedStartupOptions>((resolve) => {
self.addEventListener('message', async (event) => {
if (event.data.type === 'startup-options') {
resolve({
wpVersion: event.data.startupOptions.wpVersion,
phpVersion: event.data.startupOptions.phpVersion,
storage: event.data.startupOptions.storage,
sapiName: event.data.startupOptions.sapiName,
phpExtensions: event.data.startupOptions['php-extension'],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to make these names consistent one day

siteSlug: event.data.startupOptions['site-slug'],
scope: event.data.startupOptions.scope,
});
}
});
});
};

const receivedParams: ReceivedStartupOptions = await getReceivedParams();

export const requestedWPVersion = receivedParams.wpVersion || '';
export const startupOptions = {
Expand All @@ -59,6 +68,7 @@ export const startupOptions = {
storage: receivedParams.storage || 'local',
phpExtensions: receivedParams.phpExtensions || [],
siteSlug: receivedParams.siteSlug,
scope: receivedParams.scope || '',
} as ParsedStartupOptions;

export const downloadMonitor = new EmscriptenDownloadMonitor();
Expand Down
Loading