diff options
author | Michał Gołębiowski-Owczarek <m.goleb@gmail.com> | 2024-05-20 18:05:19 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-20 18:05:19 +0200 |
commit | 527fb3dcf0dcde69302a741dfc61cbfa58e99eb0 (patch) | |
tree | 1e3a6845a1c5cb3f70cc80584579c19f992149be /test/unit | |
parent | 5880e02707dcefc4ec527bd1c56f64b8b0eba391 (diff) | |
download | jquery-527fb3dcf0dcde69302a741dfc61cbfa58e99eb0.tar.gz jquery-527fb3dcf0dcde69302a741dfc61cbfa58e99eb0.zip |
Event: Increase robustness of an inner native event in leverageNative
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 checked 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 - added
a dummy handler that just returned `true`. The `leverageNative` logic made that
`true` then saved into private data, replacing the previous `saved` array. Since
`true` passed the truthy check, the second native inner handler treated `true`
as an array, crashing on the `slice` call.
The same issue could happen if a handler returning `true` is attached before
triggering. A bare `length` check would not be enough as the user handler may
return an array-like as well. To remove this potential data shape clash, capture
the inner result in an object with a `value` property instead of saving it
directly.
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
Closes gh-5466
Ref gh-5236
Diffstat (limited to 'test/unit')
-rw-r--r-- | test/unit/event.js | 58 |
1 files changed, 58 insertions, 0 deletions
diff --git a/test/unit/event.js b/test/unit/event.js index ac309d22d..111b1a18b 100644 --- a/test/unit/event.js +++ b/test/unit/event.js @@ -3502,6 +3502,64 @@ 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( 4 ); + + function patchAddEventListener( elem ) { + var nativeAddEvent = elem[ 0 ].addEventListener; + + // 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. + // + // Such a simulation allows us to test whether `leverageNative` + // logic correctly differentiates between data saved by outer/inner + // handlers, so it's useful even without the Firefox bug. + elem[ 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 ); + }; + } + + function runTest( handler, message ) { + var button = jQuery( "<button></button>" ); + + patchAddEventListener( button ); + button.appendTo( "#qunit-fixture" ); + + if ( handler ) { + button.on( "blur", handler ); + } + button.on( "focus", function() { + button.trigger( "blur" ); + assert.ok( true, "Did not crash (" + message + ")" ); + } ); + button.trigger( "focus" ); + } + + runTest( undefined, "no prior handler" ); + runTest( function() { + return true; + }, "prior handler returning true" ); + runTest( function() { + return { length: 42 }; + }, "prior handler returning an array-like" ); + runTest( function() { + return { value: 42 }; + }, "prior handler returning `{ value }`" ); +} ); + // TODO replace with an adaptation of // https://github.com/jquery/jquery/pull/1367/files#diff-a215316abbaabdf71857809e8673ea28R2464 ( function() { |