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

Calling a.setParent(b) without connecting blocks a and b can cause major rendering issues and errors #4989

Closed
jschanker opened this issue Jul 4, 2021 · 3 comments
Labels
issue: bug Describes why the code or behaviour is wrong issue: docs Describes missing or incorrect documentation type: cleanup

Comments

@jschanker
Copy link
Contributor

Describe the bug

Calling a.setParent(b) on blocks a and b without connecting them can cause rendering issues where the parent and unconnected child block can move together with varying degrees of space in between them. If a had no parent originally, then attempting to delete b results in an unmovable b and error messages on any event.

To Reproduce (2 possibilities)

Steps to reproduce the behavior (two different types):

  1. Go to the Blockly Playground.
  2. Import the following XML:
<xml xmlns="https://developers.google.com/blockly/xml">
  <block type="text_print" id="s+b@l4y{Nw=CeqI3/qzW" x="38" y="38">
    <value name="TEXT">
      <shadow type="text" id="^QF~!eeGDNIEJE8Ye7ji">
        <field name="TEXT">abc</field>
      </shadow>
      <block type="text_join" id="M$%c|%@HCw^J:hg7*J%t">
        <mutation items="2"></mutation>
        <value name="ADD0">
          <block type="text" id="w,TRg]2*bD`k[5+`I$VE">
            <field name="TEXT"></field>
          </block>
        </value>
      </block>
    </value>
  </block>
</xml>

3a. Enter the following in the Developer console:
workspace.getAllBlocks().find(block => block.type === "text").setParent(workspace.getAllBlocks().find(block => block.type === "text_print"))
4a. There will be a "Still connected to parent block error." Move the text_join block around the workspace and watch the text block jump all around.

OR...

3b. Disconnect the text block and then enter the code from 3a in the console.
4b. No error this time, but if you move the print block, you will notice that the disconnected text block moves together with the print block. Attempt to delete the print block and the errors ensue.

Expected behavior

Should setParent be a package method? I'm not sure why it should be called for any other purpose than connecting/disconnecting blocks. In this case (outside of Blockly.Block and Blockly.BlockSvg), it should not be needed outside of the Connection class. The only other places I could find it being called were in testing to set the parent to null here:

block.setParent(null);

and (indirectly) here:

addMoveEventParent(events, block, null);

@jschanker jschanker added issue: triage Issues awaiting triage by a Blockly team member issue: bug Describes why the code or behaviour is wrong labels Jul 4, 2021
@BeksOmega
Copy link
Collaborator

Should setParent be a package method?

setParent should definitely be package! And taking a quick look, setShadow should be as well. There could be other functions that need to be changed too, but I just did a quick skim so I'm not sure.

@moniika moniika added type: cleanup issue: docs Describes missing or incorrect documentation and removed issue: triage Issues awaiting triage by a Blockly team member labels Jul 7, 2021
@moniika moniika added this to the Bug Bash Backlog milestone Jul 7, 2021
jschanker added a commit to jschanker/blockly that referenced this issue Jul 8, 2021
* Error now thrown when calling `a.setParent(b)` on non-null b if the output/previous connection of a is not connected to an input/next connection of b without removing block from old parent's child list.
* Commented out error for when calling `a.setParent(null)` if a is connected to superior block.
* Adjusted comment to reflect that blocks were no longer being disconnected in this method.
* Also changed == to === per google#4924 (`newParent` and `this.parentBlock_` must both be instances of `Blockly.Block`).
jschanker added a commit to jschanker/blockly that referenced this issue Jul 8, 2021
* Error now thrown when calling `a.setParent(b)` on non-null b if the output/previous connection of a is not connected to an input/next connection of b without removing block from old parent's child list.
* Error now thrown when calling `a.setParent(null)` if a is connected to a superior block.
* Adjusted comment to reflect that blocks were no longer being disconnected in this method.
* Also changed == to === per google#4924 (`newParent` and `this.parentBlock_` must both be instances of `Blockly.Block`).
jschanker added a commit to jschanker/blockly that referenced this issue Jul 8, 2021
* Error now thrown when calling `a.setParent(b)` on non-null b if the output/previous connection of a is not connected to an input/next connection of b without removing block from old parent's child list.
* Commented out error for when calling `a.setParent(null)` if a is connected to superior block.
* Adjusted comment to reflect that blocks were no longer being disconnected in this method.
* Also changed == to === per google#4924 (`newParent` and `this.parentBlock_` must both be instances of `Blockly.Block`).
* Fixed lint errors.
@jschanker
Copy link
Contributor Author

