-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Footnotes: Fix recursion into updating attributes when attributes is not an object #53257
Footnotes: Fix recursion into updating attributes when attributes is not an object #53257
Conversation
@@ -221,6 +221,15 @@ export function useEntityBlockEditor( kind, name, { id: _id } = {} ) { | |||
); | |||
|
|||
function updateAttributes( attributes ) { | |||
// Only attempt to update attributes, if attributes is an object. | |||
if ( | |||
! attributes || |
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.
Note: this falsy check is required because typeof null
equals object
so this catches if somehow this function is called with null
.
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 haven't tested this yet, but just staring at it makes me think it's a safe change regardless since we're destructuring attributes
below.
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.
Yeah, one of the things that tripped me up when looking at this function is that when passed anything other than an object, the destructuring winds up converting (or attempting to convert) to an object.
Hopefully it's a fairly safe-ish change! The thing that took me the longest was testing it out in a custom block 😅
Thanks for taking a look!
Size Change: +14 B (0%) Total Size: 1.44 MB
ℹ️ View Unchanged
|
// Only attempt to update attributes, if attributes is an object. | ||
if ( | ||
! attributes || | ||
Array.isArray( attributes ) || |
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.
Will attributes ever be an Array?
Edit: okay, I just re-read the PR description. Ignore me.
Flaky tests detected in ca04999. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/5735142817
|
Update: in case it helps with testing, I've pushed the example test block from the PR description to a public repo, which you can check out / symlink into your local dev environment's |
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.
LGTM
It'd be good to get @ellatrix's learned eye to run over it.
Before
Strings are destructured
After
if ( | ||
! attributes || | ||
Array.isArray( attributes ) || | ||
typeof attributes !== '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.
I just had to double check for my own benefit: this could catch null
but I think it'd be okay since { ...null }
returns {}
👍🏻
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.
Thanks for the extra checking!
The problem for null
values is a little edge-casey, but an example would be if a block had an attribute that's a list of items where null
is a valid value. For a somewhat contrived example, if we update the attributes in the test block's block.json
to the following:
"attributes": {
"items": {
"type": "array",
"items": {
"type": "string"
},
"default": [
"apples",
null,
"oranges"
]
}
},
Then, if we remove the ! attributes
check in this if
block so that we don't handle null / falsy values, then when { ...null }
turns the value into an empty object, the attributes are mutated.
Before / after:
In practice, I think it's fairly unlikely that there'll be blocks out in the wild affected by it, but good to catch null
values here and return as-is just in case. I reckon a good next step will be to add some extra tests like Ella mentioned so that we're covering these sorts of edge cases 🙂
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.
Thanks for taking the time to double check!
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.
Thanks! I guess we should add an e2e test in a follow-up that registers such block.
Thanks for the reviews, folks! I'll merge this in now, and happy to help look into follow-up tests 👍 |
I worked out how to test it without needing more e2e tests over in #53376 🙂 |
I just cherry-picked this PR to the update/bugfixes-6-3-1 branch to get it included in the next release: ab04074 |
* Footnotes: Fix recursion into updating attributes when attributes is not an object (#53257) * Fix crash by moving editor style logic into a hook with useMemo (#53596) * Move editor style logic into a hook whith useMemo * Remove unnecessary useMemo * Move the whole logic inside the 'useMemo' * Add missing useSelect dep --------- Co-authored-by: George Mamadashvili <[email protected]> * Adding an is_array check before using count in case $footnotes is not countable (#53660) * Footnotes: fix accidental override (#53663) * Footnotes: fix accidental override * Remove double quotes * Footnotes: autosave is not slashing JSON (#53664) * Footnotes: autosave saves decoded JSON * wp_slash * Curly quote * Slash on save and restore * Ensure the preview dropdown popover closes (<16.3) for e2e tests --------- Co-authored-by: Andrew Serong <[email protected]> Co-authored-by: Noah Allen <[email protected]> Co-authored-by: George Mamadashvili <[email protected]> Co-authored-by: Ramon <[email protected]> Co-authored-by: Ella <[email protected]> Co-authored-by: ramon <[email protected]>
* Update document title buttons radius (#53221) * Fix: Sync status overlaps for some languages in Patterns post type page (#53243) * Image block: Fix stretched images constrained by max-width (#53274) * Fix dragging to resize from stretching image in the frontend * Add image block v7 deprecation * Update deprecation comment * Prevent image losing its aspect ratio at smaller window dimensions * Revert "Prevent image losing its aspect ratio at smaller window dimensions" This reverts commit 8ac9f8c. --------- Co-authored-by: scruffian <[email protected]> * Image Block: Don't render `DimensionsTool` if it is not resizable (#53181) * Fix missing Replace button in content-locked Image blocks (#53410) * Revert "don't display BlockContextualToolbar at all in contentonly locking (#53110)" This reverts commit 5efce0e. * Alternative fix to hide BlockContextualToolbar when there are no controls * fix the go to for non pages by showing it only for pages (#53408) * Site Editor: Fix document actions label helper method (#52974) * Site Editor: Fix document actions label helper method * Add missing useSelect dep * Fix e2e tests * Move the label map at the file level to avoid recreating the object on every render * Image: Clear aspect ratio when wide aligned (#53439) * RichText: Remove 'Footnotes' when interactive formatting is disabled (#53474) Introduce a new 'interactive' setting for format types * Preserve block style variations when securing theme json (#53466) * Preserve block style variations when securing theme json Valid and safe block style variations were being removed by `WP_Theme_JSON_Gutenberg::remove_insecure_properties` when securing the theme.json. When this was a problem varied depending upon site configuration, but out-of-the-box it was a problem for administrators on multi-site installs. This change adds explicit processing of variations in `remove_insecure_properties` so that they won't get removed. * Add another variation sanitisation test This test checks that when removing insecure properties an unknown/unsupported property is removed from the variation. * Site editor: add missing i18n in `HomeTemplateDetails` (#53543) * Site editor: add missing i18n in `HomeTemplateDetails` * Lint fix * Fix: Snack bar not fixed on certain pages in the Site Editor (#53207) * Fix document title alignment in command palette button (#53224) * Fallback to default max viewport if layout wide size is fluid. (#53551) * Link Control: persist advanced settings toggle state to preferences if available (#52799) * Link Control: Create a preference for whether the advanced section is open * move the useSelect to the component that uses it * Supply preference setter via settings * Pass setter to Post Editor * Provide local state fallbacks in absence of preference store settings * Conditionalise display of settings drawer to “edit” mode only * Extract to constant to improve comprehension * Fix bug in preferences resolution * Improve comments * Add e2e scaffold * Fix e2e test to correctly assert on feature * Remove focused test * Reinstate original logic to hide settings when not required * Scaffold documentation * Revert providing prefs via settings * Refactor to use `core/block-editor` as preference scope * Update docs * Reinstate remaining original conditional * tentative fix for the e2e test * Try a different syntax for shiftAlt * another attempt to fix the e2e test --------- Co-authored-by: Dave Smith <[email protected]> Co-authored-by: MaggieCabrera <[email protected]> * Add tests for fluid layout + typography (#53554) * Fix support of sticky position in non-iframed post editor (#53540) * Fix support of sticky position in non-iframed post editor * Revert "Footnotes: Fix recursion into updating attributes when attributes is not an object (#53257)" This reverts commit ab04074. * Fix: indicator style when block moving mode (#53972) * Fix post editor top toolbar with custom fields in Safari (#53688) * Set top toolbar size dynamically (#53526) * fix the top toolbar size in the space remaining after plugin items are pinned * only resize above the tablet breakpoint * fix fixed block toolbar collapse button when icon labels are shown * Update height and scroll behavior * move the layout effect to the affected component, fixes for fullscreen, no block selected, icon labels height --------- Co-authored-by: jasmussen <[email protected]> * Roll back camelCase change in 96b6b1e --------- Co-authored-by: James Koster <[email protected]> Co-authored-by: Aki Hamano <[email protected]> Co-authored-by: Alex Lende <[email protected]> Co-authored-by: scruffian <[email protected]> Co-authored-by: Robert Anderson <[email protected]> Co-authored-by: Andrei Draganescu <[email protected]> Co-authored-by: George Mamadashvili <[email protected]> Co-authored-by: Dean Sas <[email protected]> Co-authored-by: Pascal Birchler <[email protected]> Co-authored-by: Dave Smith <[email protected]> Co-authored-by: MaggieCabrera <[email protected]> Co-authored-by: Mitchell Austin <[email protected]> Co-authored-by: jasmussen <[email protected]> Co-authored-by: ramon <[email protected]>
What?
Fixes #53212
Fix the footnotes logic from unintentionally converting an array of strings block attribute into an object keyed by numerical string index. Currently, custom blocks that use an array of strings will likely break when a user goes to add a footnote anywhere else on the same post or page as the custom block.
Why?
When passed a string, the expansion of
attributes = { ...attributes }
results in an object keyed by string index. This happens due to the recursive call toupdateAttributes
for attributes that are an array. The problem is most noticeable with an attribute that is set to be an array of strings.How?
Within the footnotes' logic to update attributes (contained within
updateFootnotes
) check thatattributes
is an object before expanding it into an object.Testing Instructions
There could likely be a simpler way to test this (i.e. add an array of attributes to an existing block in the Gutenberg repo), but to be complete about it, I did the following to create a custom block for testing:
create-block
(npx @wordpress/create-block my-test-block
)block.json
add the following toattributes
:edit.js
file, add the following to output the array of strings within the edit view:This creates a custom block that renders out an array of strings within the edit view for the block. With the block plugin built and activated, go to add a Paragraph to the same post or page as the custom block. Within that Paragraph block go to add Footnotes. Without this PR, the block will error out.
In case it helps testing, I've pushed the above example custom block to a public repo over in https://github.com/andrewserong/andy-test-block
In addition to the above: check that adding Footnotes to nested blocks works as on
trunk
(e.g. paragraphs, quotes (especially the citation within the quote), and lists within multiple levels of nesting in Group blocks, etc)Screenshots or screencast