Skip to content
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

JavascriptException: RangeError: TaskQueue: Error with task : Maximum call stack size exceeded (native stack depth) #18750

Closed
sentry-io bot opened this issue Jul 7, 2023 · 16 comments · Fixed by WordPress/gutenberg#54382 or WordPress/gutenberg#55613 · May be fixed by WordPress/gutenberg#54380
Assignees
Labels
Gutenberg Editing and display of Gutenberg blocks. [Type] Crash

Comments

@sentry-io
Copy link

sentry-io bot commented Jul 7, 2023

Sentry Issue: JETPACK-ANDROID-7XY

Symbolicated stack trace:

allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
allocateTag - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1988
unmountHostComponents - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:5910
commitMutationEffects - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:6013
commitRootImpl - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:7060
completeUnitOfWork - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:7008
markRootSuspended$1 - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:6647
invalidateContextProvider - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:2121
scheduleUpdateOnFiber - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:6335
clz32Fallback - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1787
b.then$argument_1 - gutenberg-mobile/gutenberg/node_modules/react/cjs/react.production.min.js:18
_nestedChildLists.forEach$argument_0 - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Lists/VirtualizedList.js:1704
<global> - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Interaction/Batchinator.js:13
tasks.forEach$argument_0 - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Interaction/TaskQueue.js:67
<global> - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Interaction/InteractionManager.js:154
setInterval - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Core/Timers/JSTimers.js:230
_callTimer - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Core/Timers/JSTimers.js:92
_callTimer - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Core/Timers/JSTimers.js:116
callIdleCallbacks - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Core/Timers/JSTimers.js:380
enqueueNativeCall - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:331
__guard$argument_0 - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:114
enqueueNativeCall - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:316
constructor - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:85
constructor - gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:61
JavascriptException: RangeError: TaskQueue: Error with task : Maximum call stack size exceeded (native stack depth)

This error is located at:
    in l
    in RCTView
    in Unknown
    in RCTScrollView
    in C
    in ScrollView
    in Unknown
    in u
    in S
    in AnimatedComponent(S)
    in Unknown
    in RCTView
    in Unknown
    in E
    in v
    in RCTView
    in Unknown
    in AnimatedComponent(View)
    in Unknown
    in c
    in AnimatedComponent(c)
    in Unknown
    in Unknown
    in f
    in Unknown
    in RCTView
    in Unknown
    in Unknown
    in B
    in RCTView
    in Unknown
    in RCTView
    in Unknown
    in Unknown
    in RCTView
    in Unknown
    in Unknown
    in R
    in WithPreferredColorScheme(R)
    in Unknown
    in WithSelect(WithPreferredColorScheme(R))
    in RNCSafeAreaProvider
    in Unknown
    in Unknown
    in Unknown
    in Unknown
    in WithRegistryProvider(Component)
    in Unknown
    in Unknown
    in Unknown
    in Unknown
    in Unknown
    in WithRegistryProvider(Component)
    in h
    in H
    in WithDispatch(H)
    in Unknown
    in WithSelect(WithDispatch(H))
    in y
    in RNGestureHandlerRootView
    in Unknown
    in F
    in WithDispatch(F)
    in Unknown
    in WithSelect(WithDispatch(F))
    in k
    in RCTView
    in Unknown
    in RCTView
    in Unknown
    in E, js engine: hermes, stack:
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
Gn@1:172289
ba@1:200191
ka@1:201232
Si@1:209533
yi@1:209149
oi@1:206239
xt@1:173349
ni@1:203510
enqueueSetState@1:218168
anonymous@1:145593
anonymous@1:429493
anonymous@1:440377
value@1:381368
S@1:380068
anonymous@1:249595
w@1:248589
N@1:248752
callReactNativeMicrotasks@1:250665
value@1:234104
anonymous@1:233234
value@1:233981
value@1:233145
value@1:233001

    at com.facebook.react.modules.core.ExceptionsManagerModule.reportException(ExceptionsManagerModule.java:72)
    at java.lang.reflect.Method.invoke(Method.java)
    at com.facebook.react.bridge.JavaMethodWrapper.invoke(JavaMethodWrapper.java:372)
    at com.facebook.react.bridge.JavaModuleWrapper.invoke(JavaModuleWrapper.java:188)
    at com.facebook.react.bridge.queue.NativeRunnable.run(NativeRunnable.java)
