@@ -164,6 +164,7 @@ export interface ListenerTaskMeta extends TaskData {
164
164
handler : NestedEventListenerOrEventListenerObject ;
165
165
target : any ;
166
166
name : string ;
167
+ crossContext : boolean ;
167
168
invokeAddFunc : ( addFnSymbol : any , delegate : Task | NestedEventListenerOrEventListenerObject ) => any ;
168
169
invokeRemoveFunc :
169
170
( removeFnSymbol : any , delegate : Task | NestedEventListenerOrEventListenerObject ) => any ;
@@ -228,21 +229,47 @@ const defaultListenerMetaCreator = (self: any, args: any[]) => {
228
229
handler : args [ 1 ] ,
229
230
target : self || _global ,
230
231
name : args [ 0 ] ,
232
+ crossContext : false ,
231
233
invokeAddFunc : function (
232
234
addFnSymbol : any , delegate : Task | NestedEventListenerOrEventListenerObject ) {
233
- if ( delegate && ( < Task > delegate ) . invoke ) {
234
- return this . target [ addFnSymbol ] ( this . eventName , ( < Task > delegate ) . invoke , this . useCapturing ) ;
235
+ // check if the data is cross site context, if it is, fallback to
236
+ // remove the delegate directly and try catch error
237
+ if ( ! this . crossContext ) {
238
+ if ( delegate && ( < Task > delegate ) . invoke ) {
239
+ return this . target [ addFnSymbol ] (
240
+ this . eventName , ( < Task > delegate ) . invoke , this . useCapturing ) ;
241
+ } else {
242
+ return this . target [ addFnSymbol ] ( this . eventName , delegate , this . useCapturing ) ;
243
+ }
235
244
} else {
236
- return this . target [ addFnSymbol ] ( this . eventName , delegate , this . useCapturing ) ;
245
+ // add a if/else branch here for performance concern, for most times
246
+ // cross site context is false, so we don't need to try/catch
247
+ try {
248
+ return this . target [ addFnSymbol ] ( this . eventName , delegate , this . useCapturing ) ;
249
+ } catch ( err ) {
250
+ // do nothing here is fine, because objects in a cross-site context are unusable
251
+ }
237
252
}
238
253
} ,
239
254
invokeRemoveFunc : function (
240
255
removeFnSymbol : any , delegate : Task | NestedEventListenerOrEventListenerObject ) {
241
- if ( delegate && ( < Task > delegate ) . invoke ) {
242
- return this . target [ removeFnSymbol ] (
243
- this . eventName , ( < Task > delegate ) . invoke , this . useCapturing ) ;
256
+ // check if the data is cross site context, if it is, fallback to
257
+ // remove the delegate directly and try catch error
258
+ if ( ! this . crossContext ) {
259
+ if ( delegate && ( < Task > delegate ) . invoke ) {
260
+ return this . target [ removeFnSymbol ] (
261
+ this . eventName , ( < Task > delegate ) . invoke , this . useCapturing ) ;
262
+ } else {
263
+ return this . target [ removeFnSymbol ] ( this . eventName , delegate , this . useCapturing ) ;
264
+ }
244
265
} else {
245
- return this . target [ removeFnSymbol ] ( this . eventName , delegate , this . useCapturing ) ;
266
+ // add a if/else branch here for performance concern, for most times
267
+ // cross site context is false, so we don't need to try/catch
268
+ try {
269
+ return this . target [ removeFnSymbol ] ( this . eventName , delegate , this . useCapturing ) ;
270
+ } catch ( err ) {
271
+ // do nothing here is fine, because objects in a cross-site context are unusable
272
+ }
246
273
}
247
274
}
248
275
} ;
@@ -289,8 +316,9 @@ export function makeZoneAwareAddListener(
289
316
// will fail tests prematurely.
290
317
validZoneHandler = data . handler && data . handler . toString ( ) === '[object FunctionWrapper]' ;
291
318
} catch ( error ) {
292
- // Returning nothing here is fine, because objects in a cross-site context are unusable
293
- return ;
319
+ // we can still try to add the data.handler even we are in cross site context
320
+ data . crossContext = true ;
321
+ return data . invokeAddFunc ( addFnSymbol , data . handler ) ;
294
322
}
295
323
// Ignore special listeners of IE11 & Edge dev tools, see
296
324
// https://github.com/angular/zone.js/issues/150
@@ -322,10 +350,32 @@ export function makeZoneAwareRemoveListener(
322
350
323
351
return function zoneAwareRemoveListener ( self : any , args : any [ ] ) {
324
352
const data = metaCreator ( self , args ) ;
353
+
325
354
data . useCapturing = data . useCapturing || defaultUseCapturing ;
326
355
// - Inside a Web Worker, `this` is undefined, the context is `global`
327
356
// - When `addEventListener` is called on the global context in strict mode, `this` is undefined
328
357
// see https://github.com/angular/zone.js/issues/190
358
+ let delegate : EventListener = null ;
359
+ if ( typeof data . handler == 'function' ) {
360
+ delegate = < EventListener > data . handler ;
361
+ } else if ( data . handler && ( < EventListenerObject > data . handler ) . handleEvent ) {
362
+ delegate = ( event ) => ( < EventListenerObject > data . handler ) . handleEvent ( event ) ;
363
+ }
364
+ let validZoneHandler = false ;
365
+ try {
366
+ // In cross site contexts (such as WebDriver frameworks like Selenium),
367
+ // accessing the handler object here will cause an exception to be thrown which
368
+ // will fail tests prematurely.
369
+ validZoneHandler = data . handler && data . handler . toString ( ) === '[object FunctionWrapper]' ;
370
+ } catch ( error ) {
371
+ data . crossContext = true ;
372
+ return data . invokeRemoveFunc ( symbol , data . handler ) ;
373
+ }
374
+ // Ignore special listeners of IE11 & Edge dev tools, see
375
+ // https://github.com/angular/zone.js/issues/150
376
+ if ( ! delegate || validZoneHandler ) {
377
+ return data . invokeRemoveFunc ( symbol , data . handler ) ;
378
+ }
329
379
const eventTask = findExistingRegisteredTask (
330
380
data . target , data . handler , data . eventName , data . useCapturing , true ) ;
331
381
if ( eventTask ) {
0 commit comments