Skip to content

Commit

Permalink
fix: disposing of a workspace that has overwritten shadows (#6424)
Browse files Browse the repository at this point in the history
* fix: disposing of a workspace that has overwritten shadows

* fix: try slightly different placement to fix tests

* fix: add disposing parameter to guaruntee consistent behavior

* chore: wrap properties in a isDeadOrDying method

* chore: make disposing private
  • Loading branch information
BeksOmega authored Sep 26, 2022
1 parent 1b6a5d9 commit f2e408b
Show file tree
Hide file tree
Showing 9 changed files with 42 additions and 29 deletions.
25 changes: 21 additions & 4 deletions core/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,11 @@ export class Block implements IASTNodeLocation, IDeletable {
protected collapsed_ = false;
protected outputShape_: number|null = null;

/**
* Is the current block currently in the process of being disposed?
*/
private disposing = false;

/**
* A string representing the comment attached to this block.
*
Expand Down Expand Up @@ -316,7 +321,7 @@ export class Block implements IASTNodeLocation, IDeletable {
* @suppress {checkTypes}
*/
dispose(healStack: boolean) {
if (this.disposed) {
if (this.isDeadOrDying()) {
return;
}

Expand All @@ -338,6 +343,7 @@ export class Block implements IASTNodeLocation, IDeletable {
this.workspace.removeTypedBlock(this);
// Remove from block database.
this.workspace.removeBlockById(this.id);
this.disposing = true;

// First, dispose of all my children.
for (let i = this.childBlocks_.length - 1; i >= 0; i--) {
Expand All @@ -360,6 +366,16 @@ export class Block implements IASTNodeLocation, IDeletable {
}
}

/**
* Returns true if the block is either in the process of being disposed, or
* is disposed.
*
* @internal
*/
isDeadOrDying(): boolean {
return this.disposing || this.disposed;
}

/**
* Call initModel on all fields on the block.
* May be called more than once.
Expand Down Expand Up @@ -772,7 +788,7 @@ export class Block implements IASTNodeLocation, IDeletable {
* @returns True if deletable.
*/
isDeletable(): boolean {
return this.deletable_ && !this.isShadow_ && !this.disposed &&
return this.deletable_ && !this.isShadow_ && !this.isDeadOrDying() &&
!this.workspace.options.readOnly;
}

Expand All @@ -791,7 +807,7 @@ export class Block implements IASTNodeLocation, IDeletable {
* @returns True if movable.
*/
isMovable(): boolean {
return this.movable_ && !this.isShadow_ && !this.disposed &&
return this.movable_ && !this.isShadow_ && !this.isDeadOrDying() &&
!this.workspace.options.readOnly;
}

Expand Down Expand Up @@ -865,7 +881,8 @@ export class Block implements IASTNodeLocation, IDeletable {
* @returns True if editable.
*/
isEditable(): boolean {
return this.editable_ && !this.disposed && !this.workspace.options.readOnly;
return this.editable_ && !this.isDeadOrDying() &&
!this.workspace.options.readOnly;
}

/**
Expand Down
24 changes: 11 additions & 13 deletions core/block_svg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,7 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg,

/** Snap this block to the nearest grid point. */
snapToGrid() {
if (this.disposed) {
if (this.isDeadOrDying()) {
return; // Deleted block.
}
if (this.workspace.isDragging()) {
Expand Down Expand Up @@ -861,8 +861,7 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg,
* @suppress {checkTypes}
*/
override dispose(healStack?: boolean, animate?: boolean) {
if (this.disposed) {
// The block has already been deleted.
if (this.isDeadOrDying()) {
return;
}
Tooltip.dispose();
Expand Down Expand Up @@ -1067,13 +1066,12 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg,
if (this.workspace.isDragging()) {
// Don't change the warning text during a drag.
// Wait until the drag finishes.
this.warningTextDb.set(
id, setTimeout(() => {
if (!this.disposed) { // Check block wasn't deleted.
this.warningTextDb.delete(id);
this.setWarningText(text, id);
}
}, 100));
this.warningTextDb.set(id, setTimeout(() => {
if (!this.isDeadOrDying()) {
this.warningTextDb.delete(id);
this.setWarningText(text, id);
}
}, 100));
return;
}
if (this.isInFlyout) {
Expand Down Expand Up @@ -1541,11 +1539,11 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg,
* connected should not coincidentally line up on screen.
*/
override bumpNeighbours() {
if (this.disposed) {
return; // Deleted block.
if (this.isDeadOrDying()) {
return;
}
if (this.workspace.isDragging()) {
return; // Don't bump blocks during a drag.
return;
}
const rootBlock = this.getRootBlock();
if (rootBlock.isInFlyout) {
Expand Down
2 changes: 1 addition & 1 deletion core/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ export class Connection implements IASTNodeLocationWithBlock {
const parentBlock = this.getSourceBlock();
const shadowState = this.getShadowState();
const shadowDom = this.getShadowDom();
if (parentBlock.disposed || !shadowState && !shadowDom) {
if (parentBlock.isDeadOrDying() || !shadowState && !shadowDom) {
return null;
}

Expand Down
2 changes: 1 addition & 1 deletion core/contextmenu_items.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ function deleteNext_(deleteList: BlockSvg[], eventGroup: string) {
eventUtils.setGroup(eventGroup);
const block = deleteList.shift();
if (block) {
if (!block.disposed) {
if (!block.isDeadOrDying()) {
block.dispose(false, true);
setTimeout(deleteNext_, DELAY, deleteList, eventGroup);
} else {
Expand Down
5 changes: 3 additions & 2 deletions core/field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,8 @@ export abstract class Field implements IASTNodeLocationSvg,
* @returns The renderer constant provider.
*/
getConstants(): ConstantProvider|null {
if (!this.constants_ && this.sourceBlock_ && !this.sourceBlock_.disposed &&
if (!this.constants_ && this.sourceBlock_ &&
!this.sourceBlock_.isDeadOrDying() &&
this.sourceBlock_.workspace.rendered) {
this.constants_ = (this.sourceBlock_.workspace as WorkspaceSvg)
.getRenderer()
Expand Down Expand Up @@ -1050,7 +1051,7 @@ export abstract class Field implements IASTNodeLocationSvg,
* @param e Mouse down event.
*/
protected onMouseDown_(e: Event) {
if (!this.sourceBlock_ || this.sourceBlock_.disposed) {
if (!this.sourceBlock_ || this.sourceBlock_.isDeadOrDying()) {
return;
}
const gesture = (this.sourceBlock_.workspace as WorkspaceSvg).getGesture(e);
Expand Down
6 changes: 3 additions & 3 deletions core/field_variable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ export class FieldVariable extends FieldDropdown {
let variableTypes = this.variableTypes;
if (variableTypes === null) {
// If variableTypes is null, return all variable types.
if (this.sourceBlock_ && !this.sourceBlock_.disposed) {
if (this.sourceBlock_ && !this.sourceBlock_.isDeadOrDying()) {
return this.sourceBlock_.workspace.getVariableTypes();
}
}
Expand Down Expand Up @@ -456,7 +456,7 @@ export class FieldVariable extends FieldDropdown {
protected override onItemSelected_(menu: Menu, menuItem: MenuItem) {
const id = menuItem.getValue();
// Handle special cases.
if (this.sourceBlock_ && !this.sourceBlock_.disposed) {
if (this.sourceBlock_ && !this.sourceBlock_.isDeadOrDying()) {
if (id === internalConstants.RENAME_VARIABLE_ID) {
// Rename variable.
Variables.renameVariable(
Expand Down Expand Up @@ -515,7 +515,7 @@ export class FieldVariable extends FieldDropdown {
}
const name = this.getText();
let variableModelList: AnyDuringMigration[] = [];
if (this.sourceBlock_ && !this.sourceBlock_.disposed) {
if (this.sourceBlock_ && !this.sourceBlock_.isDeadOrDying()) {
const variableTypes = this.getVariableTypes_();
// Get a copy of the list, so that adding rename and new variable options
// doesn't modify the workspace's list.
Expand Down
2 changes: 1 addition & 1 deletion core/keyboard_nav/ast_node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ export class ASTNode {
// TODO(#6097): Use instanceof checks to exit early for values of
// curLocation that don't make sense.
const curLocationAsBlock = curLocation as Block;
if (!curLocationAsBlock || curLocationAsBlock.disposed) {
if (!curLocationAsBlock || curLocationAsBlock.isDeadOrDying()) {
return null;
}
const curRoot = curLocationAsBlock.getRootBlock();
Expand Down
4 changes: 0 additions & 4 deletions tests/mocha/event_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -997,10 +997,6 @@ suite('Events', function() {
this.eventsFireSpy, 0, Blockly.Events.BlockDelete,
{oldXml: expectedOldXml, group: ''},
workspaceSvg.id, expectedId);
assertNthCallEventArgEquals(
changeListenerSpy, 0, Blockly.Events.BlockDelete,
{oldXml: expectedOldXml, group: ''},
workspaceSvg.id, expectedId);

// Expect the workspace to not have a variable with ID 'test_block_id'.
chai.assert.isNull(this.workspace.getVariableById(TEST_BLOCK_ID));
Expand Down
1 change: 1 addition & 0 deletions tests/mocha/test_helpers/block_definitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ export function createTestBlock() {
},
'renameVarById': Blockly.Block.prototype.renameVarById,
'updateVarName': Blockly.Block.prototype.updateVarName,
'isDeadOrDying': () => false,
};
}

Expand Down

0 comments on commit f2e408b

Please sign in to comment.