https://bugs.documentfoundation.org/show_bug.cgi?id=172207
Bug ID: 172207
Summary: Addon toolbars: status listeners not re-registered
after ToolBarManager dispose
Product: LibreOffice
Version: 26.2.2.2 release
Hardware: All
OS: Linux (All)
Status: UNCONFIRMED
Severity: normal
Priority: medium
Component: Calc
Assignee: [email protected]
Reporter: [email protected]
When LibreOffice disposes the ToolBarManager of an `addon_*`-prefixed
toolbar (registered by an extension via Addons.xcu), the toolbar item
controllers are destroyed and `removeStatusListener` is called on the
extension's dispatch object. LibreOffice **does not re-create the
controllers** afterwards: no `OnViewCreated` follows the `OnUnload`
event, and there is no `addStatusListener` burst to restore the
controller bindings.
The toolbar remains visible in the frame but is "frozen": its buttons
keep the enabled/disabled state they had at the moment of disposal and
no longer react to subsequent state updates. The extension has no LO
API path to recover, because `XLayoutManager.requestElement` is a no-op
for addon-toolbars whose cached `m_xUIElement` is non-null.
This affects any extension that registers an `addon_*` toolbar plus an
own XStatusListener-based command state mechanism. Reproduced with
Petanque-Turnier-Manager
(https://github.com/michaelmassee/Petanque-Turnier-Manager) on
LibreOffice 26.2.2.2 / Ubuntu 24.04 LTS / Wayland.
STEPS TO REPRODUCE:
1. Install an OXT extension that
- registers an addon-toolbar via Addons.xcu
- implements its own XDispatchProvider/ProtocolHandler
- registers dynamic XStatusListener-based button state
2. Open TWO empty Calc documents (so the addon toolbar lives in both
frames).
3. From doc2, trigger an extension action that runs a sheet-modifying
background job and performs an XStorable.storeToURL (autoSave path).
4. Wait ~15-20 s after the job completes.
ACTUAL RESULT:
Without any user interaction, LO fires GlobalEvents in this order:
- OnPrepareViewClosing
- OnPrepareUnload
- 24+ removeStatusListener calls (all addon-toolbar URLs of doc1's
ProtocolHandler instance)
- OnViewClosed
- OnUnload (doc1 view)
- 24+ removeStatusListener calls (doc2's handler)
- OnUnload (doc2 view)
No corresponding OnViewCreated. No new addStatusListener calls.
Both Calc documents remain visible and editable; sheets are intact;
menu and sidebar still work. The addon toolbar in BOTH documents is
visible but frozen on its last status snapshot.
EXPECTED RESULT:
Either (a) `XLayoutManager.requestElement(addon_url)` triggered by the
extension after this state should rebuild the toolbar controllers and
result in fresh `addStatusListener` calls, OR (b) LibreOffice should
clear `m_xUIElement` for the addon-toolbar in `ToolbarLayoutManager`
when its underlying `ToolBarManager` is disposed, so that the next
`requestToolbar` enters the `createToolbar` branch.
ROOT CAUSE (source code):
`framework/source/layoutmanager/toolbarlayoutmanager.cxx:570`
(ToolbarLayoutManager::destroyToolbar):
bool bMustBeDestroyed(
!o3tl::starts_with(rResourceURL,
u"private:resource/toolbar/addon_") );
if (bMustBeDestroyed)
elem.m_xUIElement.clear(); // standard toolbars: real destroy
else
elem.m_bVisible = false; // addon toolbars: only hide
`framework/source/layoutmanager/toolbarlayoutmanager.cxx:404`
(ToolbarLayoutManager::requestToolbar):
UIElement aRequestedToolbar = impl_findToolbar(rResourceURL);
if (aRequestedToolbar.m_aName != rResourceURL)
bMustCallCreate = true;
xUIElement = aRequestedToolbar.m_xUIElement;
if (!xUIElement.is())
bMustCallCreate = true;
if (bCreateOrShowToolbar)
bNotify = bMustCallCreate ? createToolbar(...) : showToolbar(...);
Effect: for addon_* toolbars whose ToolBarManager was disposed
(`framework/source/uielement/toolbarmanager.cxx:771`,
ToolBarManager::disposing → RemoveControllers → xComponent->dispose →
controllers call removeStatusListener), the LayoutManager cache still
holds the m_xUIElement reference pointing at a disposed component.
The next `requestElement` sees `xUIElement.is() == true` and takes the
`showToolbar` branch, never rebuilding controllers.
SUGGESTED FIX:
In `ToolBarManager::disposing(EventObject)`, notify the parent
`ToolbarLayoutManager` to clear `m_xUIElement` for the resource URL.
The next `requestToolbar` would then take the `createToolbar` branch.
Alternative: in `ToolbarLayoutManager::requestToolbar`, check whether
the cached `xUIElement` points to a disposed component (via
XComponent.isDisposed or by catching DisposedException on a probe
query) and force the rebuild branch in that case.
EXTENSION WORKAROUND ATTEMPTED (failed):
The Petanque-Turnier-Manager extension detected the empty status
listener map on `onFocus` and tried `hideElement` + `showElement` +
`requestElement` for each addon URL. None of these recover the
toolbar — `requestElement` returns early because cached `xUIElement`
is non-null, and `hideElement`/`showElement` toggles only visibility.
The only known user-side recovery: close and reopen the affected
document.
--
You are receiving this mail because:
You are the assignee for the bug.