...
(6 additional frame(s) were not displayed)
@mkevins
Copy link
Contributor

mkevins commented Jul 7, 2023

Quick guess, from the trace, it's possible this is related with a reanimated (software-mansion/react-native-reanimated#4151) issue.

@mkevins
Copy link
Contributor

mkevins commented Jul 13, 2023

Update: this seems to be a grouping issue on Sentry, as the link back is to many differerent Javascript issues, which all share JavascriptException as the name.

We do not have symbolicating properly configured (possibly due to the way our app bundles are generated 🤔 ). This would help with many of these issues, and possibly with the way that Sentry is erroneously grouping them together.

@fluiddot
Copy link
Contributor

fluiddot commented Jul 27, 2023

Update: this seems to be a grouping issue on Sentry, as the link back is to many differerent Javascript issues, which all share JavascriptException as the name.

Good point @mkevins. I tried to unravel the grouped issues and collected the following list (ordered by number of events):

  • RangeError: TaskQueue: Error with task : Maximum call stack size exceeded"
  • TypeError: Cannot read property 'blur' of null
  • TypeError: Cannot read property 'name' of null
  • TypeError: false is not a function
  • TypeError: Cannot read property 'props' of undefined
  • Error: Block type 'core/other' is not registered
  • TypeError: false is not a function
  • Error: 49.9%, 50%: Expected color definition
  • TypeError: Cannot set property 'block' of undefined
  • Error: Exception in HostObject::get for prop 'UIManager': java.lang.OutOfMemoryError: std::bad_alloc

The next step would be to symbolize the stack traces and proceed to debug them. Probably we could do this in separate GitHub issues 😅.

NOTE: I can't point to the public event reference for each, as they are grouped.

@zwarm
Copy link
Contributor

zwarm commented Aug 1, 2023

@fluiddot - on release rotation this sprint, can I assign this to you and remove the triage label?

@fluiddot
Copy link
Contributor

fluiddot commented Aug 2, 2023

@fluiddot - on release rotation this sprint, can I assign this to you and remove the triage label?

@zwarm Yeah, in fact, I'm planning to take a look this week to provide more context about the crash. It's likely that we tackle all the crashes as a mini-project, so can't really tell if it's going to be me in charge of fixing it, but we can re-assign whoever will execute. Thanks!

@fluiddot fluiddot self-assigned this Aug 2, 2023
@fluiddot fluiddot added Gutenberg Editing and display of Gutenberg blocks. and removed Requires Triage labels Aug 2, 2023
@fluiddot
Copy link
Contributor

fluiddot commented Aug 2, 2023

I managed to symbolicate the stack trace and added it to the issue's description. Besides, I reproduce it using the post content from one of the Sentry events. I'll share further findings once I narrow down the culprit.

@fluiddot
Copy link
Contributor

fluiddot commented Aug 2, 2023

Besides, I reproduce it using the post content from one of the Sentry events. I'll share further findings once I narrow down the culprit.

I've tested the post content from other Sentry events and also crashed in most cases. The common factor is that they have a long nested structure. Using a Samsung Galaxy S20 FE 5G (Android 13), I'm experiencing a crash after nesting 14 Group blocks. However, it's unclear to me why it only crashes when closing the editor, this might be a clue to identifying a potential fix.

@fluiddot
Copy link
Contributor

Internal reference: p9ugOq-3WG-p2

I explored the following approaches to address this crash:

1 - Flatten inner blocks

In most cases, the group blocks like Group or Column are rendering their content linearly. There's no visual difference between rendering the blocks nested and in a single block list.

Click here to see an example

HTML structure:
Screenshot 2023-08-11 at 18 40 25

Nested block version Flattened block version

This approach considers that fact and tries to flatten all nested inner blocks when being rendered in a non-root BlockList component. In the branch rnmobile/fix/call-stack-size-exceeded-flatten-solution, I explored this and seems to work as expected. The implementation basically traverses recursively all the nested inner blocks and generates a single block list with all of them. There are cases where we still want to render an inner block list, like for example when a group block is selected and we need to display the block outline.

This approach seems feasible but we have to be careful to only flatten when it's allowed. For instance, there are some cases like the Columns block, where visualizing the editor in landscape mode we display the content in two columns. In this case, we should preserve the layout by rendering its inner blocks, but we could flatten the deeper inner blocks.

As mentioned earlier, there's no visual difference but let me share the render tree as an example.

Using the flattened approach

The following render tree (order is descended) is the one obtained when opening a post with 20 group blocks nested.

Click here to display the HTML content
<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group"><!-- wp:paragraph {"style":{"color":{"background":"#cb6767"}}} -->
<p class="has-background" style="background-color:#cb6767">Level 1 </p>
<!-- /wp:paragraph -->

<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group"><!-- wp:paragraph {"style":{"color":{"background":"#b89759"}}} -->
<p class="has-background" style="background-color:#b89759">Level 2</p>
<!-- /wp:paragraph -->

<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group"><!-- wp:paragraph {"style":{"color":{"background":"#816531"}}} -->
<p class="has-background" style="background-color:#816531">Level 3</p>
<!-- /wp:paragraph -->

<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group"><!-- wp:paragraph {"style":{"color":{"background":"#86c64d"}}} -->
<p class="has-background" style="background-color:#86c64d">Level 4</p>
<!-- /wp:paragraph -->

<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group"><!-- wp:paragraph {"style":{"color":{"background":"#58b873"}}} -->
<p class="has-background" style="background-color:#58b873">Level 5</p>
<!-- /wp:paragraph -->

<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group"><!-- wp:paragraph {"style":{"color":{"background":"#58b8b8"}}} -->
<p class="has-background" style="background-color:#58b8b8">Level 6</p>
<!-- /wp:paragraph -->

<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group"><!-- wp:paragraph {"style":{"color":{"background":"#5880b8"}}} -->
<p class="has-background" style="background-color:#5880b8">Level 7</p>
<!-- /wp:paragraph -->

<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group"><!-- wp:paragraph {"style":{"color":{"background":"#5870b8"}}} -->
<p class="has-background" style="background-color:#5870b8">Level 8</p>
<!-- /wp:paragraph -->

<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group"><!-- wp:paragraph {"style":{"color":{"background":"#5877b8"}}} -->
<p class="has-background" style="background-color:#5877b8">Level 9</p>
<!-- /wp:paragraph -->

<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group"><!-- wp:paragraph {"style":{"color":{"background":"#5873b8"}}} -->
<p class="has-background" style="background-color:#5873b8">Level 10</p>
<!-- /wp:paragraph -->

<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group"><!-- wp:paragraph {"style":{"color":{"background":"#5873b8"}}} -->
<p class="has-background" style="background-color:#5873b8">Level 11</p>
<!-- /wp:paragraph -->

<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group"><!-- wp:paragraph {"style":{"color":{"background":"#5873b8"}}} -->
<p class="has-background" style="background-color:#5873b8">Level 12</p>
<!-- /wp:paragraph -->

<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group"><!-- wp:paragraph {"style":{"color":{"background":"#5873b8"}}} -->
<p class="has-background" style="background-color:#5873b8">Level 13</p>
<!-- /wp:paragraph -->

<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group"><!-- wp:paragraph {"style":{"color":{"background":"#5873b8"}}} -->
<p class="has-background" style="background-color:#5873b8">Level 14</p>
<!-- /wp:paragraph -->

<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group"><!-- wp:paragraph {"style":{"color":{"background":"#5873b8"}}} -->
<p class="has-background" style="background-color:#5873b8">Level 15</p>
<!-- /wp:paragraph -->

