@@ -18,18 +18,23 @@ window.StyleInjector = window.INJECTED === 1 ? window.StyleInjector : ({
18
18
const PATCH_ID = 'transition-patch' ;
19
19
const kAss = 'adoptedStyleSheets' ;
20
20
const wrappedDoc = document . wrappedJSObject || document ;
21
+ const FF = wrappedDoc !== document ;
21
22
// styles are out of order if any of these elements is injected between them
22
23
// except `style` on our own page as it contains overrides
23
24
const ORDERED_TAGS = new Set ( [ 'head' , 'body' , 'frameset' , ! isExt && 'style' , 'link' ] ) ;
24
25
const docRewriteObserver = RewriteObserver ( updateRoot ) ;
25
26
const docRootObserver = RootObserver ( restoreOrder ) ;
26
27
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
28
28
/** @type {InjectedStyle[] } */
29
29
const list = [ ] ;
30
30
/** @type {Map<number,InjectedStyle> } */
31
31
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 ;
33
38
let root = document . documentElement ;
34
39
let isEnabled = true ;
35
40
let isTransitionPatched = chrome . app && CSS . supports ( 'accent-color' , 'red' ) ; // Chrome 93
@@ -87,13 +92,13 @@ window.StyleInjector = window.INJECTED === 1 ? window.StyleInjector : ({
87
92
88
93
function addElement ( el , before ) {
89
94
if ( ass ) {
90
- const sheets = getAss ( ) ;
91
- let i = sheets . indexOf ( el ) ;
95
+ const sheets = assV2 || wrappedDoc [ kAss ] ;
96
+ let i = assIndexOf ( sheets , el ) ;
92
97
if ( i >= 0 ) el = sheets . splice ( i , 1 ) [ 0 ] ;
93
- i = before ? sheets . indexOf ( before ) : - 1 ;
98
+ i = before ? assIndexOf ( sheets , before ) : - 1 ;
94
99
if ( i >= 0 ) sheets . splice ( i , 0 , el ) ;
95
100
else sheets . push ( el ) ;
96
- if ( sheets !== ass ) wrappedDoc [ kAss ] = sheets ;
101
+ if ( ! assV2 ) wrappedDoc [ kAss ] = sheets ;
97
102
} else {
98
103
updateRoot ( ) . insertBefore ( el , before ) ;
99
104
}
@@ -112,11 +117,11 @@ window.StyleInjector = window.INJECTED === 1 ? window.StyleInjector : ({
112
117
if ( el . remove ) {
113
118
el . remove ( ) ;
114
119
} 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 ) ;
117
122
if ( i >= 0 ) {
118
123
sheets . splice ( i , 1 ) ;
119
- if ( sheets !== ass ) wrappedDoc [ kAss ] = sheets ;
124
+ if ( ! assV2 ) wrappedDoc [ kAss ] = sheets ;
120
125
}
121
126
}
122
127
}
@@ -129,9 +134,12 @@ window.StyleInjector = window.INJECTED === 1 ? window.StyleInjector : ({
129
134
130
135
function replaceAss ( readd ) {
131
136
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
+ }
133
141
if ( readd ) res . push ( ...elems ) ;
134
- ass = wrappedDoc [ kAss ] = res ;
142
+ wrappedDoc [ kAss ] = res ;
135
143
}
136
144
137
145
function applyStyles ( isReplace , { cfg, sections} ) {
@@ -191,19 +199,16 @@ window.StyleInjector = window.INJECTED === 1 ? window.StyleInjector : ({
191
199
return c !== this [ i ] ;
192
200
}
193
201
194
- /** @this {Set} */
195
- function arrItemNotIn ( item ) {
196
- return ! this . has ( item ) ;
197
- }
198
-
199
202
function createStyle ( style ) {
200
203
let el ;
201
204
let { id} = style ;
202
205
if ( ass ) {
203
206
id = MEDIA + id ;
204
207
el = new CSSStyleSheet ( { media : id } ) ;
205
208
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
+ }
207
212
return el ;
208
213
}
209
214
if ( ! creationDoc && ( el = initCreationDoc ( style ) ) ) {
@@ -275,6 +280,19 @@ window.StyleInjector = window.INJECTED === 1 ? window.StyleInjector : ({
275
280
onUpdate ( ) ;
276
281
}
277
282
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
+
278
296
/*
279
297
FF59+ workaround: allow the page to read our sheets, https://github.com/openstyles/stylus/issues/461
280
298
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 : ({
296
314
if ( ok ) return ;
297
315
} catch ( err ) { }
298
316
}
299
- if ( retry && ffCsp ) { // ffCsp bug got fixed
317
+ if ( retry && ffCsp && ( ass = wrappedDoc [ kAss ] ) ) { // ffCsp bug got fixed
318
+ initAss ( ) ;
300
319
console . debug ( 'Stylus switched to document.adoptedStyleSheets due to a strict CSP of the page' ) ;
301
- ass = wrappedDoc [ kAss ] ;
302
320
return createStyle ( style ) ;
303
321
}
304
322
creationDoc = document ;
@@ -319,8 +337,12 @@ window.StyleInjector = window.INJECTED === 1 ? window.StyleInjector : ({
319
337
if ( ! el ) {
320
338
bad = false ;
321
339
} 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
+ ) ) {
324
346
bad = true ;
325
347
break ;
326
348
}
@@ -363,6 +385,7 @@ window.StyleInjector = window.INJECTED === 1 ? window.StyleInjector : ({
363
385
if ( ! ass !== ! cfg . ass ) {
364
386
removeAllElements ( ) ;
365
387
ass = ass ? null : wrappedDoc [ kAss ] ;
388
+ if ( ass ) initAss ( ) ;
366
389
for ( const s of list ) s . el = createStyle ( s ) ;
367
390
addAllElements ( ) ;
368
391
}
0 commit comments