Skip to content

Commit 5e927d5

Browse files
committed
fix adoptedStyleSheets in FF and old Chrome
1 parent c1ebd08 commit 5e927d5

File tree

1 file changed

+44
-21
lines changed

1 file changed

+44
-21
lines changed

content/style-injector.js

+44-21
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,23 @@ window.StyleInjector = window.INJECTED === 1 ? window.StyleInjector : ({
1818
const PATCH_ID = 'transition-patch';
1919
const kAss = 'adoptedStyleSheets';
2020
const wrappedDoc = document.wrappedJSObject || document;
21+
const FF = wrappedDoc !== document;
2122
// styles are out of order if any of these elements is injected between them
2223
// except `style` on our own page as it contains overrides
2324
const ORDERED_TAGS = new Set(['head', 'body', 'frameset', !isExt && 'style', 'link']);
2425
const docRewriteObserver = RewriteObserver(updateRoot);
2526
const docRootObserver = RootObserver(restoreOrder);
2627
const toSafeChar = c => String.fromCharCode(0xFF00 + c.charCodeAt(0) - 0x20);
27-
const getAss = () => Object.isExtensible(ass) ? ass : ass.slice(); // eslint-disable-line no-use-before-define
2828
/** @type {InjectedStyle[]} */
2929
const list = [];
3030
/** @type {Map<number,InjectedStyle>} */
3131
const table = new Map();
32-
let /** @type {CSSStyleSheet[]} */ass;
32+
/** @type {CSSStyleSheet[]} V1: frozen array in old Chrome, the reference changes */
33+
let ass;
34+
/** @type {CSSStyleSheet[]} V2: mutable array, the reference doesn't change */
35+
let assV2;
36+
/** @type {(haystack: CSSStyleSheet[], needle: CSSStyleSheet) => number} */
37+
let assIndexOf;
3338
let root = document.documentElement;
3439
let isEnabled = true;
3540
let isTransitionPatched = chrome.app && CSS.supports('accent-color', 'red'); // Chrome 93
@@ -87,13 +92,13 @@ window.StyleInjector = window.INJECTED === 1 ? window.StyleInjector : ({
8792

8893
function addElement(el, before) {
8994
if (ass) {
90-
const sheets = getAss();
91-
let i = sheets.indexOf(el);
95+
const sheets = assV2 || wrappedDoc[kAss];
96+
let i = assIndexOf(sheets, el);
9297
if (i >= 0) el = sheets.splice(i, 1)[0];
93-
i = before ? sheets.indexOf(before) : -1;
98+
i = before ? assIndexOf(sheets, before) : -1;
9499
if (i >= 0) sheets.splice(i, 0, el);
95100
else sheets.push(el);
96-
if (sheets !== ass) wrappedDoc[kAss] = sheets;
101+
if (!assV2) wrappedDoc[kAss] = sheets;
97102
} else {
98103
updateRoot().insertBefore(el, before);
99104
}
@@ -112,11 +117,11 @@ window.StyleInjector = window.INJECTED === 1 ? window.StyleInjector : ({
112117
if (el.remove) {
113118
el.remove();
114119
} else if (ass) {
115-
const sheets = getAss();
116-
const i = sheets.indexOf(el);
120+
const sheets = assV2 || wrappedDoc[kAss];
121+
const i = assIndexOf(sheets, el);
117122
if (i >= 0) {
118123
sheets.splice(i, 1);
119-
if (sheets !== ass) wrappedDoc[kAss] = sheets;
124+
if (!assV2) wrappedDoc[kAss] = sheets;
120125
}
121126
}
122127
}
@@ -129,9 +134,12 @@ window.StyleInjector = window.INJECTED === 1 ? window.StyleInjector : ({
129134

130135
function replaceAss(readd) {
131136
const elems = list.map(s => s.el);
132-
const res = ass.filter(arrItemNotIn, new Set(elems));
137+
const res = FF ? cloneInto([], wrappedDoc) : []; /* global cloneInto */
138+
for (let arr = assV2 || wrappedDoc[kAss], i = 0, el; i < arr.length && (el = arr[i]); i++) {
139+
if (assIndexOf(elems, el) < 0) res.push(el);
140+
}
133141
if (readd) res.push(...elems);
134-
ass = wrappedDoc[kAss] = res;
142+
wrappedDoc[kAss] = res;
135143
}
136144

137145
function applyStyles(isReplace, {cfg, sections}) {
@@ -191,19 +199,16 @@ window.StyleInjector = window.INJECTED === 1 ? window.StyleInjector : ({
191199
return c !== this[i];
192200
}
193201

194-
/** @this {Set} */
195-
function arrItemNotIn(item) {
196-
return !this.has(item);
197-
}
198-
199202
function createStyle(style) {
200203
let el;
201204
let {id} = style;
202205
if (ass) {
203206
id = MEDIA + id;
204207
el = new CSSStyleSheet({media: id});
205208
setTextAndName(el, style);
206-
for (const {media: m} of ass) if (m.mediaText === id) m.mediaText += '-old';
209+
for (let arr = assV2 || wrappedDoc[kAss], i = 0, m; i < arr.length; i++) {
210+
if ((m = arr[i].media).mediaText === id) m.mediaText += '-old';
211+
}
207212
return el;
208213
}
209214
if (!creationDoc && (el = initCreationDoc(style))) {
@@ -275,6 +280,19 @@ window.StyleInjector = window.INJECTED === 1 ? window.StyleInjector : ({
275280
onUpdate();
276281
}
277282

283+
function initAss() {
284+
if (assIndexOf) return;
285+
if (Object.isExtensible(ass)) assV2 = ass;
286+
assIndexOf = !FF
287+
? Object.call.bind([].indexOf)
288+
: (arr, {media: {mediaText: id}}) => {
289+
for (let i = 0; i < arr.length; i++) {
290+
if (arr[i].media.mediaText === id) return i;
291+
}
292+
return -1;
293+
};
294+
}
295+
278296
/*
279297
FF59+ workaround: allow the page to read our sheets, https://github.com/openstyles/stylus/issues/461
280298
First we're trying the page context document where inline styles may be forbidden by CSP
@@ -296,9 +314,9 @@ window.StyleInjector = window.INJECTED === 1 ? window.StyleInjector : ({
296314
if (ok) return;
297315
} catch (err) {}
298316
}
299-
if (retry && ffCsp) { // ffCsp bug got fixed
317+
if (retry && ffCsp && (ass = wrappedDoc[kAss])) { // ffCsp bug got fixed
318+
initAss();
300319
console.debug('Stylus switched to document.adoptedStyleSheets due to a strict CSP of the page');
301-
ass = wrappedDoc[kAss];
302320
return createStyle(style);
303321
}
304322
creationDoc = document;
@@ -319,8 +337,12 @@ window.StyleInjector = window.INJECTED === 1 ? window.StyleInjector : ({
319337
if (!el) {
320338
bad = false;
321339
} else if (ass) {
322-
for (let i = ass.length - list.length; i < ass.length; i++) {
323-
if (i < 0 || ass[i] !== list[i].el) {
340+
if (!assV2) ass = wrappedDoc[kAss];
341+
for (let len = list.length, base = ass.length - len, i = 0; i < len; i++) {
342+
if (base < 0 || (
343+
!FF ? ass[base + i] !== list[i].el
344+
: ass[base + i].media.mediaText !== list[i].el.media.mediaText
345+
)) {
324346
bad = true;
325347
break;
326348
}
@@ -363,6 +385,7 @@ window.StyleInjector = window.INJECTED === 1 ? window.StyleInjector : ({
363385
if (!ass !== !cfg.ass) {
364386
removeAllElements();
365387
ass = ass ? null : wrappedDoc[kAss];
388+
if (ass) initAss();
366389
for (const s of list) s.el = createStyle(s);
367390
addAllElements();
368391
}

0 commit comments

Comments
 (0)