<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group"><!-- wp:paragraph {"style":{"color":{"background":"#5873b8"}}} -->
<p class="has-background" style="background-color:#5873b8">Level 16d</p>
<!-- /wp:paragraph -->

<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group"><!-- wp:paragraph {"style":{"color":{"background":"#5873b8"}}} -->
<p class="has-background" style="background-color:#5873b8">Level 17</p>
<!-- /wp:paragraph -->

<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group"><!-- wp:paragraph {"style":{"color":{"background":"#5873b8"}}} -->
<p class="has-background" style="background-color:#5873b8">Level 18</p>
<!-- /wp:paragraph -->

<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group"><!-- wp:paragraph {"style":{"color":{"background":"#5873b8"}}} -->
<p class="has-background" style="background-color:#5873b8">Level 19</p>
<!-- /wp:paragraph -->

<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group"><!-- wp:paragraph {"style":{"color":{"background":"#5873b8"}}} -->
<p class="has-background" style="background-color:#5873b8">Level 20</p>
<!-- /wp:paragraph --></div>
<!-- /wp:group --></div>
<!-- /wp:group --></div>
<!-- /wp:group --></div>
<!-- /wp:group --></div>
<!-- /wp:group --></div>
<!-- /wp:group --></div>
<!-- /wp:group --></div>
<!-- /wp:group --></div>
<!-- /wp:group --></div>
<!-- /wp:group --></div>
<!-- /wp:group --></div>
<!-- /wp:group --></div>
<!-- /wp:group --></div>
<!-- /wp:group --></div>
<!-- /wp:group --></div>
<!-- /wp:group --></div>
<!-- /wp:group --></div>
<!-- /wp:group --></div>
<!-- /wp:group --></div>
<!-- /wp:group -->

Render tree:

1 - Paragraph block located at the deepest level
RichText
Anonymous
Component
Anonymous
Component
RichTextWrapper
ParagraphBlock
Edit
Edit (WithToolbarControls)
Edit (WithInspectorControls)
Edit (WithToolbarControls)
Edit (WithFilters)
2 - Parent group block (this is the only inner block list rendered)
BlockEdit
BlockDraggable
BlockWrapper
BlockListBlock
BlockListBlock (IfCondition)
BlockListBlock (WithDispatch)
Anonymous
BlockListBlock (Pure)
BlockListItemContent
BlockListItem
BlockList
UncontrolledInnerBlocks
InnerBlocks
GroupEdit
GroupEdit (WithPreferredColorScheme)
Anonymous
GroupEdit (WithSelect)
Edit
Edit (WithToolbarControls) 
Edit (WithInspectorControls)
Edit (WithToolbarControls)
Edit (WithFilters)
3 - Root block list
BlockEdit
BlockDraggable
BlockWrapper
BlockListBlock
BlockListBlock (IfCondition)
BlockListBlock (WithDispatch)
Anonymous
BlockListBlock
BlockListItemContent
BlockListItem
CellRenderer
VirtualizedList
FlatList
FlatList (AnimatedComponent)
Anonymous
KeyboardAwareFlatList
BlockDraggableWrapper
BlockList
4 - Editor layout
VisualEditor
Layout
Layout (WithPreferredColorScheme)
Anonymous
Layout (WithSelect)
Editor
Editor (WithDispatch)
Anonymous
Editor
Gutenberg
AppContainer

Using the flattened approach (selecting a group block at level 10)

When selecting a block, we force rendering it to display the block outline.

Render tree:

