Skip to content

Commit

Permalink
Event: Increase robustness of an inner native event in leverageNative
Browse files Browse the repository at this point in the history
In Firefox, alert displayed just before blurring an element dispatches
the native blur event twice which tripped the jQuery logic if a jQuery blur
handler was not attached before the trigger call.

This was because the `leverageNative` logic part for triggering first checks if
setup was done before (which, for example, is done if a jQuery handler was
registered before for this element+event pair) and - if it was not - adds
a dummy handler that just returns `true`. The `leverageNative` logic makes that
`true` then saved into private data, replacing the previous `saved` array. Since
`true` passes the truthy check, the second native inner handler treated `true`
as an array, crashing on the `slice` call.

Since it's impossible to call `alert()` in unit tests, simulate the issue by
replacing the `addEventListener` method on a test button with a version that
calls attached blur handlers twice.

Fixes gh-5459
Ref gh-5236
  • Loading branch information
mgol committed Apr 2, 2024
1 parent 284b082 commit efb6e46
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 4 deletions.
5 changes: 1 addition & 4 deletions src/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -547,16 +547,13 @@ function leverageNative( el, type, isSetup ) {
// If this is an inner synthetic event for an event with a bubbling surrogate
// (focus or blur), assume that the surrogate already propagated from triggering
// the native event and prevent that from happening again here.
// This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the
// bubbling surrogate propagates *after* the non-bubbling base), but that seems
// less bad than duplication.
} else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) {
event.stopPropagation();
}

// If this is a native event triggered above, everything is now in order
// Fire an inner synthetic event with the original arguments
} else if ( saved ) {
} else if ( saved.length ) {

// ...and capture the result
dataPriv.set( this, type, jQuery.event.trigger(
Expand Down
34 changes: 34 additions & 0 deletions test/unit/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -3493,6 +3493,40 @@ QUnit.test( "trigger(focus) fires native & jQuery handlers (gh-5015)", function(
input.trigger( "focus" );
} );

QUnit.test( "duplicate native blur doesn't crash (gh-5459)", function( assert ) {
assert.expect( 1 );

var button = jQuery( "<button></button>" ),
nativeAddEvent = button[ 0 ].addEventListener;
button.appendTo( "#qunit-fixture" );

button.on( "focus", function() {
button.trigger( "blur" );
assert.ok( true, "Did not crash" );
} );

// Support: Firefox 124+
// In Firefox, alert displayed just before blurring an element
// dispatches the native blur event twice which tripped the jQuery
// logic. We cannot call `alert()` in unit tests; simulate the
// behavior by overwriting the native `addEventListener` with
// a version that calls blur handlers twice.
button[ 0 ].addEventListener = function( eventName, handler ) {
var finalHandler;
if ( eventName === "blur" ) {
finalHandler = function wrappedHandler() {
handler.apply( this, arguments );
return handler.apply( this, arguments );
};
} else {
finalHandler = handler;
}
return nativeAddEvent.call( this, eventName, finalHandler );
};

button.trigger( "focus" );
} );

// TODO replace with an adaptation of
// https://github.com/jquery/jquery/pull/1367/files#diff-a215316abbaabdf71857809e8673ea28R2464
( function() {
Expand Down

0 comments on commit efb6e46

Please sign in to comment.