@@ -28,14 +28,48 @@ const testableMethods = {
2828
2929const untaintedBasePrototype : Partial < BasePrototypeCache > = { } ;
3030
31+ type WindowWithZone = typeof globalThis & {
32+ Zone ?: {
33+ __symbol__ ?: ( key : string ) => string ;
34+ } ;
35+ } ;
36+
37+ type WindowWithUnpatchedSymbols = typeof globalThis &
38+ Record < string , TypeofPrototypeOwner > ;
39+
40+ /*
41+ Angular zone patches many things and can pass the untainted checks below, causing performance issues
42+ Angular zone, puts the unpatched originals on the window, and the names for hose on the zone object.
43+ So, we get the unpatched versions from the window object if they exist.
44+ You can rename Zone, but this is a good enough proxy to avoid going to an iframe to get the untainted versions.
45+ see: https://github.com/angular/angular/issues/26948
46+ */
47+ function angularZoneUnpatchedAlternative ( key : keyof BasePrototypeCache ) {
48+ const angularUnpatchedVersionSymbol = (
49+ globalThis as WindowWithZone
50+ ) ?. Zone ?. __symbol__ ?.( key ) ;
51+ if (
52+ angularUnpatchedVersionSymbol &&
53+ ( globalThis as WindowWithUnpatchedSymbols ) [ angularUnpatchedVersionSymbol ]
54+ ) {
55+ return ( globalThis as WindowWithUnpatchedSymbols ) [
56+ angularUnpatchedVersionSymbol
57+ ] ;
58+ } else {
59+ return undefined ;
60+ }
61+ }
62+
3163export function getUntaintedPrototype < T extends keyof BasePrototypeCache > (
3264 key : T ,
3365) : BasePrototypeCache [ T ] {
3466 if ( untaintedBasePrototype [ key ] )
3567 return untaintedBasePrototype [ key ] as BasePrototypeCache [ T ] ;
3668
37- const defaultObj = globalThis [ key ] as TypeofPrototypeOwner ;
38- const defaultPrototype = defaultObj . prototype as BasePrototypeCache [ T ] ;
69+ const candidate =
70+ angularZoneUnpatchedAlternative ( key ) ||
71+ ( globalThis [ key ] as TypeofPrototypeOwner ) ;
72+ const defaultPrototype = candidate . prototype as BasePrototypeCache [ T ] ;
3973
4074 // use list of testable accessors to check if the prototype is tainted
4175 const accessorNames =
@@ -64,15 +98,15 @@ export function getUntaintedPrototype<T extends keyof BasePrototypeCache>(
6498 ) ;
6599
66100 if ( isUntaintedAccessors && isUntaintedMethods ) {
67- untaintedBasePrototype [ key ] = defaultObj . prototype as BasePrototypeCache [ T ] ;
68- return defaultObj . prototype as BasePrototypeCache [ T ] ;
101+ untaintedBasePrototype [ key ] = candidate . prototype as BasePrototypeCache [ T ] ;
102+ return candidate . prototype as BasePrototypeCache [ T ] ;
69103 }
70104
71105 try {
72106 const iframeEl = document . createElement ( 'iframe' ) ;
73107 document . body . appendChild ( iframeEl ) ;
74108 const win = iframeEl . contentWindow ;
75- if ( ! win ) return defaultObj . prototype as BasePrototypeCache [ T ] ;
109+ if ( ! win ) return candidate . prototype as BasePrototypeCache [ T ] ;
76110
77111 // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
78112 const untaintedObject = ( win as any ) [ key ]
0 commit comments