1 - Paragraph block located at the deepest level
RichText
Anonymous
Component
Anonymous
Component
RichTextWrapper
ParagraphBlock
Edit
Edit (WithToolbarControls)
Edit (WithInspectorControls)
Edit (WithToolbarControls)
Edit (WithFilters)
2 - Parent group block (the one that is selected)
BlockEdit
BlockDraggable
BlockWrapper
BlockListBlock
BlockListBlock (IfCondition)
BlockListBlock (WithDispatch)
Anonymous
BlockListBlock (Pure)
BlockListItemContent
BlockListItem
BlockList
UncontrolledInnerBlocks
InnerBlocks
GroupEdit
GroupEdit (WithPreferredColorScheme)
Anonymous
GroupEdit (WithSelect)
Edit
Edit (WithToolbarControls) 
Edit (WithInspectorControls)
Edit (WithToolbarControls)
Edit (WithFilters)
3 - Parent group block
BlockEdit
BlockDraggable
BlockWrapper
BlockListBlock
BlockListBlock (IfCondition)
BlockListBlock (WithDispatch)
Anonymous
BlockListBlock (Pure)
BlockListItemContent
BlockListItem
BlockList
UncontrolledInnerBlocks
InnerBlocks
GroupEdit
GroupEdit (WithPreferredColorScheme)
Anonymous
GroupEdit (WithSelect)
Edit
Edit (WithToolbarControls) 
Edit (WithInspectorControls)
Edit (WithToolbarControls)
Edit (WithFilters)
4 - Root block list
BlockEdit
BlockDraggable
BlockWrapper
BlockListBlock
BlockListBlock (IfCondition)
BlockListBlock (WithDispatch)
Anonymous
BlockListBlock
BlockListItemContent
BlockListItem
CellRenderer
VirtualizedList
FlatList
FlatList (AnimatedComponent)
Anonymous
KeyboardAwareFlatList
BlockDraggableWrapper
BlockList
5 - Editor layout
VisualEditor
Layout
Layout (WithPreferredColorScheme)
Anonymous
Layout (WithSelect)
Editor
Editor (WithDispatch)
Anonymous
Editor
Gutenberg
AppContainer

Before using the approach

The original render tree is similar but in this case, the components rendered by the Group block are repeated 13 times. This would increase linearly as more blocks are nested.

Render tree:

1 - Paragraph block located at the deepest level
RichText
Anonymous
Component
Anonymous
Component
RichTextWrapper
ParagraphBlock
Edit
Edit (WithToolbarControls)
Edit (WithInspectorControls)
Edit (WithToolbarControls)
Edit (WithFilters)
2 - Parent group blocks (this sequence is repeated 13 times)
BlockEdit
BlockDraggable
BlockWrapper
BlockListBlock
BlockListBlock (IfCondition)
BlockListBlock (WithDispatch)
Anonymous
BlockListBlock (Pure)
BlockListItemContent
BlockListItem
BlockList
UncontrolledInnerBlocks
InnerBlocks
GroupEdit
GroupEdit (WithPreferredColorScheme)
Anonymous
GroupEdit (WithSelect)
Edit
Edit (WithToolbarControls) 
Edit (WithInspectorControls)
Edit (WithToolbarControls)
Edit (WithFilters)
15 - Root block list
BlockEdit
BlockDraggable
BlockWrapper
BlockListBlock
BlockListBlock (IfCondition)
BlockListBlock (WithDispatch)
Anonymous
BlockListBlock
BlockListItemContent
BlockListItem
CellRenderer
VirtualizedList
FlatList
FlatList (AnimatedComponent)
Anonymous
KeyboardAwareFlatList
BlockDraggableWrapper
BlockList
16 - Editor layout
VisualEditor
Layout
Layout (WithPreferredColorScheme)
Anonymous
Layout (WithSelect)
Editor
Editor (WithDispatch)
Anonymous
Editor
Gutenberg
AppContainer

2 - Limiting block lists based on nesting depth

This approach is more basic but also provides a worse user experience. The idea is to simply stop rendering block lists that exceed an established maximum nesting depth. In that case, we'll present a warning message (see attached screenshot). I explored this in the branch rnmobile/fix/call-stack-size-exceeded-limit-solution.

This option can work in combination with the former, as there might be cases where we can't flatten inner blocks and we still need to limit the depth.

Here is an example of rendering a post with 20 group blocks nested (the maximum depth is set to ten levels).

