import { arrayFrom , stringReplace , toSlug , supportRules , getCssRules } from './util' ; import { doc , domainProp , onRetryProp , StyleElementCtor } from './constants' ; import { getCurrentDomain , DomainMap } from './url' ; import { InnerAssetsRetryOptions } from './assets-retry' ; type UrlProperty = 'backgroundImage' | 'borderImage' | 'listStyleImage' ; const handledStylesheets = { } ; const handledStyleTags = [ ] ; const processRules = function ( name , rule , styleSheet , styleRules , opts ) { const domainMap = opts [ domainProp ] ; const onRetry = opts [ onRetryProp ] ; const targetRule = rule . style && rule . style [ name ] ; if ( ! targetRule ) { return ; } if ( /^url\(["']?data:/ . test ( targetRule ) ) { return ; } const [ _ , originalUrl ] = targetRule . match ( /^url\(["']?(.+?)["']?\)/ ) || [ ] ; if ( ! originalUrl ) { return ; } const currentDomain = getCurrentDomain ( originalUrl , domainMap ) ; if ( ! currentDomain || ! domainMap [ currentDomain ] ) { return ; } const urlList = Object . keys ( domainMap ) . map ( domain => { const newUrl = stringReplace ( originalUrl , currentDomain , domain ) ; const userModifiedUrl = onRetry ( newUrl , originalUrl , null ) ; return `url("${ userModifiedUrl }")` ; } ) . join ( ',' ) ; const cssText = rule . selectorText + `{ ${ toSlug ( name ) }: ${ urlList } !important; }` ; try { styleSheet . insertRule ( cssText , styleRules . length ) ; } catch ( _ ) { styleSheet . insertRule ( cssText , 0 ) ; } } ; const processStyleSheets = ( styleSheets , opts ) => { const urlProperties = [ 'backgroundImage' , 'borderImage' , 'listStyleImage' ] ; styleSheets . forEach ( ( styleSheet ) => { const rules = getCssRules ( styleSheet ) ; if ( rules === null ) { return ; } const styleRules = arrayFrom ( rules ) as CSSStyleRule [ ] ; styleRules . forEach ( rule => { urlProperties . forEach ( cssProperty => { processRules ( cssProperty , rule , styleSheet , styleRules , opts ) ; } ) ; } ) ; if ( styleSheet . href ) { handledStylesheets [ styleSheet . href ] = true ; } if ( styleSheet . ownerNode instanceof StyleElementCtor ) { handledStyleTags . push ( styleSheet . ownerNode ) ; } } ) ; } ; const getStyleSheetsToBeHandled = ( styleSheets , domainMap ) : CSSStyleSheet [ ] => { const sheetsArray = arrayFrom ( styleSheets ) as unknown as CSSStyleSheet [ ] ; return sheetsArray . filter ( styleSheet => { if ( ! supportRules ( styleSheet ) ) { return false ; } if ( ! styleSheet . href ) { const ownerNode = styleSheet . ownerNode ; if ( ownerNode instanceof StyleElementCtor && handledStyleTags . indexOf ( ownerNode ) > - 1 ) { return false ; } return true ; } if ( handledStylesheets [ styleSheet . href ] ) { return false ; } const currentDomain = getCurrentDomain ( styleSheet . href , domainMap ) ; return ! ! currentDomain ; } ) ; } ; export default function initCss ( opts ) { const supportStyleSheets = doc . styleSheets ; const domainMap = opts [ domainProp ] ; if ( ! supportStyleSheets ) return false ; setInterval ( ( ) => { const newStyleSheets = getStyleSheetsToBeHandled ( doc . styleSheets , domainMap ) ; if ( newStyleSheets . length > 0 ) { processStyleSheets ( newStyleSheets , opts ) ; } } , 250 ) ; }