diff --git a/apps/zen.nix b/apps/zen.nix index 1cdb812..6170191 100644 --- a/apps/zen.nix +++ b/apps/zen.nix @@ -9,48 +9,79 @@ let # /chrome/zen-theme.css as a style-sheet-service USER_SHEET here # and re-register it under a cache-busting URI whenever its mtime moves — # that is what makes a `theme-apply` switch land in a running Zen - # (settings/theming.nix writes that file on every switch). A separate file, - # not userChrome.css: the legacy loader would hold its own copy of the same - # sheet and the two would race on cascade order. + # (settings/theming.nix writes that file on every switch). + # + # userChrome.css can't stay as a safety net: nsIStyleSheetService user + # sheets are ordered BEFORE the profile's userChrome.css, so a leftover copy + # would outrank every sheet we register and pin Zen to whichever wallpaper + # was live at startup — the exact bug this fixes. + # + # Everything here goes through Components.classes. The autoconfig sandbox is + # not a normal system-principal scope: `Services` is not a global in it, and + # reaching for it throws before a single sheet is registered. + # + # Diagnosing a silent failure: about:config -> fred.zenTheme.status holds + # "ok ", "missing " or "error " from the last pass. themeHotReload = '' try { - const { classes: kCc, interfaces: kCi } = Components; + const kCc = Components.classes; + const kCi = Components.interfaces; const sss = kCc["@mozilla.org/content/style-sheet-service;1"] .getService(kCi.nsIStyleSheetService); + const io = kCc["@mozilla.org/network/io-service;1"] + .getService(kCi.nsIIOService); + const dirs = kCc["@mozilla.org/file/directory_service;1"] + .getService(kCi.nsIProperties); + const prefs = kCc["@mozilla.org/preferences-service;1"] + .getService(kCi.nsIPrefBranch); + + const note = (msg) => { + try { prefs.setStringPref("fred.zenTheme.status", msg); } catch (e) {} + }; - const file = Services.dirsvc.get("ProfD", kCi.nsIFile); - file.append("chrome"); - file.append("zen-theme.css"); - const base = Services.io.newFileURI(file).spec; let live = null; const apply = () => { - if (!file.exists()) return; - // install(1) stamps the copy with the current time, so mtime alone - // distinguishes one wallpaper's sheet from the next. - const stamp = String(file.lastModifiedTime); - if (stamp === live) return; - // A fresh query string dodges the CSS loader's per-URI cache; without - // it a re-register would hand back the sheet parsed at startup. - sss.loadAndRegisterSheet(Services.io.newURI(base + "?" + stamp), - sss.USER_SHEET); - if (live !== null) { - const old = Services.io.newURI(base + "?" + live); - if (sss.sheetRegistered(old, sss.USER_SHEET)) - sss.unregisterSheet(old, sss.USER_SHEET); + try { + // Resolved per pass, not once: autoconfig runs early enough that + // ProfD is not guaranteed yet, and a throw here would kill the timer + // for the whole session. + const file = dirs.get("ProfD", kCi.nsIFile); + file.append("chrome"); + file.append("zen-theme.css"); + if (!file.exists()) { note("missing " + file.path); return; } + + // install(1) stamps the copy with the current time, so mtime alone + // distinguishes one wallpaper's sheet from the next. + const stamp = String(file.lastModifiedTime); + if (stamp === live) return; + + // A fresh query string dodges the CSS loader's per-URI cache; without + // it a re-register would hand back the sheet parsed at startup. + const base = io.newFileURI(file).spec; + sss.loadAndRegisterSheet(io.newURI(base + "?" + stamp), sss.USER_SHEET); + if (live !== null) { + const old = io.newURI(base + "?" + live); + if (sss.sheetRegistered(old, sss.USER_SHEET)) + sss.unregisterSheet(old, sss.USER_SHEET); + } + live = stamp; + note("ok " + stamp); + } catch (e) { + note("error " + e); } - live = stamp; }; const timer = kCc["@mozilla.org/timer;1"].createInstance(kCi.nsITimer); // The observer service holds the only strong reference that keeps the // timer (and this closure) off the GC's list for the session. - Services.obs.addObserver({ timer, observe() {} }, "quit-application"); + kCc["@mozilla.org/observer-service;1"].getService(kCi.nsIObserverService) + .addObserver({ timer, observe() {} }, "quit-application"); timer.initWithCallback({ notify: apply }, 2000, kCi.nsITimer.TYPE_REPEATING_SLACK); apply(); } catch (e) { - Components.utils.reportError(e); + Components.utils.reportError("zen-theme: " + e); } ''; in