From 98067bcadf493b9dc612bd098c1e7ce2e977673b Mon Sep 17 00:00:00 2001 From: DenizUgur Date: Wed, 22 Jan 2025 11:54:06 -0800 Subject: [PATCH 1/6] fix: deduplicate `wxt:content-script-started` > This fix prevents premature context invalidation > fixes #1359 --- .../wxt/src/client/content-scripts/content-script-context.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/wxt/src/client/content-scripts/content-script-context.ts b/packages/wxt/src/client/content-scripts/content-script-context.ts index 62f5c9a8e..d6e4a6c05 100644 --- a/packages/wxt/src/client/content-scripts/content-script-context.ts +++ b/packages/wxt/src/client/content-scripts/content-script-context.ts @@ -42,6 +42,7 @@ export class ContentScriptContext implements AbortController { private isTopFrame = window.self === window.top; private abortController: AbortController; private locationWatcher = createLocationWatcher(this); + private receivedTimestamps = new Set(); constructor( private readonly contentScriptName: string, @@ -246,6 +247,10 @@ export class ContentScriptContext implements AbortController { event.data?.type === ContentScriptContext.SCRIPT_STARTED_MESSAGE_TYPE && event.data?.contentScriptName === this.contentScriptName ) { + // Deduplicate messages + if (this.receivedTimestamps.has(event.timeStamp)) return; + this.receivedTimestamps.add(event.timeStamp); + const wasFirst = isFirst; isFirst = false; if (wasFirst && options?.ignoreFirstEvent) return; From 99c870748125d798235f167b41f42dc431a63bd8 Mon Sep 17 00:00:00 2001 From: DenizUgur Date: Mon, 27 Jan 2025 10:34:22 -0800 Subject: [PATCH 2/6] fix: use uuid for deduplicating `wxt:content-script-started` --- packages/wxt/package.json | 1 + .../content-scripts/content-script-context.ts | 8 +++--- pnpm-lock.yaml | 25 +++++++++++++------ 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/packages/wxt/package.json b/packages/wxt/package.json index 5ee5239c5..852af6d4f 100644 --- a/packages/wxt/package.json +++ b/packages/wxt/package.json @@ -123,6 +123,7 @@ "publish-browser-extension": "^2.2.2", "scule": "^1.3.0", "unimport": "^3.13.1", + "uuid": "^11.0.5", "vite": "^5.0.0 || <=6.0.8", "vite-node": "^2.1.4", "web-ext-run": "^0.2.1", diff --git a/packages/wxt/src/client/content-scripts/content-script-context.ts b/packages/wxt/src/client/content-scripts/content-script-context.ts index d6e4a6c05..014881d1c 100644 --- a/packages/wxt/src/client/content-scripts/content-script-context.ts +++ b/packages/wxt/src/client/content-scripts/content-script-context.ts @@ -1,5 +1,6 @@ import { ContentScriptDefinition } from '../../types'; import { browser } from 'wxt/browser'; +import { v4 as uuidv4 } from 'uuid'; import { logger } from '../../sandbox/utils/logger'; import { WxtLocationChangeEvent, getUniqueEventName } from './custom-events'; import { createLocationWatcher } from './location-watcher'; @@ -42,7 +43,7 @@ export class ContentScriptContext implements AbortController { private isTopFrame = window.self === window.top; private abortController: AbortController; private locationWatcher = createLocationWatcher(this); - private receivedTimestamps = new Set(); + private receivedMessageIDs = new Set(); constructor( private readonly contentScriptName: string, @@ -234,6 +235,7 @@ export class ContentScriptContext implements AbortController { { type: ContentScriptContext.SCRIPT_STARTED_MESSAGE_TYPE, contentScriptName: this.contentScriptName, + messageId: uuidv4(), }, '*', ); @@ -248,8 +250,8 @@ export class ContentScriptContext implements AbortController { event.data?.contentScriptName === this.contentScriptName ) { // Deduplicate messages - if (this.receivedTimestamps.has(event.timeStamp)) return; - this.receivedTimestamps.add(event.timeStamp); + if (this.receivedMessageIDs.has(event.data.messageId)) return; + this.receivedMessageIDs.add(event.data.messageId); const wasFirst = isFirst; isFirst = false; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 49c88371b..39faa5d70 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -335,7 +335,7 @@ importers: version: 4.1.2 '@aklinker1/rollup-plugin-visualizer': specifier: 5.12.0 - version: 5.12.0(rollup@4.24.0) + version: 5.12.0(rollup@3.29.4) '@types/chrome': specifier: ^0.0.280 version: 0.0.280 @@ -455,7 +455,10 @@ importers: version: 1.3.0 unimport: specifier: ^3.13.1 - version: 3.13.1(rollup@4.24.0)(webpack-sources@3.2.3) + version: 3.13.1(rollup@3.29.4)(webpack-sources@3.2.3) + uuid: + specifier: ^11.0.5 + version: 11.0.5 vite: specifier: ^5.0.0 || <=6.0.8 version: 5.4.11(@types/node@20.17.6)(sass@1.80.7) @@ -4876,6 +4879,10 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + uuid@11.0.5: + resolution: {integrity: sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==} + hasBin: true + uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true @@ -5196,14 +5203,14 @@ snapshots: citty: 0.1.6 typescript: 5.6.3 - '@aklinker1/rollup-plugin-visualizer@5.12.0(rollup@4.24.0)': + '@aklinker1/rollup-plugin-visualizer@5.12.0(rollup@3.29.4)': dependencies: open: 8.4.2 picomatch: 2.3.1 source-map: 0.7.4 yargs: 17.7.2 optionalDependencies: - rollup: 4.24.0 + rollup: 3.29.4 '@algolia/autocomplete-core@1.9.3(@algolia/client-search@4.20.0)(algoliasearch@4.20.0)(search-insights@2.15.0)': dependencies: @@ -6204,13 +6211,13 @@ snapshots: optionalDependencies: rollup: 3.29.4 - '@rollup/pluginutils@5.1.2(rollup@4.24.0)': + '@rollup/pluginutils@5.1.2(rollup@3.29.4)': dependencies: '@types/estree': 1.0.5 estree-walker: 2.0.2 picomatch: 2.3.1 optionalDependencies: - rollup: 4.24.0 + rollup: 3.29.4 '@rollup/pluginutils@5.1.3(rollup@4.24.0)': dependencies: @@ -9500,9 +9507,9 @@ snapshots: undici-types@6.19.8: {} - unimport@3.13.1(rollup@4.24.0)(webpack-sources@3.2.3): + unimport@3.13.1(rollup@3.29.4)(webpack-sources@3.2.3): dependencies: - '@rollup/pluginutils': 5.1.2(rollup@4.24.0) + '@rollup/pluginutils': 5.1.2(rollup@3.29.4) acorn: 8.12.1 escape-string-regexp: 5.0.0 estree-walker: 3.0.3 @@ -9629,6 +9636,8 @@ snapshots: util-deprecate@1.0.2: {} + uuid@11.0.5: {} + uuid@8.3.2: {} validate-html-nesting@1.2.2: {} From b09928767bdf4d48689f48b718ae8d6a8cfd3bb1 Mon Sep 17 00:00:00 2001 From: DenizUgur Date: Mon, 27 Jan 2025 10:42:17 -0800 Subject: [PATCH 3/6] fix: also verify the origin of messages --- .../content-scripts/content-script-context.ts | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/packages/wxt/src/client/content-scripts/content-script-context.ts b/packages/wxt/src/client/content-scripts/content-script-context.ts index 014881d1c..483112349 100644 --- a/packages/wxt/src/client/content-scripts/content-script-context.ts +++ b/packages/wxt/src/client/content-scripts/content-script-context.ts @@ -43,6 +43,7 @@ export class ContentScriptContext implements AbortController { private isTopFrame = window.self === window.top; private abortController: AbortController; private locationWatcher = createLocationWatcher(this); + private sentMessageIDs = new Set(); private receivedMessageIDs = new Set(); constructor( @@ -230,27 +231,39 @@ export class ContentScriptContext implements AbortController { } stopOldScripts() { + const uuid = uuidv4(); + this.sentMessageIDs.add(uuid); // Use postMessage so it get's sent to all the frames of the page. window.postMessage( { type: ContentScriptContext.SCRIPT_STARTED_MESSAGE_TYPE, contentScriptName: this.contentScriptName, - messageId: uuidv4(), + messageId: uuid, }, '*', ); } + verifyScriptStartedEvent(event: MessageEvent) { + const isScriptStartedEvent = + event.data?.type === ContentScriptContext.SCRIPT_STARTED_MESSAGE_TYPE; + const isSameContentScript = + event.data?.contentScriptName === this.contentScriptName; + const isNotDuplicate = !this.receivedMessageIDs.has(event.data?.messageId); + const hasValidId = this.sentMessageIDs.has(event.data?.messageId); + return ( + isScriptStartedEvent && + isSameContentScript && + isNotDuplicate && + hasValidId + ); + } + listenForNewerScripts(options?: { ignoreFirstEvent?: boolean }) { let isFirst = true; const cb = (event: MessageEvent) => { - if ( - event.data?.type === ContentScriptContext.SCRIPT_STARTED_MESSAGE_TYPE && - event.data?.contentScriptName === this.contentScriptName - ) { - // Deduplicate messages - if (this.receivedMessageIDs.has(event.data.messageId)) return; + if (this.verifyScriptStartedEvent(event)) { this.receivedMessageIDs.add(event.data.messageId); const wasFirst = isFirst; From 7f5cf60257b88d2620945b22e0c463f6bd846bdb Mon Sep 17 00:00:00 2001 From: DenizUgur Date: Mon, 27 Jan 2025 14:05:02 -0800 Subject: [PATCH 4/6] fix: use random number for the id --- packages/wxt/package.json | 1 - .../content-scripts/content-script-context.ts | 7 +++--- pnpm-lock.yaml | 25 ++++++------------- 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/packages/wxt/package.json b/packages/wxt/package.json index 852af6d4f..5ee5239c5 100644 --- a/packages/wxt/package.json +++ b/packages/wxt/package.json @@ -123,7 +123,6 @@ "publish-browser-extension": "^2.2.2", "scule": "^1.3.0", "unimport": "^3.13.1", - "uuid": "^11.0.5", "vite": "^5.0.0 || <=6.0.8", "vite-node": "^2.1.4", "web-ext-run": "^0.2.1", diff --git a/packages/wxt/src/client/content-scripts/content-script-context.ts b/packages/wxt/src/client/content-scripts/content-script-context.ts index 483112349..9f0fe6c5b 100644 --- a/packages/wxt/src/client/content-scripts/content-script-context.ts +++ b/packages/wxt/src/client/content-scripts/content-script-context.ts @@ -1,6 +1,5 @@ import { ContentScriptDefinition } from '../../types'; import { browser } from 'wxt/browser'; -import { v4 as uuidv4 } from 'uuid'; import { logger } from '../../sandbox/utils/logger'; import { WxtLocationChangeEvent, getUniqueEventName } from './custom-events'; import { createLocationWatcher } from './location-watcher'; @@ -231,14 +230,14 @@ export class ContentScriptContext implements AbortController { } stopOldScripts() { - const uuid = uuidv4(); - this.sentMessageIDs.add(uuid); + const messageId = Math.random().toString(36).slice(2); + this.sentMessageIDs.add(messageId); // Use postMessage so it get's sent to all the frames of the page. window.postMessage( { type: ContentScriptContext.SCRIPT_STARTED_MESSAGE_TYPE, contentScriptName: this.contentScriptName, - messageId: uuid, + messageId, }, '*', ); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 39faa5d70..49c88371b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -335,7 +335,7 @@ importers: version: 4.1.2 '@aklinker1/rollup-plugin-visualizer': specifier: 5.12.0 - version: 5.12.0(rollup@3.29.4) + version: 5.12.0(rollup@4.24.0) '@types/chrome': specifier: ^0.0.280 version: 0.0.280 @@ -455,10 +455,7 @@ importers: version: 1.3.0 unimport: specifier: ^3.13.1 - version: 3.13.1(rollup@3.29.4)(webpack-sources@3.2.3) - uuid: - specifier: ^11.0.5 - version: 11.0.5 + version: 3.13.1(rollup@4.24.0)(webpack-sources@3.2.3) vite: specifier: ^5.0.0 || <=6.0.8 version: 5.4.11(@types/node@20.17.6)(sass@1.80.7) @@ -4879,10 +4876,6 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - uuid@11.0.5: - resolution: {integrity: sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==} - hasBin: true - uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true @@ -5203,14 +5196,14 @@ snapshots: citty: 0.1.6 typescript: 5.6.3 - '@aklinker1/rollup-plugin-visualizer@5.12.0(rollup@3.29.4)': + '@aklinker1/rollup-plugin-visualizer@5.12.0(rollup@4.24.0)': dependencies: open: 8.4.2 picomatch: 2.3.1 source-map: 0.7.4 yargs: 17.7.2 optionalDependencies: - rollup: 3.29.4 + rollup: 4.24.0 '@algolia/autocomplete-core@1.9.3(@algolia/client-search@4.20.0)(algoliasearch@4.20.0)(search-insights@2.15.0)': dependencies: @@ -6211,13 +6204,13 @@ snapshots: optionalDependencies: rollup: 3.29.4 - '@rollup/pluginutils@5.1.2(rollup@3.29.4)': + '@rollup/pluginutils@5.1.2(rollup@4.24.0)': dependencies: '@types/estree': 1.0.5 estree-walker: 2.0.2 picomatch: 2.3.1 optionalDependencies: - rollup: 3.29.4 + rollup: 4.24.0 '@rollup/pluginutils@5.1.3(rollup@4.24.0)': dependencies: @@ -9507,9 +9500,9 @@ snapshots: undici-types@6.19.8: {} - unimport@3.13.1(rollup@3.29.4)(webpack-sources@3.2.3): + unimport@3.13.1(rollup@4.24.0)(webpack-sources@3.2.3): dependencies: - '@rollup/pluginutils': 5.1.2(rollup@3.29.4) + '@rollup/pluginutils': 5.1.2(rollup@4.24.0) acorn: 8.12.1 escape-string-regexp: 5.0.0 estree-walker: 3.0.3 @@ -9636,8 +9629,6 @@ snapshots: util-deprecate@1.0.2: {} - uuid@11.0.5: {} - uuid@8.3.2: {} validate-html-nesting@1.2.2: {} From 5a0acd716105463fa19e2009bfd5f02ea778acff Mon Sep 17 00:00:00 2001 From: DenizUgur Date: Mon, 27 Jan 2025 14:20:05 -0800 Subject: [PATCH 5/6] fix: revert b099287 --- .../content-scripts/content-script-context.ts | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/packages/wxt/src/client/content-scripts/content-script-context.ts b/packages/wxt/src/client/content-scripts/content-script-context.ts index 9f0fe6c5b..a7ef3aaeb 100644 --- a/packages/wxt/src/client/content-scripts/content-script-context.ts +++ b/packages/wxt/src/client/content-scripts/content-script-context.ts @@ -42,7 +42,6 @@ export class ContentScriptContext implements AbortController { private isTopFrame = window.self === window.top; private abortController: AbortController; private locationWatcher = createLocationWatcher(this); - private sentMessageIDs = new Set(); private receivedMessageIDs = new Set(); constructor( @@ -230,14 +229,12 @@ export class ContentScriptContext implements AbortController { } stopOldScripts() { - const messageId = Math.random().toString(36).slice(2); - this.sentMessageIDs.add(messageId); // Use postMessage so it get's sent to all the frames of the page. window.postMessage( { type: ContentScriptContext.SCRIPT_STARTED_MESSAGE_TYPE, contentScriptName: this.contentScriptName, - messageId, + messageId: Math.random().toString(36).slice(2), }, '*', ); @@ -249,13 +246,7 @@ export class ContentScriptContext implements AbortController { const isSameContentScript = event.data?.contentScriptName === this.contentScriptName; const isNotDuplicate = !this.receivedMessageIDs.has(event.data?.messageId); - const hasValidId = this.sentMessageIDs.has(event.data?.messageId); - return ( - isScriptStartedEvent && - isSameContentScript && - isNotDuplicate && - hasValidId - ); + return isScriptStartedEvent && isSameContentScript && isNotDuplicate; } listenForNewerScripts(options?: { ignoreFirstEvent?: boolean }) { From 0cd4a56c8e3c65e34a58ffab1aab30629f95c572 Mon Sep 17 00:00:00 2001 From: Aaron Date: Tue, 28 Jan 2025 00:33:01 -0600 Subject: [PATCH 6/6] Consistent variable name casing --- .../src/client/content-scripts/content-script-context.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/wxt/src/client/content-scripts/content-script-context.ts b/packages/wxt/src/client/content-scripts/content-script-context.ts index a7ef3aaeb..48165b610 100644 --- a/packages/wxt/src/client/content-scripts/content-script-context.ts +++ b/packages/wxt/src/client/content-scripts/content-script-context.ts @@ -42,7 +42,7 @@ export class ContentScriptContext implements AbortController { private isTopFrame = window.self === window.top; private abortController: AbortController; private locationWatcher = createLocationWatcher(this); - private receivedMessageIDs = new Set(); + private receivedMessageIds = new Set(); constructor( private readonly contentScriptName: string, @@ -245,7 +245,7 @@ export class ContentScriptContext implements AbortController { event.data?.type === ContentScriptContext.SCRIPT_STARTED_MESSAGE_TYPE; const isSameContentScript = event.data?.contentScriptName === this.contentScriptName; - const isNotDuplicate = !this.receivedMessageIDs.has(event.data?.messageId); + const isNotDuplicate = !this.receivedMessageIds.has(event.data?.messageId); return isScriptStartedEvent && isSameContentScript && isNotDuplicate; } @@ -254,7 +254,7 @@ export class ContentScriptContext implements AbortController { const cb = (event: MessageEvent) => { if (this.verifyScriptStartedEvent(event)) { - this.receivedMessageIDs.add(event.data.messageId); + this.receivedMessageIds.add(event.data.messageId); const wasFirst = isFirst; isFirst = false;