diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /comphelper/source/misc/accessibleeventnotifier.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | comphelper/source/misc/accessibleeventnotifier.cxx | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/comphelper/source/misc/accessibleeventnotifier.cxx b/comphelper/source/misc/accessibleeventnotifier.cxx new file mode 100644 index 000000000..f14176a03 --- /dev/null +++ b/comphelper/source/misc/accessibleeventnotifier.cxx @@ -0,0 +1,280 @@ +/* -*- 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 . + */ + +#include <comphelper/accessibleeventnotifier.hxx> +#include <com/sun/star/accessibility/XAccessibleEventListener.hpp> +#include <rtl/instance.hxx> +#include <comphelper/interfacecontainer2.hxx> + +#include <map> +#include <memory> +#include <limits> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::accessibility; +using namespace ::comphelper; + +namespace { + +typedef std::pair< AccessibleEventNotifier::TClientId, + AccessibleEventObject > ClientEvent; + +typedef std::map< AccessibleEventNotifier::TClientId, + ::comphelper::OInterfaceContainerHelper2* > ClientMap; + +/// key is the end of the interval, value is the start of the interval +typedef std::map<AccessibleEventNotifier::TClientId, + AccessibleEventNotifier::TClientId> IntervalMap; + +struct lclMutex : public rtl::Static< ::osl::Mutex, lclMutex > {}; + +struct Clients : public rtl::Static< ClientMap, Clients > {}; + +struct FreeIntervals : public rtl::StaticWithInit<IntervalMap, FreeIntervals> +{ + IntervalMap operator() () + { + IntervalMap map; + map.insert(std::make_pair( + std::numeric_limits<AccessibleEventNotifier::TClientId>::max(), 1)); + return map; + } +}; + +void releaseId(AccessibleEventNotifier::TClientId const nId) +{ + IntervalMap & rFreeIntervals(FreeIntervals::get()); + IntervalMap::iterator const upper(rFreeIntervals.upper_bound(nId)); + assert(upper != rFreeIntervals.end()); + assert(nId < upper->second); // second is start of the interval! + if (nId + 1 == upper->second) + { + --upper->second; // add nId to existing interval + } + else + { + IntervalMap::iterator const lower(rFreeIntervals.lower_bound(nId)); + if (lower != rFreeIntervals.end() && lower->first == nId - 1) + { + // add nId by replacing lower with new merged entry + rFreeIntervals.insert(std::make_pair(nId, lower->second)); + rFreeIntervals.erase(lower); + } + else // otherwise just add new 1-element interval + { + rFreeIntervals.insert(std::make_pair(nId, nId)); + } + } + // currently it's not checked whether intervals can be merged now + // hopefully that won't be a problem in practice +} + +/// generates a new client id +AccessibleEventNotifier::TClientId generateId() +{ + IntervalMap & rFreeIntervals(FreeIntervals::get()); + assert(!rFreeIntervals.empty()); + IntervalMap::iterator const iter(rFreeIntervals.begin()); + AccessibleEventNotifier::TClientId const nFirst = iter->first; + AccessibleEventNotifier::TClientId const nFreeId = iter->second; + assert(nFreeId <= nFirst); + if (nFreeId != nFirst) + { + ++iter->second; // remove nFreeId from interval + } + else + { + rFreeIntervals.erase(iter); // remove 1-element interval + } + + assert(Clients::get().end() == Clients::get().find(nFreeId)); + + return nFreeId; +} + +/** looks up a client in our client map, asserts if it cannot find it or + no event thread is present + + @precond + to be called with our mutex locked + + @param nClient + the id of the client to lookup + @param rPos + out-parameter for the position of the client in the client map + + @return + <TRUE/> if and only if the client could be found and + <arg>rPos</arg> has been filled with its position +*/ +bool implLookupClient( + const AccessibleEventNotifier::TClientId nClient, + ClientMap::iterator& rPos ) +{ + // look up this client + ClientMap &rClients = Clients::get(); + rPos = rClients.find( nClient ); + assert( rClients.end() != rPos && + "AccessibleEventNotifier::implLookupClient: invalid client id " + "(did you register your client?)!" ); + + return ( rClients.end() != rPos ); +} + +} // anonymous namespace + +namespace comphelper { + +AccessibleEventNotifier::TClientId AccessibleEventNotifier::registerClient() +{ + ::osl::MutexGuard aGuard( lclMutex::get() ); + + // generate a new client id + TClientId nNewClientId = generateId( ); + + // the event listeners for the new client + ::comphelper::OInterfaceContainerHelper2 *const pNewListeners = + new ::comphelper::OInterfaceContainerHelper2( lclMutex::get() ); + // note that we're using our own mutex here, so the listener containers for all + // our clients share this same mutex. + // this is a reminiscence to the days where the notifier was asynchronous. Today this is + // completely nonsense, and potentially slowing down the Office me thinks... + + // add the client + Clients::get().emplace( nNewClientId, pNewListeners ); + + // outta here + return nNewClientId; +} + +void AccessibleEventNotifier::revokeClient( const TClientId _nClient ) +{ + ::osl::MutexGuard aGuard( lclMutex::get() ); + + ClientMap::iterator aClientPos; + if ( !implLookupClient( _nClient, aClientPos ) ) + // already asserted in implLookupClient + return; + + // remove it from the clients map + delete aClientPos->second; + Clients::get().erase( aClientPos ); + releaseId(_nClient); +} + +void AccessibleEventNotifier::revokeClientNotifyDisposing( + const TClientId _nClient, const Reference< XInterface >& _rxEventSource ) +{ + std::unique_ptr<::comphelper::OInterfaceContainerHelper2> pListeners; + + { + // rhbz#1001768 drop the mutex before calling disposeAndClear + ::osl::MutexGuard aGuard( lclMutex::get() ); + + ClientMap::iterator aClientPos; + if (!implLookupClient(_nClient, aClientPos)) + // already asserted in implLookupClient + return; + + // notify the listeners + pListeners.reset(aClientPos->second); + + // we do not need the entry in the clients map anymore + // (do this before actually notifying, because some client + // implementations have re-entrance problems and call into + // revokeClient while we are notifying from here) + Clients::get().erase(aClientPos); + releaseId(_nClient); + } + + // notify the "disposing" event for this client + EventObject aDisposalEvent; + aDisposalEvent.Source = _rxEventSource; + + // now really do the notification + pListeners->disposeAndClear( aDisposalEvent ); +} + +sal_Int32 AccessibleEventNotifier::addEventListener( + const TClientId _nClient, const Reference< XAccessibleEventListener >& _rxListener ) +{ + ::osl::MutexGuard aGuard( lclMutex::get() ); + + ClientMap::iterator aClientPos; + if ( !implLookupClient( _nClient, aClientPos ) ) + // already asserted in implLookupClient + return 0; + + if ( _rxListener.is() ) + aClientPos->second->addInterface( _rxListener ); + + return aClientPos->second->getLength(); +} + +sal_Int32 AccessibleEventNotifier::removeEventListener( + const TClientId _nClient, const Reference< XAccessibleEventListener >& _rxListener ) +{ + ::osl::MutexGuard aGuard( lclMutex::get() ); + + ClientMap::iterator aClientPos; + if ( !implLookupClient( _nClient, aClientPos ) ) + // already asserted in implLookupClient + return 0; + + if ( _rxListener.is() ) + aClientPos->second->removeInterface( _rxListener ); + + return aClientPos->second->getLength(); +} + +void AccessibleEventNotifier::addEvent( const TClientId _nClient, const AccessibleEventObject& _rEvent ) +{ + std::vector< Reference< XInterface > > aListeners; + + { + ::osl::MutexGuard aGuard( lclMutex::get() ); + + ClientMap::iterator aClientPos; + if ( !implLookupClient( _nClient, aClientPos ) ) + // already asserted in implLookupClient + return; + + // since we're synchronous, again, we want to notify immediately + aListeners = aClientPos->second->getElements(); + } + + // default handling: loop through all listeners, and notify them + for ( const auto& rListener : aListeners ) + { + try + { + static_cast< XAccessibleEventListener* >( rListener.get() )->notifyEvent( _rEvent ); + } + catch( const Exception& ) + { + // no assertion, because a broken access remote bridge or something like this + // can cause this exception + } + } +} + +} // namespace comphelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |