@@ -164,6 +164,7 @@ export interface ListenerTaskMeta extends TaskData {
164164 handler : NestedEventListenerOrEventListenerObject ;
165165 target : any ;
166166 name : string ;
167+ crossContext : boolean ;
167168 invokeAddFunc : ( addFnSymbol : any , delegate : Task | NestedEventListenerOrEventListenerObject ) => any ;
168169 invokeRemoveFunc :
169170 ( removeFnSymbol : any , delegate : Task | NestedEventListenerOrEventListenerObject ) => any ;
@@ -228,21 +229,47 @@ const defaultListenerMetaCreator = (self: any, args: any[]) => {
228229 handler : args [ 1 ] ,
229230 target : self || _global ,
230231 name : args [ 0 ] ,
232+ crossContext : false ,
231233 invokeAddFunc : function (
232234 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+ }
235244 } 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+ }
237252 }
238253 } ,
239254 invokeRemoveFunc : function (
240255 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+ }
244265 } 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+ }
246273 }
247274 }
248275 } ;
@@ -289,8 +316,9 @@ export function makeZoneAwareAddListener(
289316 // will fail tests prematurely.
290317 validZoneHandler = data . handler && data . handler . toString ( ) === '[object FunctionWrapper]' ;
291318 } 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 ) ;
294322 }
295323 // Ignore special listeners of IE11 & Edge dev tools, see
296324 // https://github.com/angular/zone.js/issues/150
@@ -322,10 +350,32 @@ export function makeZoneAwareRemoveListener(
322350
323351 return function zoneAwareRemoveListener ( self : any , args : any [ ] ) {
324352 const data = metaCreator ( self , args ) ;
353+
325354 data . useCapturing = data . useCapturing || defaultUseCapturing ;
326355 // - Inside a Web Worker, `this` is undefined, the context is `global`
327356 // - When `addEventListener` is called on the global context in strict mode, `this` is undefined
328357 // 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+ }
329379 const eventTask = findExistingRegisteredTask (
330380 data . target , data . handler , data . eventName , data . useCapturing , true ) ;
331381 if ( eventTask ) {
0 commit comments