'Changing external CSS from chrome extension

I am writing a chrome extension that needs to iterate over ALL stylesheets in the page it is injected into and modify certain styles.

I iterate/modify styles for example like this:

const iterate = (doc, f) => {
  for (const styleSheet of doc.styleSheets) {
    const rules = styleSheet.rules || styleSheet.cssRules;
    if (!rules) continue;
    for (const cssRule of rules) {
      if (!cssRule.style) continue;
      const selector = cssRule.selectorText, style = cssRule.style;
      if (!selector || !style.cssText) continue;
      f(style);
    }
  }
}

document.addEventListener("DOMContentLoaded", e => {
  setTimeout(() => {
    iterate(document, style => {
      if (style.getPropertyValue('background-color')) style.setProperty('background-color', 'yellow');
    });
  }, 1000);
});
div {
  background-color: red;
}
<div>hello</div>

The problem I am having is that it seems that external css do not get included.

For example if I inject my extension into stackoverflow.com, which has:

<link rel="stylesheet" type="text/css" href="https://cdn.sstatic.net/Sites/stackoverflow/all.css?v=cfd0b49a38a7">

Then the styles from all.css are not iterated over.

How can I iterate/modifies external styles ?

Note 1 - I tried to manually fetch those links rel and put them into internal style tags but that breaks any relative urls in those files (i.e, background-image: url('path/image.jpg') )

Note 2 - my manifest has "permissions": [ "http://*/*", "https://*/*" ]

Note 3 - as this is for a Chrome extension I am happy with a Chrome only solution



Solution 1:[1]

This may be a mistake in your sample code, but it is apparent that it is unlikely to inject and fetch your stylesheet prior to the DOMContentLoaded event + 1 second. Try changing your setTimeout wait to 20 seconds and see what happens.

If this solves your problem then the next step is to create a better solution that waits until your stylesheet shows up before iterating.

Solution 2:[2]

Your extension’s permissions don't override the pages’ permission for content that's already on the page, like the stylesheet you're trying to read and modify.

What you can do is fetch the external stylesheets, like:

const response = await fetch('https://cdn.sstatic.net/Sites/stackoverflow/all.css');
const css = await response.text();
const style = document.createElement('style');
style.textContent = css;

Then you can iterate the rules in this new style element and inject only the necessary styles into the page, so you probably won't have to worry about changing relatives URLs in the stylesheet. If that's a problem you'd also have to manually rewrite those URLs as well.

Note: Nowadays you'll probably also encounter CORB issues with the code above, so you'll have to fetch through the background: https://stackoverflow.com/a/56929473/288906

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 N-ate
Solution 2 fregante