1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
|
import { SKIP_FORCE_THEMING_KEY } from "./shared/constants.js";
let logging = false;
if (logging) console.log("inject-css.js script loaded");
// Run as early as possible in the document lifecycle
const implementImmediateInjection = () => {
// Create a style element immediately to avoid any delay - do this before anything else
const styleElement = document.createElement("style");
styleElement.id = "zen-internet-styles";
// Set highest priority
styleElement.setAttribute("data-priority", "highest");
// Add !important to all rules to override any existing styles
styleElement.innerHTML = `
/* Prevent FOUC - temporarily hide content until styles are applied */
body { opacity: 0 !important; transition: opacity 0.1s ease-in !important; }
`;
// Insert as the first element of head if possible
if (document.head) {
document.head.insertBefore(styleElement, document.head.firstChild);
} else {
// If head isn't ready yet (very early execution), add to documentElement
document.documentElement.appendChild(styleElement);
// Set up mutation observer to move it to head when head becomes available
new MutationObserver((mutations, observer) => {
if (document.head) {
if (styleElement.parentNode !== document.head) {
document.head.insertBefore(styleElement, document.head.firstChild);
}
observer.disconnect();
}
}).observe(document.documentElement, { childList: true });
}
return styleElement;
};
// Create style element immediately
const styleElement = implementImmediateInjection();
// Function to apply styles immediately when available
function applyStyles(css) {
if (!css) return;
// Add the CSS
try {
// For immediate application, directly set textContent
// as this is more reliably applied in early document stages
styleElement.textContent = css.trim() + `
/* Remove FOUC prevention once styles are loaded */
body { opacity: 1 !important; }`;
// After a very short delay (to ensure CSS application), ensure body is visible
setTimeout(() => {
if (document.body) {
document.body.style.opacity = "1";
}
}, 10);
if (logging) console.log("Styles applied:", css.length, "bytes");
} catch (e) {
console.error("Error applying styles:", e);
}
}
// Listen for style data from background script for immediate injection
browser.runtime.onMessage.addListener((message) => {
if (message.action === "applyStyles" && message.css) {
applyStyles(message.css);
return true;
}
});
// Send hostname to background script as early as possible
browser.runtime
.sendMessage({
action: "contentScriptReady",
hostname: window.location.hostname,
url: window.location.href,
})
.catch((err) => {
if (logging) console.log("Background script not ready yet:", err);
});
// Main function - but we don't wait for this before applying styles
// This is just a backup in case background script injection fails
(async () => {
try {
const settings = await browser.storage.local.get("transparentZenSettings");
if (logging) console.log("Settings loaded", settings);
if (!settings.transparentZenSettings?.enableStyling) {
if (logging) console.log("Styling is disabled");
return;
}
if (logging) console.log("Styling is enabled");
// Tell background script we're ready and what page we're on
browser.runtime.sendMessage({
action: "contentScriptReady",
hostname: window.location.hostname,
});
const data = await browser.storage.local.get("styles");
if (!data.styles) {
if (logging) console.log("No styles data available");
return;
}
if (logging) console.log("Styles data loaded", data);
const currentUrl = window.location.hostname;
if (logging) console.log("Current URL hostname", currentUrl);
// Find the best matching CSS file
let bestMatch = null;
let bestMatchLength = 0;
for (const key of Object.keys(data.styles?.website || {})) {
const siteName = key.replace(".css", "");
if (siteName.startsWith("+")) {
const baseSiteName = siteName.slice(1);
if (
currentUrl.endsWith(baseSiteName) &&
baseSiteName.length > bestMatchLength
) {
bestMatch = key;
bestMatchLength = baseSiteName.length;
}
} else if (currentUrl === siteName || currentUrl === `www.${siteName}`) {
// Exact match has priority
bestMatch = key;
break;
} else if (
currentUrl.endsWith(siteName) &&
siteName.length > bestMatchLength
) {
bestMatch = key;
bestMatchLength = siteName.length;
}
}
// If a direct match was found, use it
if (bestMatch) {
await injectCSS(currentUrl, data.styles.website[bestMatch]);
return;
}
// If no direct match was found and force styling is enabled, check whitelist/blacklist mode
if (settings.transparentZenSettings?.forceStyling) {
const skipListData = await browser.storage.local.get(
SKIP_FORCE_THEMING_KEY
);
const siteList = skipListData[SKIP_FORCE_THEMING_KEY] || [];
const isWhitelistMode =
settings.transparentZenSettings?.whitelistMode || false;
const siteInList = siteList.includes(currentUrl);
// In whitelist mode: apply only if site is in the list
// In blacklist mode: apply only if site is NOT in the list
if (
(isWhitelistMode && siteInList) ||
(!isWhitelistMode && !siteInList)
) {
await injectCSS(currentUrl, data.styles.website["example.com.css"]);
} else {
if (logging)
console.log(
`Styling skipped due to ${
isWhitelistMode ? "whitelist" : "blacklist"
} mode settings`
);
}
} else {
if (logging) console.log("No CSS file found for current site");
}
} catch (error) {
console.error("Error injecting CSS:", error);
}
})();
async function injectCSS(hostname, features) {
if (!features) return;
const siteKey = `transparentZenSettings.${hostname}`;
const settings = await browser.storage.local.get("transparentZenSettings");
const globalSettings = settings.transparentZenSettings || {};
const siteData = await browser.storage.local.get(siteKey);
const featureSettings = siteData[siteKey] || {};
let combinedCSS = "";
for (const [feature, css] of Object.entries(features)) {
// Skip any transparency feature if disableTransparency is enabled globally
if (globalSettings.disableTransparency && feature.toLowerCase().includes("transparency")) {
if (logging) console.log(`Skipping transparency feature: ${feature}`);
continue;
}
// Apply the feature if it's not explicitly disabled
if (featureSettings[feature] !== false) {
combinedCSS += css + "\n";
}
}
if (combinedCSS) {
applyStyles(combinedCSS);
if (logging) console.log(`Injected custom CSS for ${hostname}`);
}
}
|