From 80579328e21999e7cefd7e5298b9a5957de57713 Mon Sep 17 00:00:00 2001 From: Oleg Gaidarenko Date: Mon, 16 May 2016 08:06:09 +0300 Subject: [PATCH] Events: don't execute native stop(Immediate)Propagation from simulation In Firefox, called `stop(Immediate)Propagation` methods, in capturing phase prevents receiving focus Fixes gh-3111 --- src/event.js | 5 +++-- src/event/trigger.js | 22 ++++++++-------------- test/unit/event.js | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 16 deletions(-) diff --git a/src/event.js b/src/event.js index b55f8a35f0b..abc17046530 100644 --- a/src/event.js +++ b/src/event.js @@ -558,6 +558,7 @@ jQuery.Event.prototype = { isDefaultPrevented: returnFalse, isPropagationStopped: returnFalse, isImmediatePropagationStopped: returnFalse, + executeOriginalPropagation: true, preventDefault: function() { var e = this.originalEvent; @@ -573,7 +574,7 @@ jQuery.Event.prototype = { this.isPropagationStopped = returnTrue; - if ( e ) { + if ( e && this.executeOriginalPropagation ) { e.stopPropagation(); } }, @@ -582,7 +583,7 @@ jQuery.Event.prototype = { this.isImmediatePropagationStopped = returnTrue; - if ( e ) { + if ( e && this.executeOriginalPropagation ) { e.stopImmediatePropagation(); } diff --git a/src/event/trigger.js b/src/event/trigger.js index 53acadbeccf..b044a0f5639 100644 --- a/src/event/trigger.js +++ b/src/event/trigger.js @@ -150,26 +150,20 @@ jQuery.extend( jQuery.event, { }, // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events simulate: function( type, elem, event ) { var e = jQuery.extend( new jQuery.Event(), event, { type: type, - isSimulated: true - - // Previously, `originalEvent: {}` was set here, so stopPropagation call - // would not be triggered on donor event, since in our own - // jQuery.event.stopPropagation function we had a check for existence of - // originalEvent.stopPropagation method, so, consequently it would be a noop. - // - // But now, this "simulate" function is used only for events - // for which stopPropagation() is noop, so there is no need for that anymore. - // - // For the compat branch though, guard for "click" and "submit" - // events is still used, but was moved to jQuery.event.stopPropagation function - // because `originalEvent` should point to the original event for the constancy - // with other events and for more focused logic + isSimulated: true, + + // Support: Firefox <= 46 + // In Firefox, called `stop(Immediate)Propagation` methods, in + // capturing phase, prevents receiving focus, + // so we stop only jQuery propagation not the native one instead + executeOriginalPropagation: false } ); diff --git a/test/unit/event.js b/test/unit/event.js index 5d4108c900d..22fd673fa49 100644 --- a/test/unit/event.js +++ b/test/unit/event.js @@ -2821,6 +2821,46 @@ QUnit.test( "originalEvent property for Chrome, Safari, Fx & Edge of simulated e jQuery( "#donor-input" ).trigger( "focus" ); } ); +QUnit.test( + "navite stop(Immediate)Propagation methods should not be called from simulated event", + function( assert ) { + var userAgent = window.navigator.userAgent; + + if ( !( /firefox/i.test( userAgent ) || /safari/i.test( userAgent ) ) ) { + assert.expect( 1 ); + assert.ok( true, "Assertions should run only in Chrome, Safari, Fx & Edge" ); + return; + } + + assert.expect( 2 ); + + var checker = {}; + + var html = "
" + + "
" + + "" + + "
" + + "
"; + + jQuery( "#qunit-fixture" ).append( html ); + + jQuery( "#donor-outer" ) + .on( "focusin", function( event ) { + checker.simple = sinon.stub( event.originalEvent, "stopPropagation" ); + event.stopPropagation(); + } ) + .on( "focusin", function( event ) { + checker.immediate = sinon.stub( event.originalEvent, "stopImmediatePropagation" ); + event.stopImmediatePropagation(); + } ); + + + jQuery( "#donor-input" ).trigger( "focus" ); + assert.strictEqual( checker.simple.called, false ); + assert.strictEqual( checker.immediate.called, false ); + } +); + QUnit[ jQuery.fn.click ? "test" : "skip" ]( "trigger() shortcuts", function( assert ) { assert.expect( 5 );