@@ -28,14 +28,48 @@ const testableMethods = {
28
28
29
29
const untaintedBasePrototype : Partial < BasePrototypeCache > = { } ;
30
30
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
+
31
63
export function getUntaintedPrototype < T extends keyof BasePrototypeCache > (
32
64
key : T ,
33
65
) : BasePrototypeCache [ T ] {
34
66
if ( untaintedBasePrototype [ key ] )
35
67
return untaintedBasePrototype [ key ] as BasePrototypeCache [ T ] ;
36
68
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 ] ;
39
73
40
74
// use list of testable accessors to check if the prototype is tainted
41
75
const accessorNames =
@@ -64,15 +98,15 @@ export function getUntaintedPrototype<T extends keyof BasePrototypeCache>(
64
98
) ;
65
99
66
100
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 ] ;
69
103
}
70
104
71
105
try {
72
106
const iframeEl = document . createElement ( 'iframe' ) ;
73
107
document . body . appendChild ( iframeEl ) ;
74
108
const win = iframeEl . contentWindow ;
75
- if ( ! win ) return defaultObj . prototype as BasePrototypeCache [ T ] ;
109
+ if ( ! win ) return candidate . prototype as BasePrototypeCache [ T ] ;
76
110
77
111
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
78
112
const untaintedObject = ( win as any ) [ key ]
0 commit comments