Get expected service worker version from renderer instead of main.js
Due to caching, it is possible that a webview ends up running an older service worker than the current renderer version. This can be triggered if a user has webview open, updates vs code (to a version that makes service worker related changes), and then has the webview restored. Depending on what was changed in the service worker, this could potentially break the webview Previously we tried to avoid this by checking a version number stored in `main.js`. However I don't think this is reliable as `main.js` can also be cached. The fix here to check the sw version against an expected version from the renderer. In code, these two should always be kept in sync
This commit is contained in:
parent
3bfa3455a6
commit
676340ffad
4 changed files with 17 additions and 7 deletions
|
@ -83,6 +83,8 @@ namespace WebviewState {
|
|||
|
||||
export abstract class BaseWebview<T extends HTMLElement> extends Disposable {
|
||||
|
||||
protected readonly _expectedServiceWorkerVersion = 2; // Keep this in sync with the version in service-worker.js
|
||||
|
||||
private _element: T | undefined;
|
||||
protected get element(): T | undefined { return this._element; }
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ const isSafari = navigator.vendor && navigator.vendor.indexOf('Apple') > -1 &&
|
|||
|
||||
const searchParams = new URL(location.toString()).searchParams;
|
||||
const ID = searchParams.get('id');
|
||||
const expectedWorkerVersion = parseInt(searchParams.get('swVersion'));
|
||||
|
||||
/**
|
||||
* Use polling to track focus of main webview and iframes within the webview
|
||||
|
@ -210,16 +211,16 @@ const workerReady = new Promise(async (resolve, reject) => {
|
|||
return reject(new Error('Service Workers are not enabled in browser. Webviews will not work.'));
|
||||
}
|
||||
|
||||
const expectedWorkerVersion = 2;
|
||||
const swPath = `service-worker.js${self.location.search}`;
|
||||
|
||||
navigator.serviceWorker.register(`service-worker.js${self.location.search}`).then(
|
||||
navigator.serviceWorker.register(swPath).then(
|
||||
async registration => {
|
||||
await navigator.serviceWorker.ready;
|
||||
|
||||
/**
|
||||
* @param {MessageEvent} event
|
||||
*/
|
||||
const versionHandler = (event) => {
|
||||
const versionHandler = async (event) => {
|
||||
if (event.data.channel !== 'version') {
|
||||
return;
|
||||
}
|
||||
|
@ -228,10 +229,16 @@ const workerReady = new Promise(async (resolve, reject) => {
|
|||
if (event.data.version === expectedWorkerVersion) {
|
||||
return resolve();
|
||||
} else {
|
||||
// If we have the wrong version, try once to unregister and re-register
|
||||
return registration.update()
|
||||
console.log(`Found unexpected service worker version. Found: ${event.data.version}. Expected: ${expectedWorkerVersion}`);
|
||||
console.log(`Attempting to reload service worker`);
|
||||
|
||||
// If we have the wrong version, try once (and only once) to unregister and re-register
|
||||
// Note that `.update` doesn't seem to work desktop electron at the moment so we use
|
||||
// `unregister` and `register` here.
|
||||
return registration.unregister()
|
||||
.then(() => navigator.serviceWorker.register(swPath))
|
||||
.then(() => navigator.serviceWorker.ready)
|
||||
.finally(resolve);
|
||||
.finally(() => { resolve(); });
|
||||
}
|
||||
};
|
||||
navigator.serviceWorker.addEventListener('message', versionHandler);
|
||||
|
|
|
@ -100,6 +100,7 @@ export class IFrameWebview extends BaseWebview<HTMLIFrameElement> implements Web
|
|||
protected initElement(extension: WebviewExtensionDescription | undefined, options: WebviewOptions, extraParams?: { [key: string]: string }) {
|
||||
const params: { [key: string]: string } = {
|
||||
id: this.id,
|
||||
swVersion: String(this._expectedServiceWorkerVersion),
|
||||
extensionId: extension?.id.value ?? '', // The extensionId and purpose in the URL are used for filtering in js-debug:
|
||||
...extraParams,
|
||||
'vscode-resource-base-authority': this.webviewRootResourceAuthority,
|
||||
|
|
|
@ -159,7 +159,7 @@ export class ElectronWebviewBasedWebview extends BaseWebview<WebviewTag> impleme
|
|||
// and not the `vscode-file` URI because preload scripts are loaded
|
||||
// via node.js from the main side and only allow `file:` protocol
|
||||
this.element!.preload = FileAccess.asFileUri('./pre/electron-index.js', require).toString(true);
|
||||
this.element!.src = `${Schemas.vscodeWebview}://${this.id}/electron-browser-index.html?platform=electron&id=${this.id}&vscode-resource-base-authority=${encodeURIComponent(this.webviewRootResourceAuthority)}`;
|
||||
this.element!.src = `${Schemas.vscodeWebview}://${this.id}/electron-browser-index.html?platform=electron&id=${this.id}&vscode-resource-base-authority=${encodeURIComponent(this.webviewRootResourceAuthority)}&swVersion=${this._expectedServiceWorkerVersion}`;
|
||||
}
|
||||
|
||||
protected createElement(options: WebviewOptions) {
|
||||
|
|
Loading…
Reference in a new issue