Skip to content

Commit 38b5522

Browse files
committed
AG-37909 do not inject content scripts between extension wakeups
1 parent 047ba42 commit 38b5522

File tree

3 files changed

+52
-11
lines changed

3 files changed

+52
-11
lines changed

Extension/src/background/app/app-mv2.ts

+13-9
Original file line numberDiff line numberDiff line change
@@ -140,18 +140,22 @@ export class App {
140140
await UiApi.init();
141141

142142
/**
143-
* When the extension is enabled, disabled and re-enabled during the user session,
144-
* content scripts will be loaded multiple times in each open tab.
145-
* If statistics collection is enabled, the content script will initialize cssHitCounter.
146-
* Multiple cssHitCounters in the same page context will conflict with each other,
147-
* with a high probability of breaking the page.
148-
* To avoid this bug, we don't inject content scripts into open tabs during initialization
149-
* when stats collection is enabled.
143+
* Injects content scripts into already open tabs.
144+
*
145+
* Skips injection when:
146+
* - Statistics collection is enabled.
147+
* - Content scripts have already been injected in the current session.
148+
*
149+
* This prevents conflicts from multiple `cssHitCounters` and avoids unnecessary injections.
150150
*/
151-
if (SettingsApi.getSetting(SettingOption.DisableCollectHits)) {
152-
// inject content scripts into opened tabs
151+
if (
152+
SettingsApi.getSetting(SettingOption.DisableCollectHits)
153+
&& !await ContentScriptInjector.isInjected()
154+
) {
153155
await ContentScriptInjector.init();
156+
await ContentScriptInjector.setInjected();
154157
}
158+
155159
/**
156160
* Initializes Filters data:
157161
* - Loads app i18n metadata and caches it in i18n-metadata storage

Extension/src/background/app/app-mv3.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ export class App {
153153

154154
await rulesLimitsService.init();
155155

156-
// TODO mv3 uses other way to inject scripts. AG-33507
156+
// TODO inject content scripts to tabs open before installation AG-33507
157157
// /**
158158
// * When the extension is enabled, disabled and re-enabled during the user session,
159159
// * content scripts will be loaded multiple times in each open tab.

Extension/src/background/content-script-injector.ts

+38-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
*/
1818

1919
import { getHostname } from 'tldts';
20-
import { type Tabs } from 'webextension-polyfill';
20+
import browser, { type Tabs } from 'webextension-polyfill';
2121

2222
import { isHttpRequest } from 'tswebextension';
2323

@@ -39,6 +39,11 @@ import { createPromiseWithTimeout } from './utils/timeouts';
3939
* Helper class for injecting content script into tabs, opened before extension initialization.
4040
*/
4141
export class ContentScriptInjector {
42+
/**
43+
* Key used to store the injected flag in session storage.
44+
*/
45+
private static INJECTED_KEY = 'content_script_injected';
46+
4247
private static INJECTION_LIMIT_MS = 1000;
4348

4449
/**
@@ -202,4 +207,36 @@ export class ContentScriptInjector {
202207

203208
return true;
204209
}
210+
211+
/**
212+
* Sets the injected flag in session storage.
213+
* This method updates the session storage to indicate that content scripts have been injected.
214+
*/
215+
public static async setInjected(): Promise<void> {
216+
try {
217+
await browser.storage.session.set({ [ContentScriptInjector.INJECTED_KEY]: true });
218+
} catch (e) {
219+
logger.error('Cannot set injected flag in session storage', e);
220+
}
221+
}
222+
223+
/**
224+
* Checks if content scripts have been injected.
225+
* Uses session storage since it is faster than sending a message to the content script.
226+
* As of November 25, 2025, Firefox v132.0.2 takes 1 second to send a message,
227+
* whereas reading from the session storage takes only 1 ms.
228+
*
229+
* @returns True if content scripts were injected; otherwise, false.
230+
*/
231+
public static async isInjected(): Promise<boolean> {
232+
let isInjected = false;
233+
try {
234+
const result = await browser.storage.session.get(ContentScriptInjector.INJECTED_KEY);
235+
isInjected = result[ContentScriptInjector.INJECTED_KEY] === true;
236+
} catch (e) {
237+
logger.error('Cannot get injected flag from session storage', e);
238+
}
239+
240+
return isInjected;
241+
}
205242
}

0 commit comments

Comments
 (0)