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

Unhandled JS Exception: TypeError: undefined is not an object (evaluating 't._parentNode=null') #4145

Closed
fluiddot opened this issue Oct 21, 2021 · 13 comments · Fixed by #4174
Assignees
Labels

Comments

@fluiddot
Copy link
Contributor

Sentry issue: https://sentry.io/share/issue/83edfe1cf6ea4b7fbb2bdc853e6201eb/

Here is the desymbolyzed JS stacktrace for version WPiOS 18.4 (GB-mobile 1.63.0):

gutenberg-mobile/gutenberg/node_modules/jsdom-jscore-rn/lib/jsdom/level1/core.js:631:Node.prototype.removeChild
gutenberg-mobile/gutenberg/packages/blocks/src/api/raw-handling/ms-list-converter.js:57:msListConverter
gutenberg-mobile/gutenberg/packages/blocks/src/api/raw-handling/utils.js:117:filters.forEach$argument_0
forEach@(null):(null)
gutenberg-mobile/gutenberg/packages/blocks/src/api/raw-handling/utils.js:111:Array.from.forEach$argument_0
forEach@(null):(null)
gutenberg-mobile/gutenberg/packages/blocks/src/api/raw-handling/utils.js:108:deepFilterNodeList
gutenberg-mobile/gutenberg/packages/blocks/src/api/raw-handling/utils.js:109:Array.from.forEach$argument_0
forEach@(null):(null)
gutenberg-mobile/gutenberg/packages/blocks/src/api/raw-handling/utils.js:108:deepFilterNodeList
gutenberg-mobile/gutenberg/packages/blocks/src/api/raw-handling/utils.js:109:Array.from.forEach$argument_0
forEach@(null):(null)
gutenberg-mobile/gutenberg/packages/blocks/src/api/raw-handling/utils.js:108:deepFilterNodeList
gutenberg-mobile/gutenberg/packages/blocks/src/api/raw-handling/utils.js:137:deepFilterHTML
gutenberg-mobile/gutenberg/packages/blocks/src/api/raw-handling/paste-handler.js:203:flatMap$argument_1
gutenberg-mobile/gutenberg/node_modules/lodash/lodash.js:653:arrayMap
gutenberg-mobile/gutenberg/node_modules/lodash/lodash.js:9621:map
gutenberg-mobile/gutenberg/node_modules/lodash/lodash.js:9325:flatMap
gutenberg-mobile/gutenberg/packages/blocks/src/api/raw-handling/paste-handler.js:177:pasteHandler
gutenberg-mobile/gutenberg/packages/block-editor/src/components/rich-text/index.native.js:450:useCallback$argument_0
gutenberg-mobile/gutenberg/packages/rich-text/src/component/index.native.js:534:RichText#onPaste
value@(null):(null)
gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:22:invokeGuardedCallbackImpl
gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:40:invokeGuardedCallback
gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:53:invokeGuardedCallbackAndCatchFirstError
gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:72:executeDispatch
gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1018:executeDispatchesAndReleaseTopLevel
gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:349:forEachAccumulated
gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1053:batchedUpdates$argument_0
gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:7817:batchedUpdatesImpl
gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:999:batchedUpdates
gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1030:_receiveRootNodeIDEvent
gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js:1071:ReactNativePrivateInterface.RCTEventEmitter.register$argument_0.receiveEvent
gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:414:__callFunction
gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:113:__guard$argument_0
gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:365:__guard
gutenberg-mobile/gutenberg/node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:112:callFunctionReturnFlushedQueue
value@(null):(null)

A quick exploration of the stacktrace tells that the issue is most likely related to pasting content from MS Word/MS Word Online that can't be parsed in the native version:

To Reproduce
Unknown, yet to be defined.

Expected behavior
Pasting content shouldn't produce JS exceptions.

Screenshots
N/A

Smartphone (please complete the following information):

  • Device: iPad (8th generation)
  • OS: iOS 15.0.2
  • Version: 18.4.0.3

Additional context
N/A

@fluiddot fluiddot added [Type] Bug Something isn't working [OS] iOS labels Oct 21, 2021
@twstokes
Copy link
Contributor

twstokes commented Oct 21, 2021

I was able to reproduce the following error:

2021-10-21 14:34:09:689 WordPress[8578:4048467] TypeError: undefined is not an object (evaluating 't._parentNode=null')

with these steps:

  1. Run a debug build of WPiOS from Xcode on the Simulator (I used latest develop)
  2. In Word for Mac, create an unordered list with the following format:
    1. Start a new unordered list
    2. Next to the first bullet, press space
    3. Copy the list and paste into a Paragraph block in the Simulator
  3. Observe the error above and no output in the editor.

@guarani
Copy link
Contributor

guarani commented Oct 21, 2021

I can also reproduce this crash, even when pasting in a single bullet point from the MS Word app.

@twstokes
Copy link
Contributor

I was able to dig into this a little more. In the case where a single bullet point was pasted from Word for Mac, adding a breakpoint here and outputting node.innerHTML gives us a structure like this:

<span style="font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family:\r\nSymbol"><span style="mso-list:Ignore">·<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\r\n</span></span></span><span style="mso-spacerun:yes">&nbsp;</span><o:p></o:p>

node.firstElementChild is undefined and causing the crash.

Why it's undefined I'm not sure, because node.firstChild.nodeType is 1 [doc 1, doc 2]. Either way, the code isn't accounting for nested <span>s.

@hypest
Copy link
Contributor

hypest commented Oct 22, 2021

