Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
const onElectron = searchParams.get('platform') === 'electron';
const disableServiceWorker = searchParams.has('disableServiceWorker');
const expectedWorkerVersion = parseInt(searchParams.get('swVersion'));
/** @type {MessageChannel | undefined} */
let outerIframeMessageChannel;

/**
* Use polling to track focus of main webview and iframes within the webview
Expand Down Expand Up @@ -230,7 +232,7 @@
return reject(new Error('Service Workers are not enabled. Webviews will not work. Try disabling private/incognito mode.'));
}

const swPath = encodeURI(`service-worker.js?v=${expectedWorkerVersion}&vscode-resource-base-authority=${searchParams.get('vscode-resource-base-authority')}&id=${ID}&remoteAuthority=${searchParams.get('remoteAuthority') ?? ''}`);
const swPath = encodeURI(`service-worker.js?v=${expectedWorkerVersion}&vscode-resource-base-authority=${searchParams.get('vscode-resource-base-authority')}&remoteAuthority=${searchParams.get('remoteAuthority') ?? ''}`);
navigator.serviceWorker.register(swPath)
.then(async registration => {
/**
Expand Down Expand Up @@ -259,7 +261,8 @@
navigator.serviceWorker.addEventListener('message', versionHandler);

const postVersionMessage = (/** @type {ServiceWorker} */ controller) => {
controller.postMessage({ channel: 'version' });
outerIframeMessageChannel = new MessageChannel();
controller.postMessage({ channel: 'version' }, [outerIframeMessageChannel.port2]);
};

// At this point, either the service worker is ready and
Expand Down Expand Up @@ -1166,6 +1169,17 @@

unloadMonitor.onIframeLoaded(newFrame);
}

if (!disableServiceWorker && outerIframeMessageChannel) {
outerIframeMessageChannel.port1.onmessage = event => {
switch (event.data.channel) {
case 'load-resource':
case 'load-localhost':
hostMessaging.postMessage(event.data.channel, event.data);
return;
}
};
}
});

// propagate vscode-context-menu-visible class
Expand Down
20 changes: 17 additions & 3 deletions src/vs/workbench/contrib/webview/browser/pre/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<meta charset="UTF-8">

<meta http-equiv="Content-Security-Policy"
content="default-src 'none'; script-src 'sha256-nlLyDpnjtftJG2xvXh2vuy77l7xFTjfOz7Jnj1iXNmA=' 'self'; frame-src 'self'; style-src 'unsafe-inline';">
content="default-src 'none'; script-src 'sha256-8TXwJF4lf6BC+0Kve/BD7lzDnK4M7vmJ802hpOYmdlg=' 'self'; frame-src 'self'; style-src 'unsafe-inline';">


<!-- Disable pinch zooming -->
Expand Down Expand Up @@ -36,6 +36,8 @@
const onElectron = searchParams.get('platform') === 'electron';
const disableServiceWorker = searchParams.has('disableServiceWorker');
const expectedWorkerVersion = parseInt(searchParams.get('swVersion'));
/** @type {MessageChannel | undefined} */
let outerIframeMessageChannel;

/**
* Use polling to track focus of main webview and iframes within the webview
Expand Down Expand Up @@ -236,7 +238,7 @@
return reject(new Error('Service Workers are not enabled. Webviews will not work. Try disabling private/incognito mode.'));
}

const swPath = encodeURI(`service-worker.js?v=${expectedWorkerVersion}&vscode-resource-base-authority=${searchParams.get('vscode-resource-base-authority')}&id=${ID}&remoteAuthority=${searchParams.get('remoteAuthority') ?? ''}`);
const swPath = encodeURI(`service-worker.js?v=${expectedWorkerVersion}&vscode-resource-base-authority=${searchParams.get('vscode-resource-base-authority')}&remoteAuthority=${searchParams.get('remoteAuthority') ?? ''}`);
navigator.serviceWorker.register(swPath)
.then(async registration => {
/**
Expand Down Expand Up @@ -265,7 +267,8 @@
navigator.serviceWorker.addEventListener('message', versionHandler);

const postVersionMessage = (/** @type {ServiceWorker} */ controller) => {
controller.postMessage({ channel: 'version' });
outerIframeMessageChannel = new MessageChannel();
controller.postMessage({ channel: 'version' }, [outerIframeMessageChannel.port2]);
};

// At this point, either the service worker is ready and
Expand Down Expand Up @@ -1203,6 +1206,17 @@

unloadMonitor.onIframeLoaded(newFrame);
}

if (!disableServiceWorker && outerIframeMessageChannel) {
outerIframeMessageChannel.port1.onmessage = event => {
switch (event.data.channel) {
case 'load-resource':
case 'load-localhost':
hostMessaging.postMessage(event.data.channel, event.data);
return;
}
};
}
});

// propagate vscode-context-menu-visible class
Expand Down
92 changes: 56 additions & 36 deletions src/vs/workbench/contrib/webview/browser/pre/service-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const searchParams = new URL(location.toString()).searchParams;

const remoteAuthority = searchParams.get('remoteAuthority');

const ID = searchParams.get('id');
let outerIframeMessagePort: MessagePort | undefined;

/**
* Origin used for resources
Expand Down Expand Up @@ -103,6 +103,7 @@ sw.addEventListener('message', async (event: ExtendableMessageEvent) => {
const source = event.source as Client;
switch (event.data.channel) {
case 'version': {
outerIframeMessagePort = event.ports[0];
sw.clients.get(source.id).then(client => {
if (client) {
client.postMessage({
Expand Down Expand Up @@ -202,21 +203,24 @@ async function processResourceRequest(
event: FetchEvent,
requestUrlComponents: ResourceRequestUrlComponents
): Promise<Response> {
const client = await sw.clients.get(event.clientId);
let webviewId: string | null | undefined;
let client = await sw.clients.get(event.clientId);
if (!client) {
const workerClient = await getWorkerClientForId(event.clientId);
if (!workerClient) {
client = await getWorkerClientForId(event.clientId);
if (!client) {
console.error('Could not find inner client for request');
return notFound();
} else {
webviewId = getWebviewIdForClient(workerClient);
}
} else {
webviewId = getWebviewIdForClient(client);
}

if (!webviewId) {
const webviewId = getWebviewIdForClient(client);

// Refs https://github.com/microsoft/vscode/issues/244143
// With PlzDedicatedWorker, worker subresources and blob wokers
// will use clients different from the window client.
// Since we cannot different a worker main resource from a worker subresource
// we will use message channel to the outer iframe provided at the time
// of service worker controller version initialization.
if (!webviewId && client.type !== 'worker' && client.type !== 'sharedworker') {
console.error('Could not resolve webview id');
return notFound();
}
Expand Down Expand Up @@ -321,12 +325,6 @@ async function processResourceRequest(
return response.clone();
};

const parentClients = await getOuterIframeClient(webviewId);
if (!parentClients.length) {
console.log('Could not find parent client for request');
return notFound();
}

let cached: Response | undefined;
if (shouldTryCaching) {
const cache = await caches.open(resourceCacheName);
Expand All @@ -335,8 +333,26 @@ async function processResourceRequest(

const { requestId, promise } = resourceRequestStore.create();

for (const parentClient of parentClients) {
parentClient.postMessage({
if (webviewId) {
const parentClients = await getOuterIframeClient(webviewId);
if (!parentClients.length) {
console.log('Could not find parent client for request');
return notFound();
}

for (const parentClient of parentClients) {
parentClient.postMessage({
channel: 'load-resource',
id: requestId,
scheme: requestUrlComponents.scheme,
authority: requestUrlComponents.authority,
path: requestUrlComponents.path,
query: requestUrlComponents.query,
ifNoneMatch: cached?.headers.get('ETag'),
});
}
} else if (client.type === 'worker' || client.type === 'sharedworker') {
outerIframeMessagePort?.postMessage({
channel: 'load-resource',
id: requestId,
scheme: requestUrlComponents.scheme,
Expand All @@ -361,7 +377,13 @@ async function processLocalhostRequest(
return fetch(event.request);
}
const webviewId = getWebviewIdForClient(client);
if (!webviewId) {
// Refs https://github.com/microsoft/vscode/issues/244143
// With PlzDedicatedWorker, worker subresources and blob wokers
// will use clients different from the window client.
// Since we cannot different a worker main resource from a worker subresource
// we will use message channel to the outer iframe provided at the time
// of service worker controller version initialization.
if (!webviewId && client.type !== 'worker' && client.type !== 'sharedworker') {
console.error('Could not resolve webview id');
return fetch(event.request);
}
Expand All @@ -385,15 +407,22 @@ async function processLocalhostRequest(
});
};

const parentClients = await getOuterIframeClient(webviewId);
if (!parentClients.length) {
console.log('Could not find parent client for request');
return notFound();
}

const { requestId, promise } = localhostRequestStore.create();
for (const parentClient of parentClients) {
parentClient.postMessage({
if (webviewId) {
const parentClients = await getOuterIframeClient(webviewId);
if (!parentClients.length) {
console.log('Could not find parent client for request');
return notFound();
}
for (const parentClient of parentClients) {
parentClient.postMessage({
channel: 'load-localhost',
origin: origin,
id: requestId,
});
}
} else if (client.type === 'worker' || client.type === 'sharedworker') {
outerIframeMessagePort?.postMessage({
channel: 'load-localhost',
origin: origin,
id: requestId,
Expand All @@ -404,15 +433,6 @@ async function processLocalhostRequest(
}

function getWebviewIdForClient(client: Client): string | null {
// Refs https://github.com/microsoft/vscode/issues/244143
// With PlzDedicatedWorker, worker subresources and blob wokers
// will use clients different from the window client.
// Since we cannot different a worker main resource from a worker subresource
// we will use the global webview ID passed in at the time of
// service worker registration.
if (client.type === 'worker' || client.type === 'sharedworker') {
return ID;
}
const requesterClientUrl = new URL(client.url);
return requesterClientUrl.searchParams.get('id');
}
Expand Down
Loading