@fluiddot
Copy link
Contributor

Similar to wordpress-mobile/gutenberg-mobile#6123 (comment), the fix didn't cover all the cases of the crash. We have reports in version 23.4 with the following stack trace:

packages/react-native-editor/src/jsdom-patches.js:45:Node.prototype.contains
packages/react-native-editor/src/jsdom-patches.js:46:Array.prototype.some.call$argument_1
packages/react-native-editor/src/jsdom-patches.js:45:Node.prototype.contains
packages/react-native-editor/src/jsdom-patches.js:46:Array.prototype.some.call$argument_1
packages/react-native-editor/src/jsdom-patches.js:45:Node.prototype.contains
packages/react-native-editor/src/jsdom-patches.js:46:Array.prototype.some.call$argument_1
packages/react-native-editor/src/jsdom-patches.js:45:Node.prototype.contains
packages/react-native-editor/src/jsdom-patches.js:46:Array.prototype.some.call$argument_1
packages/react-native-editor/src/jsdom-patches.js:45:Node.prototype.contains
packages/react-native-editor/src/jsdom-patches.js:46:Array.prototype.some.call$argument_1
packages/react-native-editor/src/jsdom-patches.js:45:Node.prototype.contains
packages/react-native-editor/src/jsdom-patches.js:46:Array.prototype.some.call$argument_1
packages/react-native-editor/src/jsdom-patches.js:45:Node.prototype.contains
packages/react-native-editor/src/jsdom-patches.js:46:Array.prototype.some.call$argument_1
packages/react-native-editor/src/jsdom-patches.js:45:Node.prototype.contains
packages/react-native-editor/src/jsdom-patches.js:46:Array.prototype.some.call$argument_1
packages/react-native-editor/src/jsdom-patches.js:45:Node.prototype.contains
packages/react-native-editor/src/jsdom-patches.js:46:Array.prototype.some.call$argument_1
packages/react-native-editor/src/jsdom-patches.js:45:Node.prototype.contains
packages/react-native-editor/src/jsdom-patches.js:46:Array.prototype.some.call$argument_1
packages/react-native-editor/src/jsdom-patches.js:45:Node.prototype.contains
packages/react-native-editor/src/jsdom-patches.js:46:Array.prototype.some.call$argument_1
packages/react-native-editor/src/jsdom-patches.js:45:Node.prototype.contains
packages/react-native-editor/src/jsdom-patches.js:46:Array.prototype.some.call$argument_1
packages/blocks/src/api/raw-handling/utils.js:129:deepFilterNodeList
packages/blocks/src/api/raw-handling/utils.js:130:Array.from.forEach$argument_0
packages/blocks/src/api/raw-handling/utils.js:129:deepFilterNodeList
packages/blocks/src/api/raw-handling/utils.js:130:Array.from.forEach$argument_0
packages/blocks/src/api/raw-handling/utils.js:129:deepFilterNodeList
packages/blocks/src/api/raw-handling/utils.js:130:Array.from.forEach$argument_0
packages/blocks/src/api/raw-handling/utils.js:129:deepFilterNodeList
packages/blocks/src/api/raw-handling/utils.js:130:Array.from.forEach$argument_0
packages/blocks/src/api/raw-handling/utils.js:129:deepFilterNodeList
packages/blocks/src/api/raw-handling/utils.js:130:Array.from.forEach$argument_0
packages/blocks/src/api/raw-handling/utils.js:129:deepFilterNodeList
packages/blocks/src/api/raw-handling/utils.js:130:Array.from.forEach$argument_0
packages/blocks/src/api/raw-handling/utils.js:129:deepFilterNodeList
packages/blocks/src/api/raw-handling/utils.js:130:Array.from.forEach$argument_0
packages/blocks/src/api/raw-handling/utils.js:129:deepFilterNodeList
packages/blocks/src/api/raw-handling/utils.js:130:Array.from.forEach$argument_0
packages/blocks/src/api/raw-handling/utils.js:129:deepFilterNodeList
packages/blocks/src/api/raw-handling/utils.js:158:deepFilterHTML
packages/blocks/src/api/raw-handling/paste-handler.js:155:pasteHandler
packages/block-editor/src/components/rich-text/index.native.js:449:onPaste
packages/rich-text/src/component/index.native.js:571:RichText#onPaste
node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:22:invokeGuardedCallbackImpl
node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:40:invokeGuardedCallback
node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:53:invokeGuardedCallbackAndCatchFirstError
node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:73:executeDispatch
node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1125:executeDispatchesAndReleaseTopLevel
node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:361:forEachAccumulated
node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1160:batchedUpdates$argument_0
node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:8457:batchedUpdatesImpl
node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1106:batchedUpdates
node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1137:_receiveRootNodeIDEvent
node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1178:ReactNativePrivateInterface.RCTEventEmitter.register$argument_0.receiveEvent
node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:427:__callFunction
node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:113:__guard$argument_0
node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:368:__guard
node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:112:callFunctionReturnFlushedQueue

