For Angular 14 + Material there's another issue with media matcher style tag injection below are the monkey patches to these injectors to provide our custom nonce
Media Matcher override
inline-styles-csp\media-matcher.ts
import{Inject,Injectable}from'@angular/core';import{Platform}from'@angular/cdk/platform';import{CustomDomSharedStylesHost}from'./shared_styles_host';import{ɵDomSharedStylesHost}from'@angular/platform-browser';/** Global registry for all dynamically-created, injected media queries. */constmediaQueriesForWebkitCompatibility:Set<string>=newSet<string>();/** Style tag that holds all of the dynamically-created media queries. */letmediaQueryStyleNode:HTMLStyleElement|undefined;@Injectable()exportclassCustomMediaMatcher{/** The internal matchMedia method to return back a MediaQueryList like object. */private_matchMedia:(query:string)=>MediaQueryList;constructor(private_platform:Platform,@Inject(ɵDomSharedStylesHost)privatecustomDomSharedStylesHost:CustomDomSharedStylesHost){this._matchMedia=this._platform.isBrowser&&window.matchMedia?// matchMedia is bound to the window scope intentionally as it is an illegal invocation to// call it from a different scope.window.matchMedia.bind(window):noopMatchMedia;}/**
* Evaluates the given media query and returns the native MediaQueryList from which results
* can be retrieved.
* Confirms the layout engine will trigger for the selector query provided and returns the
* MediaQueryList for the query provided.
*/matchMedia(query:string):MediaQueryList{if(this._platform.WEBKIT||this._platform.BLINK){createEmptyStyleRule(query,this.customDomSharedStylesHost.nonce);}returnthis._matchMedia(query);}}/**
* Creates an empty stylesheet that is used to work around browser inconsistencies related to
* `matchMedia`. At the time of writing, it handles the following cases:
* 1. On WebKit browsers, a media query has to have at least one rule in order for `matchMedia`
* to fire. We work around it by declaring a dummy stylesheet with a `@media` declaration.
* 2. In some cases Blink browsers will stop firing the `matchMedia` listener if none of the rules
* inside the `@media` match existing elements on the page. We work around it by having one rule
* targeting the `body`. See https://github.com/angular/components/issues/23546.
*/functioncreateEmptyStyleRule(query:string,nonce:string|null|undefined){if(mediaQueriesForWebkitCompatibility.has(query)){return;}try{if(!mediaQueryStyleNode){mediaQueryStyleNode=document.createElement('style');mediaQueryStyleNode.setAttribute('type','text/css');if(!!nonce){mediaQueryStyleNode.setAttribute('nonce',nonce);}document.head!.appendChild(mediaQueryStyleNode);}if(mediaQueryStyleNode.sheet){mediaQueryStyleNode.sheet.insertRule(`@media ${query} {body{ }}`,0);mediaQueriesForWebkitCompatibility.add(query);}}catch(e){console.error(e);}}/** No-op matchMedia replacement for non-browser platforms. */functionnoopMatchMedia(query:string):MediaQueryList{// Use `as any` here to avoid adding additional necessary properties for// the noop matcher.return{matches:query==='all'||query==='',media:query,addListener:()=>{},removeListener:()=>{},}asany;}
Updated shared styles host
inline-styles-csp\shared_styles_host.ts
import{DOCUMENT,ɵgetDOMasgetDOM}from'@angular/common';import{Inject,Injectable,OnDestroy}from'@angular/core';import{ɵSharedStylesHost}from'@angular/platform-browser';@Injectable()exportclassCustomDomSharedStylesHostextendsɵSharedStylesHostimplementsOnDestroy{// Maps all registered host nodes to a list of style nodes that have been added to the host node.private_hostNodes=newMap<Node,Node[]>();private_nonce:string|null|undefined=null;constructor(@Inject(DOCUMENT)private_doc:any,@Inject('cspMetaSelector')private_metaCSPTag:string){super();this._hostNodes.set(_doc.head,[]);this._setCSPNonce();}private_addStylesToHost(styles:Set<string>,host:Node,styleNodes:Node[]):void{styles.forEach((style:string)=>{conststyleEl=this._doc.createElement('style');styleEl.textContent=style;if(!style.includes('without-nonce')&&this._nonce){styleEl.setAttribute('nonce',this._nonce);}styleNodes.push(host.appendChild(styleEl));});if(this._nonce){this._removeCSPNonceHeader();}}private_setCSPNonce():void{this._nonce=document.querySelector(this._metaCSPTag)?.getAttribute('content');}private_removeCSPNonceHeader():void{document.querySelector(this._metaCSPTag)?.remove();}addHost(hostNode:Node):void{conststyleNodes:Node[]=[];this._hostNodes.set(hostNode,styleNodes);}removeHost(hostNode:Node):void{conststyleNodes=this._hostNodes.get(hostNode);if(styleNodes){styleNodes.forEach(removeStyle);}this._hostNodes.delete(hostNode);}overrideonStylesAdded(additions:Set<string>):void{this._hostNodes.forEach((styleNodes,hostNode)=>{this._addStylesToHost(additions,hostNode,styleNodes);});}getnonce():string|null|undefined{returnthis._nonce;}ngOnDestroy():void{this._hostNodes.forEach((styleNodes)=>styleNodes.forEach(removeStyle));}}functionremoveStyle(styleNode:Node):void{getDOM().remove(styleNode);}
For Angular 14 + Material there's another issue with media matcher style tag injection below are the monkey patches to these injectors to provide our custom nonce
Media Matcher override
inline-styles-csp\media-matcher.tsUpdated shared styles host
inline-styles-csp\shared_styles_host.tsUpdated module
inline-styles-csp\inline-styles-csp.module.tsthanks,it is useful for me, but how implement inject the CSP Nonce value in the inline style generated by the third-party library in angular?