Files
libreoffice/svtools/source/control/valueimp.hxx
Michael Weghorn b3c460980c valueset a11y: Dispose ValueItemAcc objects
So far, the ValueItemAcc objects created by
ValueSet were never disposed.

While this is a preexisting issue, this started triggering
crashes with the qt6 VCL plugin after

    Change-Id: If448008b3a6dc7b22a06b6ed551b08a40b2d5de3
    Author: Michael Weghorn <m.weghorn@posteo.de>
    Date:   Tue Feb 25 12:14:24 2025 +0100

        valueset a11y: Use OAccessibleComponentHelper for ValueItemAcc

as described in its commit message.

Fix this by disposing the objects in ValueSet::ImplDeleteItems,
and not just sending an AccessibleEventId::CHILD event.

Adjust the logic to be independent of whether the item is visible,
but instead do this for all items that have an associated ValueItemAcc.
Add a new `bCreate` param to ValueSetItem::GetAccessible and pass
false here to avoid creating new ones.

In the ValueSet dtor, call `ImplDeleteItems` before invalidating
the accessible object, as it may still be needed in `ImplDeleteItems`
to send the events.

This fixes the crash on exit for the scenario described in the
above-mentioned commit.

Backtrace of such a crash/assert:

    soffice.bin: /home/michi/development/git/libreoffice/comphelper/source/misc/accessibleeventnotifier.cxx:142: bool (anonymous namespace)::implLookupClient(const AccessibleEventNotifier::TClientId, ClientMap::iterator &): Assertion `rClients.end() != rPos && "AccessibleEventNotifier::implLookupClient: invalid client id " "(did you register your client?)!"' failed.
    [New Thread 9546.9547]
    [New Thread 9546.9548]
    [New Thread 9546.9549]
    [New Thread 9546.9557]

    Thread 1 received signal SIGABRT, Aborted.
    __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at ./nptl/pthread_kill.c:44
    warning: 44     ./nptl/pthread_kill.c: No such file or directory
    (rr) bt
    #0  __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at ./nptl/pthread_kill.c:44
    #1  0x00007ff8c289de2f in __pthread_kill_internal (threadid=<optimized out>, signo=6) at ./nptl/pthread_kill.c:78
    #2  0x00007ff8c2849d02 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
    #3  0x00007ff8c28324f0 in __GI_abort () at ./stdlib/abort.c:79
    #4  0x00007ff8c2832418 in __assert_fail_base
        (fmt=0x7ff8c29b6ca0 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", assertion=assertion@entry=0x7ff8c0f27cc6 "rClients.end() != rPos && \"AccessibleEventNotifier::implLookupClient: invalid client id \" \"(did you register your client?)!\"", file=file@entry=0x7ff8c0f360b9 "/home/michi/development/git/libreoffice/comphelper/source/misc/accessibleeventnotifier.cxx", line=line@entry=142, function=function@entry=0x7ff8c0efd262 "bool (anonymous namespace)::implLookupClient(const AccessibleEventNotifier::TClientId, ClientMap::iterator &)") at ./assert/assert.c:96
    #5  0x00007ff8c2842612 in __assert_fail
        (assertion=0x7ff8c0f27cc6 "rClients.end() != rPos && \"AccessibleEventNotifier::implLookupClient: invalid client id \" \"(did you register your client?)!\"", file=0x7ff8c0f360b9 "/home/michi/development/git/libreoffice/comphelper/source/misc/accessibleeventnotifier.cxx", line=142, function=0x7ff8c0efd262 "bool (anonymous namespace)::implLookupClient(const AccessibleEventNotifier::TClientId, ClientMap::iterator &)") at ./assert/assert.c:105
    #6  0x00007ff8c10eb4c8 in (anonymous namespace)::implLookupClient (nClient=24, rPos=...) at /home/michi/development/git/libreoffice/comphelper/source/misc/accessibleeventnotifier.cxx:140
    #7  0x00007ff8c10eb938 in comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing (_nClient=24, _rxEventSource=uno::Reference to (ValueItemAcc *) 0x558da6f7a220)
        at /home/michi/development/git/libreoffice/comphelper/source/misc/accessibleeventnotifier.cxx:185
    #8  0x00007ff8c10e751d in comphelper::OCommonAccessibleComponent::disposing (this=0x558da6f7a220) at /home/michi/development/git/libreoffice/comphelper/source/misc/accessiblecomponenthelper.cxx:61
    #9  0x00007ff8c0b3cee0 in cppu::WeakComponentImplHelperBase::dispose (this=0x558da6f7a220) at /home/michi/development/git/libreoffice/cppuhelper/source/implbase.cxx:104
    #10 0x00007ff8bb84a0c5 in cppu::PartialWeakComponentImplHelper<com::sun::accessibility::XAccessibleContext2, com::sun::accessibility::XAccessibleEventBroadcaster>::dispose (this=0x558da6f7a220)
        at include/cppuhelper/compbase.hxx:90
    #11 0x00007ff8c0b3cc07 in cppu::WeakComponentImplHelperBase::release (this=0x558da6f7a220) at /home/michi/development/git/libreoffice/cppuhelper/source/implbase.cxx:79
    #12 0x00007ff8bb84d6c5 in cppu::PartialWeakComponentImplHelper<com::sun::accessibility::XAccessibleContext2, com::sun::accessibility::XAccessibleEventBroadcaster>::release (this=0x558da6f7a220)
        at include/cppuhelper/compbase.hxx:86
    #13 0x00007ff8bb8e0b95 in cppu::ImplInheritanceHelper<comphelper::OCommonAccessibleComponent, com::sun::accessibility::XAccessibleComponent>::release (this=0x558da6f7a220) at include/cppuhelper/implbase.hxx:171
    #14 0x00007ff8bb97d595 in cppu::ImplInheritanceHelper<comphelper::OAccessibleComponentHelper, com::sun::accessibility::XAccessible>::release (this=0x558da6f7a220) at include/cppuhelper/implbase.hxx:171
    #15 0x00007ff8af309baa in com::sun::uno::Reference<com::sun::accessibility::XAccessible>::~Reference (this=0x558da6f7a428) at include/com/sun/star/uno/Reference.hxx:114
    #16 0x00007ff8af321bad in QtAccessibleWidget::~QtAccessibleWidget (this=0x558da6f7a3e0) at vcl/inc/qt6/../qt5/QtAccessibleWidget.hxx:39
    #17 0x00007ff8af321c49 in QtAccessibleWidget::~QtAccessibleWidget (this=0x558da6f7a3e0) at vcl/inc/qt6/../qt5/QtAccessibleWidget.hxx:39
    #18 0x00007ff8ad8faf48 in QAccessibleCache::deleteInterface (this=0x558d9fbd0ac0, id=2147483692, obj=0x558da6eecdb0) at /home/michi/development/git/qt5/qtbase/src/gui/accessible/qaccessiblecache.cpp:173
    #19 0x00007ff8ad8fad78 in QAccessibleCache::~QAccessibleCache (this=0x558d9fbd0ac0) at /home/michi/development/git/qt5/qtbase/src/gui/accessible/qaccessiblecache.cpp:31
    #20 0x00007ff8ad8faf8d in QAccessibleCache::~QAccessibleCache (this=0x558d9fbd0ac0) at /home/michi/development/git/qt5/qtbase/src/gui/accessible/qaccessiblecache.cpp:29
    #21 0x00007ff8ad8fb027 in cleanupAccessibleCache () at /home/michi/development/git/qt5/qtbase/src/gui/accessible/qaccessiblecache.cpp:24
    #22 0x00007ff8ae44a792 in qt_call_post_routines () at /home/michi/development/git/qt5/qtbase/src/corelib/kernel/qcoreapplication.cpp:343
    #23 0x00007ff8ac3ddee4 in QApplication::~QApplication (this=0x558d9e8c76c0) at /home/michi/development/git/qt5/qtbase/src/widgets/kernel/qapplication.cpp:667
    #24 0x00007ff8ac3de29d in QApplication::~QApplication (this=0x558d9e8c76c0) at /home/michi/development/git/qt5/qtbase/src/widgets/kernel/qapplication.cpp:663
    #25 0x00007ff8af3d63b8 in std::default_delete<QApplication>::operator() (this=0x558d9e955870, __ptr=0x558d9e8c76c0) at /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/unique_ptr.h:93
    #26 0x00007ff8af3d7da8 in std::__uniq_ptr_impl<QApplication, std::default_delete<QApplication> >::reset (this=0x558d9e955870, __p=0x0) at /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/unique_ptr.h:205
    #27 0x00007ff8af3cfcbd in std::unique_ptr<QApplication, std::default_delete<QApplication> >::reset (this=0x558d9e955870, __p=0x0) at /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/unique_ptr.h:504
    #28 0x00007ff8af3c7b34 in QtInstance::~QtInstance (this=0x558d9e9556e0) at vcl/qt6/../qt5/QtInstance.cxx:323
    #29 0x00007ff8af3c7c29 in QtInstance::~QtInstance (this=0x558d9e9556e0) at vcl/qt6/../qt5/QtInstance.cxx:320
    #30 0x00007ff8b9bb1c54 in DestroySalInstance (pInst=0x558d9e9556f0) at /home/michi/development/git/libreoffice/vcl/source/app/salplug.cxx:361
    #31 0x00007ff8b9ca1509 in DeInitVCL () at /home/michi/development/git/libreoffice/vcl/source/app/svmain.cxx:629
    #32 0x00007ff8b9c9fa4f in ImplSVMain () at /home/michi/development/git/libreoffice/vcl/source/app/svmain.cxx:241
    #33 0x00007ff8b9ca15e9 in SVMain () at /home/michi/development/git/libreoffice/vcl/source/app/svmain.cxx:248
    #34 0x00007ff8c2b9f4ba in soffice_main () at /home/michi/development/git/libreoffice/desktop/source/app/sofficemain.cxx:122
    #35 0x0000558d9330ba6d in sal_main () at /home/michi/development/git/libreoffice/desktop/source/app/main.c:51
    #36 0x0000558d9330ba47 in main (argc=2, argv=0x7ffd2669fb48) at /home/michi/development/git/libreoffice/desktop/source/app/main.c:49

Change-Id: Ifa7e18393edcc1889bcb390fa453c611d9345bdc
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/182174
Tested-by: Jenkins
Reviewed-by: Michael Weghorn <m.weghorn@posteo.de>
2025-02-26 08:09:46 +01:00

209 lines
8.2 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#pragma once
#include <tools/color.hxx>
#include <vcl/image.hxx>
#include <cppuhelper/implbase.hxx>
#include <comphelper/accessiblecomponenthelper.hxx>
#include <comphelper/compbase.hxx>
#include <com/sun/star/accessibility/XAccessible.hpp>
#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
#include <mutex>
#include <vector>
#define VALUESET_ITEM_NONEITEM 0xFFFE
enum ValueSetItemType
{
VALUESETITEM_NONE,
VALUESETITEM_IMAGE,
VALUESETITEM_IMAGE_AND_TEXT,
VALUESETITEM_COLOR,
VALUESETITEM_USERDRAW
};
class ValueItemAcc;
class ValueSet;
struct ValueSetItem
{
ValueSet& mrParent;
OUString maText;
void* mpData;
rtl::Reference< ValueItemAcc > mxAcc;
Image maImage;
Color maColor;
sal_uInt16 mnId;
sal_uInt8 meType;
bool mbVisible;
explicit ValueSetItem( ValueSet& rParent );
~ValueSetItem();
const rtl::Reference<ValueItemAcc>& GetAccessible(bool bCreate = true);
};
class ValueSetAcc final
: public cppu::ImplInheritanceHelper<comphelper::OAccessibleComponentHelper,
css::accessibility::XAccessible,
css::accessibility::XAccessibleSelection>
{
public:
explicit ValueSetAcc(ValueSet* pValueSet);
virtual ~ValueSetAcc() override;
void FireAccessibleEvent( short nEventId, const css::uno::Any& rOldValue, const css::uno::Any& rNewValue );
bool HasAccessibleListeners() const;
public:
/** Called by the corresponding ValueSet when it gets the focus.
Stores the new focus state and broadcasts a state change event.
*/
void GetFocus();
/** Called by the corresponding ValueSet when it loses the focus.
Stores the new focus state and broadcasts a state change event.
*/
void LoseFocus();
/** Called by the corresponding ValueSet when it gets destroyed. */
void Invalidate();
// XAccessible
virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL getAccessibleContext( ) override;
// XAccessibleContext
virtual sal_Int64 SAL_CALL getAccessibleChildCount( ) override;
virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int64 i ) override;
virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent( ) override;
virtual sal_Int64 SAL_CALL getAccessibleIndexInParent( ) override;
virtual sal_Int16 SAL_CALL getAccessibleRole( ) override;
virtual OUString SAL_CALL getAccessibleDescription( ) override;
virtual OUString SAL_CALL getAccessibleName( ) override;
virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet( ) override;
virtual sal_Int64 SAL_CALL getAccessibleStateSet( ) override;
virtual css::lang::Locale SAL_CALL getLocale( ) override;
// OCommonAccessibleComponent
virtual css::awt::Rectangle implGetBounds() override;
// XAccessibleComponent
virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint( const css::awt::Point& aPoint ) override;
virtual void SAL_CALL grabFocus( ) override;
virtual sal_Int32 SAL_CALL getForeground( ) override;
virtual sal_Int32 SAL_CALL getBackground( ) override;
// XAccessibleSelection
virtual void SAL_CALL selectAccessibleChild( sal_Int64 nChildIndex ) override;
virtual sal_Bool SAL_CALL isAccessibleChildSelected( sal_Int64 nChildIndex ) override;
virtual void SAL_CALL clearAccessibleSelection( ) override;
virtual void SAL_CALL selectAllAccessibleChildren( ) override;
virtual sal_Int64 SAL_CALL getSelectedAccessibleChildCount( ) override;
virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex ) override;
virtual void SAL_CALL deselectAccessibleChild( sal_Int64 nSelectedChildIndex ) override;
private:
ValueSet* mpValueSet;
/// The current FOCUSED state.
bool mbIsFocused;
/** Return the number of items. This takes the None-Item into account.
*/
sal_uInt16 getItemCount() const;
/** Return the item associated with the given index. The None-Item is
taken into account which, when present, is taken to be the first
(with index 0) item.
@param nIndex
Index of the item to return. The index 0 denotes the None-Item
when present.
@return
Returns NULL when the given index is out of range.
*/
ValueSetItem* getItem (sal_uInt16 nIndex) const;
/** Check whether or not the object has been disposed (or is in the
state of being disposed). If that is the case then
DisposedException is thrown to inform the (indirect) caller of the
foul deed.
@param bCheckValueSet
Whether to also check that the ValueSet is non-null.
@throws css::lang::DisposedException
*/
void ThrowIfDisposed(bool bCheckValueSet = true);
/** Check whether the value set has a 'none' field, i.e. a field (button)
that deselects any items (selects none of them).
@return
Returns <true/> if there is a 'none' field and <false/> if it is
missing.
*/
bool HasNoneField() const;
};
class ValueItemAcc : public cppu::ImplInheritanceHelper<comphelper::OAccessibleComponentHelper,
css::accessibility::XAccessible>
{
private:
ValueSetItem* mpValueSetItem;
public:
ValueItemAcc(ValueSetItem* pValueSetItem);
virtual ~ValueItemAcc() override;
void ValueSetItemDestroyed();
void FireAccessibleEvent( short nEventId, const css::uno::Any& rOldValue, const css::uno::Any& rNewValue );
public:
// XAccessible
virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL getAccessibleContext( ) override;
// XAccessibleContext
virtual sal_Int64 SAL_CALL getAccessibleChildCount( ) override;
virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int64 i ) override;
virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent( ) override;
virtual sal_Int64 SAL_CALL getAccessibleIndexInParent( ) override;
virtual sal_Int16 SAL_CALL getAccessibleRole( ) override;
virtual OUString SAL_CALL getAccessibleDescription( ) override;
virtual OUString SAL_CALL getAccessibleName( ) override;
virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet( ) override;
virtual sal_Int64 SAL_CALL getAccessibleStateSet( ) override;
virtual css::lang::Locale SAL_CALL getLocale( ) override;
// OCommonAccessibleComponent
virtual css::awt::Rectangle implGetBounds() override;
// XAccessibleComponent
virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint( const css::awt::Point& aPoint ) override;
virtual void SAL_CALL grabFocus( ) override;
virtual sal_Int32 SAL_CALL getForeground( ) override;
virtual sal_Int32 SAL_CALL getBackground( ) override;
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */