9

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

kofifus
  • 17,260
  • 17
  • 99
  • 173
  • 1
    Have you tried using the function `loadCSSCors` from [this answer](https://stackoverflow.com/a/18471848/7663972) ? – camelsWriteInCamelCase Nov 19 '17 at 04:18
  • FWIW you can use a lightweight css parser or do a straight text replace... – wOxxOm Nov 19 '17 at 05:42
  • camelsWriteInCamelCase and wOxxOm you both imply manually loading the external css file but that breaks all relative urls in it – kofifus Nov 19 '17 at 09:47
  • 1
    You can use `loadCSSCors` function + [some regular expression](https://stackoverflow.com/a/7544757/7663972) to replace relative paths to absolute paths in external CSS files. – camelsWriteInCamelCase Nov 19 '17 at 18:00
  • 1
    Do you want to modify external styles? Why you just don't create your own style with the same rule and override the external? – Maxwell s.c Nov 23 '17 at 11:03
  • because as said above if I copy the rules from the external to my own style all relative URLs are broken :( – kofifus Nov 24 '17 at 00:58
  • @kofifus, I'm still unclear on why Maxwell s.c's suggestion doesn't work. Can you create your own style sheet with named style and append (not replace!) those to the elements you are trying to modify? How would that break local links? It should only apply your style on top of original one. That's what I did for chrome extension to add control elements and highlight. Can share code snippets if that works, otherwise wouldn't bother writing useless answer. LMK – isp-zax Nov 27 '17 at 06:50
  • thx isp-zak, for that I will need to fetch and parse the external css file, find out all selectors with the attributes (ie background color) then create internal styles that exactly override them, considering how complex CSS selectors/pseudo/precedence/etc are I don't think this is a simple task ... – kofifus Nov 27 '17 at 09:37
  • did you check that already? https://developer.chrome.com/extensions/contentSecurityPolicy#relaxing-remote-script – Robin F. Nov 27 '17 at 10:35
  • 1
    Due to CORS, I believe the only way to get around this is to use a middleman server to get the contents of the stylesheet. – Cole Nov 27 '17 at 18:35

2 Answers2

0

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.

N-ate
  • 6,051
  • 2
  • 40
  • 48
  • the code was just to demonstrate the functionality of iterate ... ofcourse it will not be there in an actual extension – kofifus Nov 28 '17 at 21:31
0

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

fregante
  • 29,050
  • 14
  • 119
  • 159