In MV2, it's quite easy because there's a background.html
concept, so that I can access window.matchMedia('(prefers-color-scheme: dark)').matches
to understand the OS's theme setting.
But in MV3, the background.html
is gone and in the background.js
, if you retrieve that value, it will display the following error:
> window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
Uncaught ReferenceError: window is not defined
at <anonymous>:1:1
(anonymous) @.....
Is there anything I can do?
The reason I want to achieve this is to set a observer in the background script to check the OS theme changing, so that I can update the extension icon accordingly to make sure it's perfectly matching the OS style.
Thanks for any kind of tips.
manifest.json:
"permissions": ["offscreen", "storage"],
background.js
(async () => {
await chrome.offscreen.createDocument({
url: 'offscreen.html',
reasons: ['MATCH_MEDIA'],
justification: '!',
}).catch(() => {});
setDarkTheme(await chrome.runtime.sendMessage('checkDarkTheme'));
chrome.offscreen.closeDocument();
})();
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg === 'darkThemeChanged') {
setDarkTheme(msg.data);
}
});
function setDarkTheme(val) {
chrome.storage.session.set({isDark: val});
// do something meaningful e.g. change the icon
}
offscreen.html:
<script src=offscreen.js></script>
offscreen.js:
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg === 'checkDarkTheme') {
sendResponse(matchMedia('(prefers-color-scheme: dark)').matches);
}
});
Browsers don't report it in invisible pages like MV2 background page or the offscreen document. This means that you won't receive any notification until a visible page is opened.
You'll need to declare a content script for <all_urls>
, which adds "Reads data on all sites" permission warning when installing the extension.
content.js:
{
const mq = matchMedia('(prefers-color-scheme: dark)');
const cb = async evt => {
const { isDark } = await chrome.storage.session.get('isDark');
if (isDark !== evt.matches) {
chrome.runtime.sendMessage({
cmd: 'darkThemeChanged',
data: evt.matches,
});
}
};
mq.onchange = cb;
cb(mq);
}
You should also include this code block in your extension pages (but not the entire content.js which is meant for web pages) so that the detection occurs when your extension page or popup is opened in a browser that doesn't have any tabs with web pages where you content.js runs.