Skip to content

Commit d58516a

Browse files
committed
Clear iframes when top dark theme detected
1 parent 49a8c4d commit d58516a

3 files changed

Lines changed: 46 additions & 38 deletions

File tree

src/background/extension.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -459,9 +459,9 @@ export class Extension {
459459
};
460460
}
461461

462-
private static async getConnectionMessage(tabURL: string, url: string, isTopFrame: boolean) {
462+
private static async getConnectionMessage(tabURL: string, url: string, isTopFrame: boolean, topFrameHasDarkTheme?: boolean) {
463463
await Extension.loadData();
464-
return Extension.getTabMessage(tabURL, url, isTopFrame);
464+
return Extension.getTabMessage(tabURL, url, isTopFrame, topFrameHasDarkTheme);
465465
}
466466

467467
private static async loadData() {
@@ -660,10 +660,10 @@ export class Extension {
660660
};
661661
}
662662

663-
private static getTabMessage = (tabURL: string, url: string, isTopFrame: boolean): TabData => {
663+
private static getTabMessage = (tabURL: string, url: string, isTopFrame: boolean, topFrameHasDarkTheme?: boolean): TabData => {
664664
const settings = UserStorage.settings;
665665
const tabInfo = Extension.getTabInfo(tabURL);
666-
if (Extension.isExtensionSwitchedOn() && isURLEnabled(tabURL, settings, tabInfo)) {
666+
if (Extension.isExtensionSwitchedOn() && isURLEnabled(tabURL, settings, tabInfo) && !topFrameHasDarkTheme) {
667667
const custom = settings.customThemes.find(({url: urlList}) => isURLInList(tabURL, urlList));
668668
const preset = custom ? null : settings.presets.find(({urls}) => isURLInList(tabURL, urls));
669669
let theme = custom ? custom.theme : preset ? preset.theme : settings.theme;

src/background/tab-manager.ts

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {canInjectScript} from '../background/utils/extension-api';
22
import {createFileLoader} from './utils/network';
33
import type {FetchRequestParameters} from './utils/network';
4-
import type {MessageBGtoCS, MessageCStoBG, MessageUItoBG, documentId, frameId, scriptId, tabId} from '../definitions';
4+
import type {MessageBGtoCS, MessageCStoBG, MessageUItoBG} from '../definitions';
55
import {isFirefox} from '../utils/platform';
66
import {MessageTypeCStoBG, MessageTypeBGtoCS, MessageTypeUItoBG} from '../utils/message';
77
import {ASSERT, logInfo, logWarn} from './utils/log';
@@ -16,14 +16,14 @@ declare const __CHROMIUM_MV3__: boolean;
1616
declare const __THUNDERBIRD__: boolean;
1717

1818
interface TabManagerOptions {
19-
getConnectionMessage: (tabURl: string, url: string, isTopFrame: boolean) => Promise<MessageBGtoCS>;
19+
getConnectionMessage: (tabURl: string, url: string, isTopFrame: boolean, topFrameHasDarkTheme?: boolean) => Promise<MessageBGtoCS>;
2020
getTabMessage: (tabURL: string, url: string, isTopFrame: boolean) => MessageBGtoCS;
2121
onColorSchemeChange: (isDark: boolean) => void;
2222
}
2323

2424
interface DocumentInfo {
25-
scriptId: scriptId;
26-
documentId: documentId | null;
25+
scriptId: string;
26+
documentId: string | null;
2727
isTop: true | undefined;
2828
url: string | null;
2929
state: DocumentState;
@@ -32,7 +32,7 @@ interface DocumentInfo {
3232
}
3333

3434
interface TabManagerState extends Record<string, unknown> {
35-
tabs: {[tabId: tabId]: {[frameId: frameId]: DocumentInfo}};
35+
tabs: {[tabId: number]: {[frameId: number]: DocumentInfo}};
3636
timestamp: number;
3737
}
3838

@@ -52,7 +52,7 @@ enum DocumentState {
5252

5353
/**
5454
* Note: On Chromium builds, we use documentId if it is available.
55-
* We avoid messaging using farmeId entirely since when document is prerendered, it gets a temporary frameId
55+
* We avoid messaging using frameId entirely since when document is pre-rendered, it gets a temporary frameId
5656
* and if we attempt to send to {frameId, documentId} with old frameId, then the message will be dropped.
5757
*/
5858
export default class TabManager {
@@ -78,8 +78,8 @@ export default class TabManager {
7878
case MessageTypeCStoBG.DOCUMENT_CONNECT: {
7979
TabManager.onColorSchemeMessage(message, sender);
8080
await TabManager.stateManager.loadState();
81-
const reply = (tabURL: string, url: string, isTopFrame: boolean) => {
82-
getConnectionMessage(tabURL, url, isTopFrame).then((response) => {
81+
const reply = (tabURL: string, url: string, isTopFrame: boolean, topFrameHasDarkTheme?: boolean) => {
82+
getConnectionMessage(tabURL, url, isTopFrame, topFrameHasDarkTheme).then((response) => {
8383
if (!response) {
8484
return;
8585
}
@@ -113,14 +113,15 @@ export default class TabManager {
113113
const url = sender.url!;
114114
const tabId = sender.tab!.id!;
115115
const scriptId = message.scriptId!;
116+
const topFrameHasDarkTheme = isTopFrame ? false : TabManager.tabs[tabId]?.[0]?.darkThemeDetected;
116117
// Chromium 106+ may prerender frames resulting in top-level frames with chrome.runtime.MessageSender.tab.url
117118
// set to chrome://newtab/ and positive chrome.runtime.MessageSender.frameId
118119
const tabURL = ((__CHROMIUM_MV2__ || __CHROMIUM_MV3__) && isTopFrame) ? url : sender.tab!.url!;
119-
const documentId: documentId | null = __CHROMIUM_MV3__ ? sender.documentId! : (sender.documentId || null);
120+
const documentId: string | null = __CHROMIUM_MV3__ ? sender.documentId! : (sender.documentId || null);
120121

121122
TabManager.addFrame(tabId, frameId!, documentId, scriptId, url, isTopFrame);
122123

123-
reply(tabURL, url, isTopFrame);
124+
reply(tabURL, url, isTopFrame, topFrameHasDarkTheme);
124125
TabManager.stateManager.saveState();
125126
break;
126127
}
@@ -150,7 +151,7 @@ export default class TabManager {
150151
const tabURL = sender.tab!.url!;
151152
const frameId = sender.frameId!;
152153
const url = sender.url!;
153-
const documentId: documentId | null = __CHROMIUM_MV3__ ? sender.documentId! : (sender.documentId! || null);
154+
const documentId: string | null = __CHROMIUM_MV3__ ? sender.documentId! : (sender.documentId! || null);
154155
const isTopFrame: boolean = (__CHROMIUM_MV2__ || __CHROMIUM_MV3__) ? (frameId === 0 || message.data.isTopFrame) : frameId === 0;
155156
if (TabManager.tabs[tabId][frameId].timestamp < TabManager.timestamp) {
156157
const response = TabManager.getTabMessage(tabURL, url, isTopFrame);
@@ -171,9 +172,23 @@ export default class TabManager {
171172
}
172173

173174
case MessageTypeCStoBG.DARK_THEME_DETECTED:
174-
const frames = TabManager.tabs[sender.tab!.id!];
175-
for (const frame of Object.values(frames)) {
175+
const tabId = sender.tab!.id!;
176+
const frames = TabManager.tabs[tabId];
177+
if (!frames) {
178+
break;
179+
}
180+
for (const entry of Object.entries(frames)) {
181+
const frameId = Number(entry[0]);
182+
const frame = entry[1];
176183
frame.darkThemeDetected = true;
184+
const {documentId, scriptId} = frame;
185+
if (sender.frameId === 0 && !frame.isTop && frameId && documentId) {
186+
const message = {
187+
type: MessageTypeBGtoCS.CLEAN_UP,
188+
scriptId,
189+
};
190+
TabManager.sendDocumentMessage(tabId, documentId, message, frameId);
191+
}
177192
}
178193
break;
179194

@@ -221,16 +236,16 @@ export default class TabManager {
221236
chrome.tabs.onRemoved.addListener(async (tabId) => TabManager.removeFrame(tabId, 0));
222237
}
223238

224-
private static sendDocumentMessage(tabId: tabId, documentId: documentId, message: MessageBGtoCS, frameId: frameId) {
239+
private static sendDocumentMessage(tabId: number, documentId: string, message: MessageBGtoCS, frameId: number) {
225240
if (__CHROMIUM_MV3__) {
226-
// On MV3, Chromium has a bug which prevents sending messages to prerendered frames without specifying frameId
227-
// Furethermore, if we send a message addressed to a temporary frameId after the document exits prerender state,
241+
// On MV3, Chromium has a bug which prevents sending messages to pre-rendered frames without specifying frameId
242+
// Furthermore, if we send a message addressed to a temporary frameId after the document exits prerender state,
228243
// the message will also fail to be delivered.
229244
//
230245
// To work around this:
231246
// 1. Attempt to send the message by documentId. If this fails, this means the document is in prerender state.
232-
// 2. Attempt to send the message by dicumentId and temporary frameId. If this fails, this means the document
233-
// either alteady exited prerendred state or was discarded.
247+
// 2. Attempt to send the message by documentId and temporary frameId. If this fails, this means the document
248+
// either already exited pre-rendered state or was discarded.
234249
// 3. Attempt to send the message by documentId (omitting the permanent frameId which is 0).If this fails, this
235250
// means the document was already discarded.
236251
//
@@ -262,8 +277,8 @@ export default class TabManager {
262277
}
263278
}
264279

265-
private static addFrame(tabId: tabId, frameId: frameId, documentId: documentId | null, scriptId: scriptId, url: string, isTop: boolean) {
266-
let frames: {[frameId: frameId]: DocumentInfo};
280+
private static addFrame(tabId: number, frameId: number, documentId: string | null, scriptId: string, url: string, isTop: boolean) {
281+
let frames: {[frameId: number]: DocumentInfo};
267282
if (TabManager.tabs[tabId]) {
268283
frames = TabManager.tabs[tabId];
269284
} else {
@@ -281,7 +296,7 @@ export default class TabManager {
281296
};
282297
}
283298

284-
private static async removeFrame(tabId: tabId, frameId: frameId) {
299+
private static async removeFrame(tabId: number, frameId: number) {
285300
await TabManager.stateManager.loadState();
286301

287302
if (frameId === 0) {
@@ -300,7 +315,7 @@ export default class TabManager {
300315
static async getTabURL(tab: chrome.tabs.Tab | null): Promise<string> {
301316
if (__CHROMIUM_MV3__) {
302317
if (!tab) {
303-
return 'abou:blank';
318+
return 'about:blank';
304319
}
305320
try {
306321
if (TabManager.tabs[tab.id!] && TabManager.tabs[tab.id!][0]) {
@@ -404,7 +419,7 @@ export default class TabManager {
404419
return tab && Boolean(TabManager.tabs[tab.id!]) || false;
405420
}
406421

407-
static getTabDocumentId(tab: chrome.tabs.Tab | null): documentId | null {
422+
static getTabDocumentId(tab: chrome.tabs.Tab | null): string | null {
408423
return tab && TabManager.tabs[tab.id!] && TabManager.tabs[tab.id!][0] && TabManager.tabs[tab.id!][0].documentId;
409424
}
410425

src/definitions.d.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,6 @@ import type {ThemeEngine} from './generators/theme-engines';
66

77
export type ColorScheme = 'dark' | 'light';
88

9-
// ContextId is a number on Firefox and documentId is a string in Chromium,
10-
// let's use string for simplicity
11-
export type documentId = string;
12-
export type scriptId = string;
13-
export type tabId = number;
14-
export type frameId = number;
15-
169
export interface ExtensionData {
1710
isEnabled: boolean;
1811
isReady: boolean;
@@ -139,8 +132,8 @@ export interface LocationSettings {
139132

140133
export interface TabInfo {
141134
url: string;
142-
id: tabId | null;
143-
documentId: documentId | null;
135+
id: number | null;
136+
documentId: string | null;
144137
isProtected: boolean;
145138
isInjected: boolean | null;
146139
isInDarkList: boolean;
@@ -149,7 +142,7 @@ export interface TabInfo {
149142

150143
export interface MessageCStoBG {
151144
id?: string;
152-
scriptId?: scriptId;
145+
scriptId?: string;
153146
type: MessageTypeCStoBG;
154147
data?: any;
155148
}
@@ -165,7 +158,7 @@ export interface MessageCStoUI {
165158

166159
export interface MessageBGtoCS {
167160
id?: string;
168-
scriptId?: scriptId;
161+
scriptId?: string;
169162
type: MessageTypeBGtoCS;
170163
data?: any;
171164
error?: any;

0 commit comments

Comments
 (0)