In Code.org's App Lab editor, we recently started seeing this error in Chrome 64:
Uncaught DOMException: Failed to read the 'rules' property from 'CSSStyleSheet'
The error is thrown in this function designed to detect whether CSS media queries are being used by the browser, on the line that includes styleSheets[i].cssRules
.
/**
* IE9 throws an exception when trying to access the media field of a stylesheet
*/
export function browserSupportsCssMedia() {
var styleSheets = document.styleSheets;
for (var i = 0; i < styleSheets.length; i++) {
var rules = styleSheets[i].cssRules || styleSheets[i].rules;
try {
if (rules.length > 0) {
// see if we can access media
rules[0].media;
}
} catch (e) {
return false;
}
}
return true;
}
The problem has been seen on Windows, OSX, Ubuntu, and ChromeOS; on Chrome versions 64.0.3282.167 and 64.0.3282.186. However, we've also seen this problem not happen on exactly the same Chrome version and platform - and we don't seem to be able to reproduce the problem in an incognito window.
What is the root cause of this error?
This was a good story and a new 'gotcha' for web developers, so I just had to share:
Chrome 64.0.3282.0 (released January 2018, full change list) introduced a change to security rules for stylesheets. I'm irritated that I couldn't find this change in any changelog less detailed than the full commit list.
Commit a4ebe08 in Chromium is described:
Update behavior of CSSStyleSheet to match spec for Security origin
Spec is here: https://www.w3.org/TR/cssom-1/#the-cssstylesheet-interface
Updated: the following methods now throw a SecurityError if the style sheet is not accessible:
- cssRules() / rules()
- insertRule()
- deleteRule()
This commit is a fix for the bug Security: Inconsistent CORS implementation regarding CSS and the link element. The linked W3C spec describes in detail where use of the CSS Object Model requires same-origin access.
All that said, why was this issue showing up in App Lab? We shouldn't experience any CORS issues, because we only load stylesheets from our own origin:
The final clue was that we couldn't reproduce this issue in a private tab. We started looking at Chrome extensions and realized that some affected users had the Loom Video Recorder extension enabled, which seems to inject its own CSS into the page. Since our (naïve) function was iterating through all loaded stylesheets, it was attempting to access this stylesheet injected by the extension and thus causing the CORS error.
That said, there's still some open issues and debate around this change in Chrome:
try/catch
.To fix our problem, we just tore out the entire function. We don't support IE9 anymore, and we know all of our supported browsers handle media queries properly.
Related (but not quite duplicate) questions: