Opening a menu whose items are reordered while its rows are still being created throws a TypeError, and the moved row disappears from the menu:
Exception in callback for signal: child-moved: TypeError: menu.box.add is not a function
Stack trace:
moveItemInMenu@…/dbusMenu.js:838:30
_onRootChildMoved@…/dbusMenu.js:956:19
…
set active@…/dbusMenu.js:474:22
_onMenuOpenStateChanged@…/dbusMenu.js:963:9
Every menu opening that takes this path drops another row, so the menu keeps shrinking the more it is used.
MenuUtils.moveItemInMenu() appends a row with menu.box.add() when its new position is past the last row the menu currently has. menu.box is an St.BoxLayout, and add() went away with Clutter.Container — js/ui/popupMenu.js appends with add_child(). The row has already been removed from the box at that point, so the exception leaves it out of the menu altogether.
Cause
menu.box.remove_child(family[i]);
// and add it again somewhere else
if (newpos < family.length && family[newpos] !== family[i])
menu.box.insert_child_below(family[i], family[newpos]);
else
menu.box.add(family[i]);
The else is taken when newpos >= family.length, that is when the children of a DbusMenuItem are ahead of the rows in the menu. _onRootChildAdded() creates rows from an idle callback and keeps them in _itemsBeingAdded in the meantime, so a layout update that adds and moves items in one pass gets here while some rows are still queued.
The call dates back to 2014, but the branch was unreachable while moveItemInMenu() was looking up the root item instead of the moved one; correcting that in v65 exposed it.
Suggested fix
menu.box.add_child(family[i]);
Reproduction
Any application that refills part of its menu while the menu is being opened, so that the additions are still queued when the moves of the surrounding items are processed.
indicator-test-tool/reorderTool.js does not get there: rotating a fixed set of items only ever moves items towards the front, and all of its rows exist by the time the moves arrive.
Environment
- Extension version 65 and current
master, GNOME Shell 50.2 and 50.3
Related
Opening a menu whose items are reordered while its rows are still being created throws a
TypeError, and the moved row disappears from the menu:Every menu opening that takes this path drops another row, so the menu keeps shrinking the more it is used.
MenuUtils.moveItemInMenu()appends a row withmenu.box.add()when its new position is past the last row the menu currently has.menu.boxis anSt.BoxLayout, andadd()went away withClutter.Container—js/ui/popupMenu.jsappends withadd_child(). The row has already been removed from the box at that point, so the exception leaves it out of the menu altogether.Cause
The
elseis taken whennewpos >= family.length, that is when the children of aDbusMenuItemare ahead of the rows in the menu._onRootChildAdded()creates rows from an idle callback and keeps them in_itemsBeingAddedin the meantime, so a layout update that adds and moves items in one pass gets here while some rows are still queued.The call dates back to 2014, but the branch was unreachable while
moveItemInMenu()was looking up the root item instead of the moved one; correcting that in v65 exposed it.Suggested fix
Reproduction
Any application that refills part of its menu while the menu is being opened, so that the additions are still queued when the moves of the surrounding items are processed.
indicator-test-tool/reorderTool.jsdoes not get there: rotating a fixed set of items only ever moves items towards the front, and all of its rows exist by the time the moves arrive.Environment
master, GNOME Shell 50.2 and 50.3Related
child-movedargument order, whose fix in v65 made this branch reachableonAboutToShow, so its tray menu runs into this on every click on the icon