浏览器扩展可以覆盖 CSS 媒体 features/query 吗?
Can browser extensions overwrite CSS media features/query?
Chrome/ium 或 Firefox 浏览器扩展(也称为 add-ons/WebExtensions)真的可以以某种方式覆盖媒体查询的结果吗?
我的意思是,对于 JS window.matchMedia()
这可能很简单:只需注入一个覆盖该 JS 函数的内容脚本。
但是如果使用 "real" CSS 媒体查询(在 css 文件内),这真的不可能,是吗?
我显然可以注入自己的 CSS,但这不是我想要的:我只想触发网站的 CSS 做一些不同的事情,即假设不同的媒体查询结果。
一些背景:如果您想知道为什么,我的用例是覆盖 Firefox 67 中引入的新 prefers-color-scheme
CSS media feature。(它目前不适用于支持 browser/WebExtensions 的任何其他浏览器。)
交叉发布于 Mozilla's Discourse instance。
解决方法
感谢所有其他人,他们为我指出了正确的方向。基本上,没有简单的方法,但您可以:
- 注入内容脚本并迭代
document.styleSheets
,其中所有已解析的样式表都已加载。这是艰巨的任务。
不过这都是只读的,不能直接修改。
- 然后您需要将该结果发送回后台脚本(因为内容脚本无法访问所需的 API)并通过
browser.tabs.insertCSS
手动应用 CSS。
至于第一个任务,这里是 returns 所有 CSS 给定媒体查询的代码片段(一次以功能方式编写,一次仅以结构方式编写)。
你例如可以调用 getCssForMediaQueryFunc("(prefers-color-scheme: dark)")
并获得所有应用于深色方案的 CSS。
/**
* Return CSS from the website for a specific query string.
*
* (functional implementation)
*
* @private
* @param {string} queryString
* @returns {string}
*/
function getCssForMediaQueryFunc(queryString) {
return Array.from(document.styleSheets).reduce((prev, styleSheet) => {
/* workaround for crazy HTML spec throwing an SecurityError here,
* see https://discourse.mozilla.org/t/accessing-some-fonts-css-style-sheet-via-stylesheet/38717?u=rugkx
* and */
try {
styleSheet.cssRules; // eslint-disable-line no-unused-expressions
} catch (e) {
return prev;
}
return Array.from(styleSheet.cssRules).reduce((prev, cssRule) => {
if (cssRule instanceof CSSMediaRule) {
if (cssRule.conditionText === queryString) {
return Array.from(cssRule.cssRules).reduce((prev, subCssRule) => {
return prev + subCssRule.cssText;
}, prev);
}
}
return prev;
}, prev);
}, "");
}
/**
* Return CSS from the website for a specific query string.
*
* @private
* @param {string} queryString
* @returns {string}
*/
function getCssForMediaQuery(queryString) { // eslint-disable-line no-unused-vars
let cssRules = "";
for (const styleSheet of document.styleSheets) {
/* workaround for crazy HTML spec throwing an SecurityError here,
* see https://discourse.mozilla.org/t/accessing-some-fonts-css-style-sheet-via-stylesheet/38717?u=rugkx
* and */
try {
styleSheet.cssRules; // eslint-disable-line no-unused-expressions
} catch (e) {
continue;
}
for (const cssRule of styleSheet.cssRules) {
if (cssRule instanceof CSSMediaRule) {
if (cssRule.conditionText === queryString) {
for (const subCssRule of cssRule.cssRules) {
cssRules = cssRules + subCssRule.cssText;
}
}
}
}
}
return cssRules;
}
(最新代码 should be accessible here)
- 如前所述,您还需要覆盖
window.matchMedia()
to also fake the result of the websites that could use JS for the detection. However, this is also it's own non-trivial task,并且需要将此功能从内容脚本导出到网站。 (假装也很难。)
概念验证
我在 an add-on here, also available on addons.mozilla.org (AMO) 中将这整件事实现为或多或少的概念验证。 (我在这里使用了永久链接,但将来当然可能会更新附加组件。)
未来
显然,这不是一个好方法,所以 I've created a new Bugzilla issue 找到更好的解决方案,例如一个特殊的 Firefox API.
Chrome/ium 或 Firefox 浏览器扩展(也称为 add-ons/WebExtensions)真的可以以某种方式覆盖媒体查询的结果吗?
我的意思是,对于 JS window.matchMedia()
这可能很简单:只需注入一个覆盖该 JS 函数的内容脚本。
但是如果使用 "real" CSS 媒体查询(在 css 文件内),这真的不可能,是吗?
我显然可以注入自己的 CSS,但这不是我想要的:我只想触发网站的 CSS 做一些不同的事情,即假设不同的媒体查询结果。
一些背景:如果您想知道为什么,我的用例是覆盖 Firefox 67 中引入的新 prefers-color-scheme
CSS media feature。(它目前不适用于支持 browser/WebExtensions 的任何其他浏览器。)
交叉发布于 Mozilla's Discourse instance。
解决方法
感谢所有其他人,他们为我指出了正确的方向。基本上,没有简单的方法,但您可以:
- 注入内容脚本并迭代
document.styleSheets
,其中所有已解析的样式表都已加载。这是艰巨的任务。 不过这都是只读的,不能直接修改。 - 然后您需要将该结果发送回后台脚本(因为内容脚本无法访问所需的 API)并通过
browser.tabs.insertCSS
手动应用 CSS。
至于第一个任务,这里是 returns 所有 CSS 给定媒体查询的代码片段(一次以功能方式编写,一次仅以结构方式编写)。
你例如可以调用 getCssForMediaQueryFunc("(prefers-color-scheme: dark)")
并获得所有应用于深色方案的 CSS。
/**
* Return CSS from the website for a specific query string.
*
* (functional implementation)
*
* @private
* @param {string} queryString
* @returns {string}
*/
function getCssForMediaQueryFunc(queryString) {
return Array.from(document.styleSheets).reduce((prev, styleSheet) => {
/* workaround for crazy HTML spec throwing an SecurityError here,
* see https://discourse.mozilla.org/t/accessing-some-fonts-css-style-sheet-via-stylesheet/38717?u=rugkx
* and */
try {
styleSheet.cssRules; // eslint-disable-line no-unused-expressions
} catch (e) {
return prev;
}
return Array.from(styleSheet.cssRules).reduce((prev, cssRule) => {
if (cssRule instanceof CSSMediaRule) {
if (cssRule.conditionText === queryString) {
return Array.from(cssRule.cssRules).reduce((prev, subCssRule) => {
return prev + subCssRule.cssText;
}, prev);
}
}
return prev;
}, prev);
}, "");
}
/**
* Return CSS from the website for a specific query string.
*
* @private
* @param {string} queryString
* @returns {string}
*/
function getCssForMediaQuery(queryString) { // eslint-disable-line no-unused-vars
let cssRules = "";
for (const styleSheet of document.styleSheets) {
/* workaround for crazy HTML spec throwing an SecurityError here,
* see https://discourse.mozilla.org/t/accessing-some-fonts-css-style-sheet-via-stylesheet/38717?u=rugkx
* and */
try {
styleSheet.cssRules; // eslint-disable-line no-unused-expressions
} catch (e) {
continue;
}
for (const cssRule of styleSheet.cssRules) {
if (cssRule instanceof CSSMediaRule) {
if (cssRule.conditionText === queryString) {
for (const subCssRule of cssRule.cssRules) {
cssRules = cssRules + subCssRule.cssText;
}
}
}
}
}
return cssRules;
}
(最新代码 should be accessible here)
- 如前所述,您还需要覆盖
window.matchMedia()
to also fake the result of the websites that could use JS for the detection. However, this is also it's own non-trivial task,并且需要将此功能从内容脚本导出到网站。 (假装也很难。)
概念验证
我在 an add-on here, also available on addons.mozilla.org (AMO) 中将这整件事实现为或多或少的概念验证。 (我在这里使用了永久链接,但将来当然可能会更新附加组件。)
未来
显然,这不是一个好方法,所以 I've created a new Bugzilla issue 找到更好的解决方案,例如一个特殊的 Firefox API.