diff options
-rw-r--r-- | background.js | 61 | ||||
-rw-r--r-- | data-viewer/data-viewer.js | 27 | ||||
-rw-r--r-- | inject-css.js | 220 | ||||
-rw-r--r-- | popup/popup.js | 42 |
4 files changed, 99 insertions, 251 deletions
diff --git a/background.js b/background.js index a3f6e43..9b4ba51 100644 --- a/background.js +++ b/background.js @@ -92,20 +92,36 @@ browser.runtime.onMessage.addListener(async (message, sender) => { // Get appropriate styles for a hostname based on all rules async function getStylesForHostname(hostname, settings) { - // Check for exact matches + console.log("DEBUG: Finding styles for hostname:", hostname); + + // Check for exact matches first (highest priority) if (cssCache.has(hostname)) { + console.log("DEBUG: Found exact hostname match in cache"); return cssCache.get(hostname); } else if (cssCache.has(`www.${hostname}`)) { + console.log("DEBUG: Found www prefix match in cache"); return cssCache.get(`www.${hostname}`); } else { // Check for wildcard matches (+domain.com) for (const [cachedSite, cachedCSS] of cssCache.entries()) { if (cachedSite.startsWith("+")) { const baseSite = cachedSite.slice(1); - if (hostname.endsWith(baseSite)) { + // Ensure we're matching with proper domain boundary (dot or exact match) + if (hostname === baseSite || hostname.endsWith(`.${baseSite}`)) { + console.log( + `DEBUG: Found wildcard match: ${cachedSite} for ${hostname}` + ); return cachedCSS; } - } else if (hostname.endsWith(cachedSite)) { + } else if ( + cachedSite !== hostname && + cachedSite !== `www.${hostname}` && + hostname.endsWith(`.${cachedSite}`) + ) { + // Only match subdomains, not partial domain names + console.log( + `DEBUG: Found subdomain match: ${cachedSite} for ${hostname}` + ); return cachedCSS; } } @@ -197,35 +213,48 @@ async function applyCSSToTab(tab) { // Find the best matching CSS file let bestMatch = null; let bestMatchLength = 0; + let matchType = "none"; for (const key of Object.keys(data.styles?.website || {})) { const siteName = key.replace(".css", ""); + + // Exact match has highest priority + if (hostname === siteName || hostname === `www.${siteName}`) { + bestMatch = key; + matchType = "exact"; + console.log("DEBUG: Found exact match:", key); + break; + } + + // Then check wildcard matches if (siteName.startsWith("+")) { - const baseSiteName = siteName.slice(1); + const baseSite = siteName.slice(1); + // Ensure we're matching with proper domain boundary if ( - hostname.endsWith(baseSiteName) && - baseSiteName.length > bestMatchLength + (hostname === baseSite || hostname.endsWith(`.${baseSite}`)) && + baseSite.length > bestMatchLength ) { bestMatch = key; - bestMatchLength = baseSiteName.length; + bestMatchLength = baseSite.length; + matchType = "wildcard"; console.log( "DEBUG: Found wildcard match:", key, "with length", - baseSiteName.length + baseSite.length ); } - } else if (hostname === siteName || hostname === `www.${siteName}`) { - // Exact match has priority - bestMatch = key; - console.log("DEBUG: Found exact match:", key); - break; - } else if ( - hostname.endsWith(siteName) && + } + // Last, check subdomain matches with proper domain boundary + else if ( + hostname !== siteName && + hostname !== `www.${siteName}` && + hostname.endsWith(`.${siteName}`) && siteName.length > bestMatchLength ) { bestMatch = key; bestMatchLength = siteName.length; + matchType = "subdomain"; console.log( "DEBUG: Found domain suffix match:", key, @@ -237,7 +266,7 @@ async function applyCSSToTab(tab) { // If we found a direct match, use it if (bestMatch) { - console.log("DEBUG: Using direct match:", bestMatch); + console.log("DEBUG: Using match:", bestMatch, "of type:", matchType); await applyCSS(tab.id, hostname, data.styles.website[bestMatch]); return; } else { diff --git a/data-viewer/data-viewer.js b/data-viewer/data-viewer.js index d38f53f..f6dd566 100644 --- a/data-viewer/data-viewer.js +++ b/data-viewer/data-viewer.js @@ -334,11 +334,28 @@ document.addEventListener("DOMContentLoaded", function () { // Get site settings if available const siteName = website.replace(".css", ""); - const domainName = siteName.startsWith("+") - ? siteName.slice(1) - : siteName; - const settingsData = - siteSettings[domainName] || siteSettings[`www.${domainName}`] || {}; + let domainName; + // Declare settingsData at a higher scope so it's accessible throughout the function + let settingsData = {}; + + // Handle wildcard sites correctly + if (siteName.startsWith("+")) { + domainName = siteName.slice(1); + // For wildcard sites, we need to find any matching domain in settings + const matchingDomains = Object.keys(siteSettings).filter( + (domain) => domain === domainName || domain.endsWith(`.${domainName}`) + ); + + // Use the first matching domain's settings if any found + const settingsKey = + matchingDomains.length > 0 ? matchingDomains[0] : null; + settingsData = settingsKey ? siteSettings[settingsKey] : {}; + } else { + // For direct domains, just check the domain and www.domain + domainName = siteName; + settingsData = + siteSettings[domainName] || siteSettings[`www.${domainName}`] || {}; + } header.innerHTML = ` <div class="website-header-content"> diff --git a/inject-css.js b/inject-css.js deleted file mode 100644 index 27b4ecc..0000000 --- a/inject-css.js +++ /dev/null @@ -1,220 +0,0 @@ -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}`); - } -} diff --git a/popup/popup.js b/popup/popup.js index 37d1ab2..7b00bb5 100644 --- a/popup/popup.js +++ b/popup/popup.js @@ -331,12 +331,21 @@ new (class ExtensionPopup { isCurrentSite(siteName) { if (logging) console.log("isCurrentSite called with", siteName); if (!this.currentSiteHostname) return false; + + // Exact match has priority + if (this.currentSiteHostname === siteName) return true; + if (this.currentSiteHostname === `www.${siteName}`) return true; + + // Wildcard match (with proper domain boundary) if (siteName.startsWith("+")) { const baseSiteName = siteName.slice(1); - return this.currentSiteHostname.endsWith(baseSiteName); + return ( + this.currentSiteHostname === baseSiteName || + this.currentSiteHostname.endsWith(`.${baseSiteName}`) + ); } - if (this.currentSiteHostname === siteName) return true; - if (this.currentSiteHostname === `www.${siteName}`) return true; + + // Don't match partial domain names return false; } @@ -413,25 +422,38 @@ new (class ExtensionPopup { for (const site of Object.keys(styles)) { const siteName = site.replace(/\.css$/, ""); + + // Exact match has highest priority + if (hostname === siteName || hostname === `www.${siteName}`) { + bestMatch = site; + if (logging) console.log("Popup: Found exact match:", site); + break; + } + + // Then check wildcard matches if (siteName.startsWith("+")) { const baseSiteName = siteName.slice(1); + // Ensure we're matching with proper domain boundary if ( - hostname.endsWith(baseSiteName) && + (hostname === baseSiteName || + hostname.endsWith(`.${baseSiteName}`)) && baseSiteName.length > bestMatchLength ) { bestMatch = site; bestMatchLength = baseSiteName.length; + if (logging) console.log("Popup: Found wildcard match:", site); } - } else if (hostname === siteName || hostname === `www.${siteName}`) { - // Exact match has priority - bestMatch = site; - break; - } else if ( - hostname.endsWith(siteName) && + } + // Last, check subdomain matches with proper domain boundary + else if ( + hostname !== siteName && + hostname !== `www.${siteName}` && + hostname.endsWith(`.${siteName}`) && siteName.length > bestMatchLength ) { bestMatch = site; bestMatchLength = siteName.length; + if (logging) console.log("Popup: Found subdomain match:", site); } } |