-
Notifications
You must be signed in to change notification settings - Fork 20.5k
Event: Increase robustness of an inner native event in leverageNative #5466
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
| // 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part of the comment is no longer valid since the spec now mandates what browsers ship, i.e. that the bubbling events are fired after the non-bubbling ones.
| // 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 ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In some very edge-case'y code, it's possible to have a blur handler returning an array and falling into this check, destructuring the incorrect array. But guarding against that would be way more complex and I doubt many people will hit such an issue.
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 jquerygh-5459 Closes jquerygh-5466 Ref jquerygh-5236
efb6e46 to
ee787ff
Compare
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 jquerygh-5459 Closes jquerygh-5466 Ref jquerygh-5236
ee787ff to
53b647c
Compare
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 jquerygh-5459 Closes jquerygh-5466 Ref jquerygh-5236
53b647c to
e66d718
Compare
|
@timmywil @gibson042 PR updated & ready for another review. |
| var result, | ||
| saved = dataPriv.get( this, type ); | ||
|
|
||
| if ( ( event.isTrigger & 1 ) && this[ type ] ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should explain the relevance of saved up here, rather than by implication scattered throughout the code branches.
| if ( ( event.isTrigger & 1 ) && this[ type ] ) { | |
| // This controller function is invoked under multiple circumstances, | |
| // differentiated by the stored value in `saved`: | |
| // 1. For an outer synthetic .trigger()ed event (detected by | |
| // `event.isTrigger & 1` and non-array `saved`), it records arguments | |
| // as an array and fires an [inner] native event to prompt state | |
| // changes that should be observed by registered listeners (such as | |
| // checkbox toggling and focus updating), then clears the stored value. | |
| // 2. For an [inner] native event (detected by `saved` being | |
| // an array), it triggers an inner synthetic event, records the | |
| // result, and preempts propagation to further jQuery listeners. | |
| // 3. For an inner synthetic event (detected by `event.isTrigger & 1` and | |
| // array `saved`), it prevents double-propagation of surrogate events | |
| // but otherwise allows everything to proceed (particularly including | |
| // further listeners). | |
| if ( ( event.isTrigger & 1 ) && this[ type ] ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, can we be more specific than "non-array saved" in the first case? What other types might it be?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added Richard's comment plus a short listing of possible saved data types, PTAL.
| // There will always be at least one argument (an event object), | ||
| // so this array will not be confused with a leftover capture object. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should experiment with checking isArray(saved) rather than saved.length (as both point replacements in the if conditions and as a dedicated variable like savedIsArgs = Array.isArray(saved)). If there's no size increase, then this commentary would be unnecessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changing saved.length checks to Array.isArray( saved ) results in a size increase:
raw gz Filename
+16 +7 dist/jquery.min.js
+16 +1 dist/jquery.slim.min.js
+16 +4 dist-module/jquery.module.min.js
+16 0 dist-module/jquery.slim.module.min.js
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 jquerygh-5459 Closes jquerygh-5466 Ref jquerygh-5236
e66d718 to
4b3bb8a
Compare
|
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 jquerygh-5459 Closes jquerygh-5466 Ref jquerygh-5236
4b3bb8a to
1952220
Compare
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 (cherry picked from commit 527fb3d)
Summary
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
leverageNativelogic part for triggering first checked ifsetup 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. TheleverageNativelogic made thattruethen saved into private data, replacing the previoussavedarray. Sincetruepassed the truthy check, the second native inner handler treatedtrueas an array, crashing on the
slicecall.The same issue could happen if a handler returning
trueis attached beforetriggering. A bare
lengthcheck would not be enough as the user handler mayreturn an array-like as well. To remove this potential data shape clash, capture
the inner result in an object with a
valueproperty instead of saving itdirectly.
Since it's impossible to call
alert()in unit tests, simulate the issue byreplacing the
addEventListenermethod on a test button with a version thatcalls attached blur handlers twice.
Fixes gh-5459
Ref gh-5236
Checklist
If needed, a docs issue/PR was created at https://github.com/jquery/api.jquery.com