Skip to content

Insert- and AddMutations applied in wrong order at Receiver-side #519

@mfal

Description

@mfal

Browser

(Chrome 131.0.6778.140)

Error description

In some situation where an observed remote-mutation includes interleaved insertions and additions of nodes, the (index-based) application of these mutations at receivers-side leads to accessing the children-collection at wrong indexes with the following downstream errors:

  • insertion at wrong index

    TypeError: Cannot read properties of undefined (reading 'eventListeners')
        at Object.updateProperty (RemoteReceiver.mjs:125:1)
        at mutate (connection.mjs:25:1)
    
  • removal at wrong index

    TypeError: Cannot read properties of undefined (reading 'id')
        at detach (RemoteReceiver.mjs:226:1)
        at Object.removeChild (RemoteReceiver.mjs:111:1)
        at mutate (connection.mjs:25:1)
    

I investigated on this issue and it comes up that this only happens if the amount of children is the same after the mutation. When this happens, the mutation observer seems to "batch" insertions and additions – first removals, then additions. This totally messes up the application of mutations at the receiver side, because it assumes an "ordered" mutations array.

Example

// From
<remote-parent>
  <remote-child key="2" />
  <remote-child key="3" />
</remote-parent>
// To
<remote-parent>
  <remote-child key="1" />
  <remote-child key="2" />
</remote-parent>

The mutations collected by the RemoteMutationObserver are:

[
  // remove child (key 3) at index 2
  [MUTATION_TYPE_REMOVE_CHILD, 'parent-id', 2],
  // add child  (key 1) at index 0 
  [MUTATION_TYPE_INSERT_CHILD, 'parent-id', { */ serialized child */ }, 0],
]

When the RemoteReceiver tries to apply the first mutation, it wants to remove the child at index 2, which does not exist 💥. Instead it should have done the insertion before and then the removal. In case of more than two mutations (which can be interleaved as well) all the removals are recorded before all insertions.

I don't know why the Mutation Observer API behaves this way (maybe some internal optimization), but this leads to unexpected behaviour (or even errors) on the host side.

Possible solution

The RemoteMutationObserver could sort the mutations by their index before sending them to the receiver. This should only be necessary if there are insertions and removals in one mutation and and amount of removals and insertions is equal.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions