@@ -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.
639708const 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 ( ) ;
0 commit comments