Skip to content

Commit da0b371

Browse files
committed
Add light/dark autodetection.
I'm tired of waiting for w3c/webextensions#229
1 parent 3504b62 commit da0b371

File tree

8 files changed

+106
-4
lines changed

8 files changed

+106
-4
lines changed

src/background.js

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ class TabInfo extends SaveableEntry {
274274

275275
if (!spriteImg.ready) throw "must await spriteImgReady!";
276276
if (!options.ready) throw "must await optionsReady!";
277+
if (!darkMode.ready) throw "must await darkModeReady!";
277278

278279
if (tabMap[tabId]) throw "Duplicate entry in tabMap";
279280
if (tabTracker.exists(tabId)) {
@@ -426,7 +427,10 @@ class TabInfo extends SaveableEntry {
426427

427428
// Don't waste time redrawing the same icon.
428429
if (this.lastPattern != pattern) {
429-
const color = options[this.color];
430+
let color = options[this.color];
431+
if (color == "auto") {
432+
color = darkMode.value ? "lightfg" : "darkfg";
433+
}
430434
action.setIcon({
431435
"tabId": this.id(),
432436
"imageData": {
@@ -634,11 +638,77 @@ function lookupOriginMap(origin) {
634638
return originMap[origin] || new Set();
635639
}
636640

641+
// This horrific mess can eventually be replaced by
642+
// https://github.com/w3c/webextensions/issues/229
643+
let initDarkMode = null;
644+
const darkMode = {ready: false, resolve: null, value: false};
645+
if (typeof window !== 'undefined' && window.matchMedia) {
646+
// Watching for Dark Mode is trivial in Firefox.
647+
initDarkMode = (async () => {
648+
const query = window.matchMedia('(prefers-color-scheme: dark)');
649+
darkMode.value = query.matches;
650+
query.addEventListener("change", (event) => {
651+
changeDarkMode(event.matches);
652+
});
653+
darkMode.ready = true;
654+
});
655+
} else {
656+
initDarkMode = (async () => {
657+
const p = new Promise((resolve) => {darkMode.resolve = resolve});
658+
try {
659+
await chrome.offscreen.createDocument({
660+
url: "detectdarkmode.html",
661+
reasons: ['MATCH_MEDIA'],
662+
justification: 'detect light/dark mode for icon colors',
663+
});
664+
darkMode.value = await p;
665+
} catch {
666+
console.log("detectdarkmode failed!");
667+
}
668+
// The offscreen document can't provide darkMode updates, so kill it now.
669+
// We will get updates from the popup/option windows instead.
670+
try {
671+
await chrome.offscreen.closeDocument();
672+
} catch {
673+
// ignore
674+
}
675+
darkMode.ready = true;
676+
});
677+
678+
chrome.runtime.onMessage.addListener((message) => {
679+
console.log("onMessage", message);
680+
if (message.hasOwnProperty("darkModeOffscreen")) {
681+
darkMode.resolve?.(message.darkModeOffscreen);
682+
darkMode.resolve = null;
683+
} else if (message.hasOwnProperty("darkModeInteractive")) {
684+
// Receive updates from popup/option windows.
685+
changeDarkMode(message.darkModeInteractive);
686+
}
687+
});
688+
}
689+
const darkModeReady = initDarkMode();
690+
691+
async function changeDarkMode(newValue) {
692+
if (!darkMode.ready || darkMode.value == newValue) return;
693+
darkMode.value = newValue;
694+
695+
await storageReady;
696+
const RCS = "regularColorScheme";
697+
if (options[RCS] == "auto") {
698+
for (const tabInfo of Object.values(tabMap)) {
699+
if (tabInfo.color == RCS){
700+
tabInfo.refreshPageAction();
701+
}
702+
}
703+
}
704+
}
705+
637706
// Must "await storageReady;" before reading maps.
638707
// You can force initStorage() from the console for debugging purposes.
639708
const initStorage = async () => {
640709
await spriteImgReady;
641710
await optionsReady;
711+
await darkModeReady;
642712

643713
// Migrate previous-version data from local to session storage.
644714
const oldItems = await chrome.storage.local.get();

src/common.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ function drawSprite(ctx, size, targets, sources) {
159159
}
160160

161161
const DEFAULT_OPTIONS = {
162-
regularColorScheme: "darkfg",
162+
regularColorScheme: "auto",
163163
incognitoColorScheme: "lightfg",
164164
};
165165

@@ -276,4 +276,15 @@ function revertNAT64() {
276276
// our local Set is used for deduplication.
277277
_watchOptionsFunc?.([NAT64_KEY]);
278278
}
279+
}
280+
281+
if (chrome.runtime.getManifest().background.service_worker &&
282+
typeof window !== 'undefined' && window.matchMedia) {
283+
// We are running in Chrome, and the Options or Popup UI is visible.
284+
// Send darkMode updates to the service_worker.
285+
const query = window.matchMedia('(prefers-color-scheme: dark)');
286+
chrome.runtime.sendMessage({darkModeInteractive: query.matches});
287+
query.addEventListener("change", (event) => {
288+
chrome.runtime.sendMessage({darkModeInteractive: event.matches});
289+
});
279290
}

src/detectdarkmode.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<script src="detectdarkmode.js"></script>
5+
</head>
6+
</html>

src/detectdarkmode.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"use strict";
2+
const query = window.matchMedia('(prefers-color-scheme: dark)');
3+
chrome.runtime.sendMessage({darkModeOffscreen: query.matches});

src/iputil.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"use strict";
2+
13
const DOT = '.';
24
const COLON = ':';
35
const IPV6_PART_COUNT = 8;

src/manifest.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
"contextMenus",
2424
"storage",
2525
"webNavigation",
26-
"webRequest"
26+
"webRequest",
27+
"offscreen"
2728
],
2829
"host_permissions": [
2930
"<all_urls>"

src/manifest/chrome-manifest.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
"contextMenus",
2424
"storage",
2525
"webNavigation",
26-
"webRequest"
26+
"webRequest",
27+
"offscreen"
2728
],
2829
"host_permissions": [
2930
"<all_urls>"

src/options.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
border-color: #888;
3737
border-style: solid;
3838
padding: 0;
39+
white-space: nowrap;
40+
vertical-align: middle;
3941
}
4042
#nat64 table, #nat64 td, #nat64 tr {
4143
font-family: monospace;
@@ -114,6 +116,12 @@ <h1>Icon color scheme</h1>
114116
</div>
115117
</label>
116118
</td>
119+
<td>
120+
<label>
121+
<input type="radio" name="regularColorScheme" value="auto" class="disabler">
122+
Auto
123+
</label>
124+
</td>
117125
</tr>
118126
<tr>
119127
<td class="colorscheme_title">Incognito tabs:</td>

0 commit comments

Comments
 (0)