Same as shared in wordpress-mobile/gutenberg-mobile#6123 (comment), seems we'd need to review the pasting functionality to avoid processing deeply nested structures.

@fluiddot fluiddot reopened this Oct 23, 2023
@mkevins
Copy link
Contributor

mkevins commented Oct 23, 2023

I wonder if this is partially because the Node.contains polyfill uses .call? I found this comment that could be relevant, as it suggests this makes it native. From my understanding, the native stack depth limit with Hermes is 128, which seems pretty small to me (almost incompatible with the recursive needs of parsing). Could this limit be increased somehow? I saw this comment, but I'm not sure how feasible this is for our project. 🤔

@fluiddot
Copy link
Contributor

I wonder if this is partially because the Node.contains polyfill uses .call? I found facebook/hermes#135 (comment) that could be relevant, as it suggests this makes it native.

Good catch @mkevins! Definitely, the use of .call in Node.contains seems the culprit of the crash.

From my understanding, the native stack depth limit with Hermes is 128, which seems pretty small to me (almost incompatible with the recursive needs of parsing). Could this limit be increased somehow? I saw facebook/hermes#135, but I'm not sure how feasible this is for our project. 🤔

It could be increased but we'd need to compile Hermes, generate the binaries, and integrate the ad-hoc version into the build process. We currently rely on the Hermes binaries provided by the React Native version, so applying this approach won't be trivial.

As an alternative, I'd like to explore potential adjustments to the Node.contains function to avoid hitting the call stack limit, which I presume it would be simpler than modifying Hermes.

@mkevins
Copy link
Contributor

mkevins commented Oct 25, 2023

As an alternative, I'd like to explore potential adjustments to the Node.contains function to avoid hitting the call stack limit, which I presume it would be simpler than modifying Hermes.

Probably this is possible, but keep in mind most of the Array.prototype methods that come to mind in making a recursive polyfill for this will also be [Native code] implementations. Still, maybe refactoring it will afford us some "depth factor" when it comes to the native stack, since I think each level in "JS-land" corresponds to two levels natively (one for the some, and one for the call).

It is also conceivable to avoid native calls (I think 🤔), by recreating some (or forEach, or one of the other Array.prototype functions) purely in JS, though I suspect that could impact performance / memory usage. 🤷‍♂️

@fluiddot
Copy link
Contributor

Heads up that I'm working on this PR to update the Node.contains polyfill with a non-recursive solution. I'm using for the new version the same polyfill we have in WordPress core (reference 1, reference 2). So far it's working great and I couldn't reproduce the crash.

@mkevins
Copy link
Contributor

mkevins commented Oct 25, 2023

a non-recursive solution

Nice! Good idea to try this. If parentNode is well defined in our jsdom-jscore-rn, this should be just as reliable, in theory, and probably much faster.

@fluiddot
Copy link
Contributor

Nice! Good idea to try this. If parentNode is well defined in our jsdom-jscore-rn, this should be just as reliable, in theory, and probably much faster.

I ran some of the unit tests related to paste functionality, as well as manual testing, and so far parentNode is working as expected 🤞. I think the solution is good to go at this point.

Regarding the unit tests, I noticed that some are failing in the native version of the editor, and confirmed that a couple of them produce a crash 😮. As a follow-up, I'll create a tracking issue with all the information I captured after testing.

@fluiddot
Copy link
Contributor

fluiddot commented Dec 4, 2023

Similar to #18750 (comment), we spotted new events in 23.7 related to this issue produced by a different stack trace:

gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:193:lengthFromProperties
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:204:NodeList.prototype._update
get /workdir/gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:233:NodeList.prototype.get__length
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:44:visit
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:52:reducer
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:44:visit
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:52:reducer
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:44:visit
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:52:reducer
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:44:visit
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:52:reducer
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:44:visit
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:52:reducer
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:44:visit
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:52:reducer
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:44:visit
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:52:reducer
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:44:visit
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:52:reducer
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:44:visit
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:52:reducer
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:44:visit
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:52:reducer
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:44:visit
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:52:reducer
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:44:visit
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:44:visit
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:52:reducer
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:44:visit
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:52:reducer
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:44:visit
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:57:mapDOMNodes
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:37:<anonymous>
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:207:NodeList.prototype._update
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:185:NodeList
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:1375:inheritFrom$argument_2.getElementsByTagName
gutenberg/node_modules/jsdom-jscore-rn/lib/builtins/nwmatcher/src/nwmatcher-noqsa.js:881:select
gutenberg/node_modules/jsdom-jscore-rn/lib/builtins/nwmatcher/src/nwmatcher-noqsa.js:798:first
gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/selectors/index.js:29:dom.Element.prototype.querySelector
gutenberg/node_modules/hpq/dist/hpq.js:108:<anonymous>
gutenberg/node_modules/hpq/dist/hpq.js:133:<anonymous>
gutenberg/packages/blocks/src/api/parser/get-block-attributes.js:261:parseWithAttributeSchema
gutenberg/packages/blocks/src/api/parser/get-block-attributes.js:141:getBlockAttribute
gutenberg/packages/blocks/src/api/parser/get-block-attributes.js:285:Object.entries.map$argument_0
gutenberg/packages/blocks/src/api/parser/get-block-attributes.js:282:getBlockAttributes
gutenberg/packages/blocks/src/api/parser/apply-block-deprecated-versions.js:71:applyBlockDeprecatedVersions
gutenberg/packages/blocks/src/api/parser/index.js:250:parseRawBlock
gutenberg/packages/blocks/src/api/parser/index.js:312:grammarParse.reduce$argument_0
gutenberg/packages/blocks/src/api/parser/index.js:311:parse
gutenberg/packages/edit-post/src/editor.native.js:168:Editor#render
gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:4729:finishClassComponent
gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:4700:updateClassComponent
gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:7866:beginWork$1
gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:7304:performUnitOfWork
gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:7297:workLoopSync
gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:7279:renderRootSync
gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:6975:performSyncWorkOnRoot
gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:2145:flushSyncCallbacks
gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:6650:scheduleUpdateOnFiber
gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:8423:updateContainer
gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:8584:exports.render
gutenberg/node_modules/react-native/Libraries/ReactNative/RendererImplementation.js:35:renderElement
gutenberg/node_modules/react-native/Libraries/ReactNative/renderApplication.js:96:renderApplication
gutenberg/node_modules/react-native/Libraries/ReactNative/AppRegistry.js:120:runnables.appKey.run
gutenberg/node_modules/react-native/Libraries/ReactNative/AppRegistry.js:214:runApplication
gutenberg/node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:427:__callFunction
gutenberg/node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:113:__guard$argument_0
gutenberg/node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:368:__guard
gutenberg/node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:112:callFunctionReturnFlushedQueue

I will follow-up on this in a new GitHub issue in the GBM repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment