Skip to content

Commit e1df9f8

Browse files
Merge branch 'main' into Not_Stable
2 parents a9c327d + dbe1818 commit e1df9f8

File tree

6 files changed

+95
-10
lines changed

6 files changed

+95
-10
lines changed

extension/entrypoints/background.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,4 +601,4 @@ function sendStatusToTab(pending: PendingDownload, status: DownloadStatus, userM
601601
try {
602602
chrome.tabs.sendMessage(pending.tabId, { type: 'CQD_DOWNLOAD_STATUS', requestId: pending.requestId, status, errorCode, userMessage });
603603
} catch {}
604-
}
604+
}

extension/entrypoints/comment_frame.content.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,9 @@ function stopCommentsFeature(): void {
139139
* ---------------------------------------------------*/
140140
if (typeof chrome !== 'undefined' && chrome.runtime?.onMessage) {
141141
chrome.runtime.onMessage.addListener((message) => {
142-
if (!message) return;
143-
if (message.type !== 'CQD_POPUP_SET_DESIRED_STATE') return;
142+
// Firefox fix: return false for messages we don't handle
143+
if (!message) return false;
144+
if (message.type !== 'CQD_POPUP_SET_DESIRED_STATE') return false;
144145
tabEnabled = !!message.enabled;
145146
if (tabEnabled) {
146147
whenExtensionEnabled(() => {
@@ -149,6 +150,7 @@ if (typeof chrome !== 'undefined' && chrome.runtime?.onMessage) {
149150
} else {
150151
stopCommentsFeature();
151152
}
153+
return false; // No async response needed
152154
});
153155
}
154156

extension/entrypoints/content/both-badge.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// filepath: entrypoints/content/both-badge.ts
2-
import { COMMENT_ICON_URL, EDIT_ICON_SVG_RAW } from './icons';
2+
import { COMMENT_ICON_URL, EDIT_ICON_SVG_RAW, appendSvgFromString } from './icons';
33

44
const INJECTED_ATTR = 'data-cqd-injected';
55

@@ -128,7 +128,7 @@ export function upgradeCombinedBadge(post: HTMLElement): void {
128128

129129
const editedIcon = document.createElement('div');
130130
editedIcon.className = 'cqd-both-icon cqd-both-icon-edited';
131-
editedIcon.innerHTML = EDIT_ICON_SVG_RAW;
131+
appendSvgFromString(editedIcon, EDIT_ICON_SVG_RAW);
132132

133133
const diffValue = document.createElement('span');
134134
diffValue.className = 'cqd-both-value cqd-both-value-edited';

extension/entrypoints/content/icons.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,29 @@ export const EDIT_ICON_URL = `data:image/svg+xml;utf8,${encodeURIComponent(
5050
)}`;
5151
export const COMMENT_ICON_URL = `data:image/svg+xml;utf8,${encodeURIComponent(
5252
COMMENT_ICON_SVG_RAW
53-
)}`;
53+
)}`;
54+
55+
/**
56+
* CSP-safe: Parse SVG string and append to container using DOMParser.
57+
* Avoids innerHTML which can be blocked by strict CSP in Firefox.
58+
*/
59+
export function appendSvgFromString(container: HTMLElement, svgString: string): void {
60+
try {
61+
const parser = new DOMParser();
62+
const doc = parser.parseFromString(svgString, 'image/svg+xml');
63+
const svg = doc.documentElement;
64+
65+
// Check for parse errors
66+
const parseError = doc.querySelector('parsererror');
67+
if (parseError) {
68+
console.warn('[CQD] SVG parse error:', parseError.textContent);
69+
return;
70+
}
71+
72+
// Import the node into the current document and append
73+
const importedSvg = document.importNode(svg, true);
74+
container.appendChild(importedSvg);
75+
} catch (err) {
76+
console.warn('[CQD] Failed to append SVG:', err);
77+
}
78+
}

extension/entrypoints/edited_frame.content.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// filepath: entrypoints/edited_frame.content.ts
2-
import { EDIT_ICON_SVG_RAW } from './content/icons';
2+
import { EDIT_ICON_SVG_RAW, appendSvgFromString } from './content/icons';
33
import { injectStyles } from './content/styles';
44
import { isPageDark } from './content/theme';
55
import { t } from './content/i18n';
@@ -132,8 +132,9 @@ function stopEditedFeature(): void {
132132
* ---------------------------------------------------*/
133133
if (typeof chrome !== 'undefined' && chrome.runtime?.onMessage) {
134134
chrome.runtime.onMessage.addListener((message) => {
135-
if (!message) return;
136-
if (message.type !== 'CQD_POPUP_SET_DESIRED_STATE') return;
135+
// Firefox fix: return false for messages we don't handle
136+
if (!message) return false;
137+
if (message.type !== 'CQD_POPUP_SET_DESIRED_STATE') return false;
137138
tabEnabled = !!message.enabled;
138139
if (tabEnabled) {
139140
whenExtensionEnabled(() => {
@@ -142,6 +143,7 @@ if (typeof chrome !== 'undefined' && chrome.runtime?.onMessage) {
142143
} else {
143144
stopEditedFeature();
144145
}
146+
return false; // No async response needed
145147
});
146148
}
147149

@@ -360,7 +362,7 @@ function createEditedOverlay(post: HTMLElement, diffText: string) {
360362

361363
const iconWrapper = document.createElement('div');
362364
iconWrapper.className = 'cqd-edited-icon';
363-
iconWrapper.innerHTML = EDIT_ICON_SVG_RAW;
365+
appendSvgFromString(iconWrapper, EDIT_ICON_SVG_RAW);
364366
pill.appendChild(iconWrapper);
365367

366368
const content = document.createElement('div');
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// filepath: extension/entrypoints/utils/firefox-debug.ts
2+
3+
/**
4+
* Firefox-specific debugging utilities.
5+
* Helps diagnose Firefox-only issues in the extension.
6+
*/
7+
8+
/**
9+
* Detect if the current browser is Firefox.
10+
*/
11+
export function isFirefox(): boolean {
12+
return typeof navigator !== 'undefined' &&
13+
/Firefox/i.test(navigator.userAgent);
14+
}
15+
16+
/**
17+
* Log a debug message only when running in Firefox.
18+
*/
19+
export function logFirefox(category: string, message: string, data?: unknown): void {
20+
if (!isFirefox()) return;
21+
22+
const prefix = `[CQD:Firefox:${category}]`;
23+
if (data !== undefined) {
24+
console.log(prefix, message, data);
25+
} else {
26+
console.log(prefix, message);
27+
}
28+
}
29+
30+
/**
31+
* Log a warning message only when running in Firefox.
32+
*/
33+
export function warnFirefox(category: string, message: string, data?: unknown): void {
34+
if (!isFirefox()) return;
35+
36+
const prefix = `[CQD:Firefox:${category}]`;
37+
if (data !== undefined) {
38+
console.warn(prefix, message, data);
39+
} else {
40+
console.warn(prefix, message);
41+
}
42+
}
43+
44+
/**
45+
* Check if Firefox's Promise-based messaging API is available.
46+
* Returns true if sendMessage returns a Promise (Firefox behavior).
47+
*/
48+
export function hasPromiseBasedMessaging(): boolean {
49+
if (typeof chrome === 'undefined' || !chrome.runtime?.sendMessage) {
50+
return false;
51+
}
52+
53+
// In Firefox, sendMessage can return a Promise even in MV2
54+
// We detect this by checking the browser's user agent
55+
return isFirefox();
56+
}

0 commit comments

Comments
 (0)