I browsed the history of the block.js file because I had the following questions:

  1. Why is the block removed from its old parent's child list when setParent is called in a disallowed case? I.e., why can errors be thrown after Blockly.utils.arrayRemove is called? This is what permits the error reproduced in the first way.

  2. Why is there a mention about disconnecting from superior blocks in the comments if all that happens is that the parent is changed to something else?

The answer to question 2 and perhaps 1 can be traced back to the commit here where disconnection calls were replaced with thrown errors during refactoring: 2a1ffa1#diff-9b5800799b33a83e7ff338b52ff14964b5ab04f1a6e1834523b121f2ea070aeaR419.

It seems to me then that for future stability, this method should be changed even though it's being declared as package. Specifically, I think that a precondition for calling a.setParent(b) for non-null b should be that the output/previous connection of a is connected to an input/next connection of b. If b is null, then a should not be connected via its previous/output connections. The pull request #4999 enforces this.

jschanker added a commit to jschanker/blockly that referenced this issue Jul 8, 2021
* Error now thrown when calling `a.setParent(b)` on non-null b if the output/previous connection of a is not connected to an input/next connection of b without removing block from old parent's child list.
* Commented out error for when calling `a.setParent(null)` if a is connected to superior block.
* Adjusted comment to reflect that blocks were no longer being disconnected in this method.
* Also changed == to === per google#4924 (`newParent` and `this.parentBlock_` must both be instances of `Blockly.Block`).
* Fixed lint errors.
* Adjusted comment.
BeksOmega pushed a commit that referenced this issue Jul 13, 2021
* Fix error conditions for setParent #4989

* Error now thrown when calling `a.setParent(b)` on non-null b if the output/previous connection of a is not connected to an input/next connection of b without removing block from old parent's child list.
* Commented out error for when calling `a.setParent(null)` if a is connected to superior block.
* Adjusted comment to reflect that blocks were no longer being disconnected in this method.
* Also changed == to === per #4924 (`newParent` and `this.parentBlock_` must both be instances of `Blockly.Block`).

* Fix error conditions for setParent #4989

* Error now thrown when calling `a.setParent(b)` on non-null b if the output/previous connection of a is not connected to an input/next connection of b without removing block from old parent's child list.
* Error now thrown when calling `a.setParent(null)` if a is connected to a superior block.
* Adjusted comment to reflect that blocks were no longer being disconnected in this method.
* Also changed == to === per #4924 (`newParent` and `this.parentBlock_` must both be instances of `Blockly.Block`).

* Fix error conditions for setParent #4989

* Error now thrown when calling `a.setParent(b)` on non-null b if the output/previous connection of a is not connected to an input/next connection of b without removing block from old parent's child list.
* Commented out error for when calling `a.setParent(null)` if a is connected to superior block.
* Adjusted comment to reflect that blocks were no longer being disconnected in this method.
* Also changed == to === per #4924 (`newParent` and `this.parentBlock_` must both be instances of `Blockly.Block`).
* Fixed lint errors.

* Fix error conditions for setParent #4989

* Error now thrown when calling `a.setParent(b)` on non-null b if the output/previous connection of a is not connected to an input/next connection of b without removing block from old parent's child list.
* Commented out error for when calling `a.setParent(null)` if a is connected to superior block.
* Adjusted comment to reflect that blocks were no longer being disconnected in this method.
* Also changed == to === per #4924 (`newParent` and `this.parentBlock_` must both be instances of `Blockly.Block`).
* Fixed lint errors.
* Adjusted comment.

* Removed unnecessary set to null/added tests

* One is failing (commented out), will investigate later

* Lint fix

* Removed failing test that correctly fails

* Update comments to conform to style guide

Capitalize first letter, period at end
@jschanker
Copy link
Contributor Author

jschanker commented Jul 13, 2021

Resolved by #4995 and #4999.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
issue: bug Describes why the code or behaviour is wrong issue: docs Describes missing or incorrect documentation type: cleanup
Projects
None yet
Development

No branches or pull requests

3 participants