Since it's reproducible on iOS but @twstokes's analysis shows a possible logic error, can you also check if it happens on Android? Thanks!

@twstokes
Copy link
Contributor

Since it's reproducible on iOS but @twstokes's analysis shows a possible logic error, can you also check if it happens on Android? Thanks!

I'll test this and report back @hypest.

@twstokes
Copy link
Contributor

I had a different experience on Android. It seems that the HTML markup doesn't match iOS when msListConverter is called, and no mso-list style property is included.

Note: I used Word for Mac pasting to the Android Emulator (which may not be the most realistic test), as well as Word on Android pasting into WPAndroid. Both exhibited the same behavior, but I only inspected values with breakpoints on the emulator.

Pasting the empty list that crashes iOS

List Example

Results

  • msListConverter is never called, and a single bullet character is output in the paragraph block.
  • The paragraph block does not transform into a list block.
  • Since msListConverter was never called, the suspect logic never executed.

Screenshot of empty list

Pasting a normal list

List Example

  • A normal
  • List right
  • Here

Results

  • msListConverter was called.
  • The paragraph block did not transform into a list block.
  • The incoming HTML was: <p>• A normal<br />\n• List right<br />\n• Here</p>
  • Since no style attribute was included, the function never proceeded past this line.

Screenshot of normal list

@hypest
Copy link
Contributor

hypest commented Oct 25, 2021

Aha, interesting, thanks for sharing @twstokes . Looks like there's some difference in the logic/implementation/codepath between the platforms and it's not clear to me yet what's going on. Would you mind devoting some additional time to it to see if we can get to the bottom of this? For example, why the different behaviour between the platforms? Is it the input data (from the clipboards) that's different, or some native/JS code takes a different turn?

In general, I consider this issue as rather important since the flow where people draft their content in other apps and then paste it into the WordPress app is far from rare so, let's figure this one out. Thanks!

@twstokes
Copy link
Contributor

Will do @hypest. 👍

@twstokes
Copy link
Contributor

twstokes commented Oct 26, 2021

Is it the input data (from the clipboards) that's different, or some native/JS code takes a different turn?

From what I can tell, it appears to be that the data from the system's clipboard is different. If I understand the stack correctly, ReactNativeAztec receives the clipboard event and data from the OS and then pushes that data to the editor.

Here's what I observed by using the Mobile Gutenberg demo app:

iOS

  1. A list from Word containing an empty item is pasted.
  2. RCTAztecView:paste() catches the event and calls RCTAztecView:readHTML().
  3. RCTAztecView:readHTML() is able to return parsed HTML here because the clipboard contained HTML.
  4. The RichText component onPaste() function is called and event.nativeEvent.pastedHtml equals a large Microsoft generated HTML string.
  5. When tested as a MS list filter, it reaches msListConverter.

Android

  1. A list from Word containing an empty item is pasted. (note: pasting was done via a long press, then selecting Paste)
  2. ReactAztecText:onTextContextMenuItem() calls onPaste.
  3. ReactAztecText:onPaste() is called with isPastedAsPlainText equal to false.
  4. The output of clipData.mItems[0] shows mHtmlText = null, and mText = •\t ". (see screenshot)
  5. The paste event is dispatched.
  6. The RichText component onPaste() function is called and event.nativeEvent.pastedHtml equals &amp;#8226;&amp;#9;.
  7. [I believe] Because it's not HTML, it's not tested against the msListConverter.

Note: When pasting a "normal" unordered list into Android, msListConverter is called because it's wrapped in HTML (e.g. <p>• Testing<br />\n• A<br />\n• List</p>). However, it doesn't get far as noted above.

Screen Shot 2021-10-25 at 9 28 14 PM

@hypest
Copy link
Contributor

hypest commented Oct 26, 2021

Aha, thanks for drilling down @twstokes ! Can you continue working on this on the iOS side, to try and come up with a fix for the crash? We can reel in someone for the Android side if the iOS fix doesn't fit for both. Thanks!

@twstokes
Copy link
Contributor

Aha, thanks for drilling down @twstokes ! Can you continue working on this on the iOS side, to try and come up with a fix for the crash? We can reel in someone for the Android side if the iOS fix doesn't fit for both. Thanks!

Sounds good @hypest I'll check it out. 👍

@twstokes
Copy link
Contributor

twstokes commented Oct 27, 2021

node.firstElementChild is undefined and causing the crash.

I understand this better now. On the RN side, we appear to be pulling in the jsdom-jscore-rn package to manipulate DOM structures. From what I can tell, this fork / trimmed version of jsdom doesn't include the firstElementChild property.

Web has this property so the tests pass and no exception is thrown. jsdom has had this feature since 5.2.0.

A couple options:

  1. Use firstChild [this impacts Gutenberg]
  2. Update to a newer jsdom (or jsdom fork) which supports firstElementChild

If option 1, we update it here.

@hypest
Copy link
Contributor

hypest commented Oct 29, 2021

Aha, it's good to explore option 1 as a perhaps faster way to resolve this for now, but also wonder if we should give some effort on trying to add the missing API, which is something we've done in the past in similar cases (see https://github.com/WordPress/gutenberg/blob/trunk/packages/react-native-editor/src/jsdom-patches.js). I wouldn't prioritize this extra effort too much, but such work has been useful in the past to support more of the web code.

For added context, I see that the firstElementChild seems to have been added in jsdom in this PR (and here's the implementation?).

@fluiddot fluiddot mentioned this issue Nov 11, 2021
4 tasks
@dcalhoun dcalhoun mentioned this issue Nov 12, 2021
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants