1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_comphelper.hxx" 26 #include <comphelper/accessiblecontexthelper.hxx> 27 #include <comphelper/accessibleeventbuffer.hxx> 28 #include <osl/diagnose.h> 29 #include <cppuhelper/weakref.hxx> 30 #include <com/sun/star/accessibility/AccessibleEventId.hpp> 31 #include <com/sun/star/accessibility/AccessibleStateType.hpp> 32 #include <comphelper/accessibleeventnotifier.hxx> 33 34 //......................................................................... 35 namespace comphelper 36 { 37 //......................................................................... 38 39 using namespace ::com::sun::star::uno; 40 using namespace ::com::sun::star::lang; 41 using namespace ::com::sun::star::accessibility; 42 43 //===================================================================== 44 //= OContextHelper_Impl 45 //===================================================================== 46 /** implementation class for OAccessibleContextHelper. No own thread safety! 47 */ 48 class OContextHelper_Impl 49 { 50 private: 51 OAccessibleContextHelper* m_pAntiImpl; // the owning instance 52 IMutex* m_pExternalLock; // the optional additional external lock 53 54 ::cppu::OInterfaceContainerHelper* m_pEventListeners; 55 WeakReference< XAccessible > m_aCreator; // the XAccessible which created our XAccessibleContext 56 57 AccessibleEventNotifier::TClientId m_nClientId; 58 59 public: getCreator() const60 inline Reference< XAccessible > getCreator( ) const { return m_aCreator; } 61 inline void setCreator( const Reference< XAccessible >& _rAcc ); 62 getExternalLock()63 inline IMutex* getExternalLock( ) { return m_pExternalLock; } setExternalLock(IMutex * _pLock)64 inline void setExternalLock( IMutex* _pLock ) { m_pExternalLock = _pLock; } 65 66 inline AccessibleEventNotifier::TClientId getClientId() const67 getClientId() const { return m_nClientId; } setClientId(const AccessibleEventNotifier::TClientId _nId)68 inline void setClientId( const AccessibleEventNotifier::TClientId _nId ) 69 { m_nClientId = _nId; } 70 71 public: OContextHelper_Impl(OAccessibleContextHelper * _pAntiImpl)72 OContextHelper_Impl( OAccessibleContextHelper* _pAntiImpl ) 73 :m_pAntiImpl( _pAntiImpl ) 74 ,m_pExternalLock( NULL ) 75 ,m_pEventListeners( NULL ) 76 ,m_nClientId( 0 ) 77 { 78 } 79 }; 80 81 //--------------------------------------------------------------------- setCreator(const Reference<XAccessible> & _rAcc)82 inline void OContextHelper_Impl::setCreator( const Reference< XAccessible >& _rAcc ) 83 { 84 m_aCreator = _rAcc; 85 } 86 87 //===================================================================== 88 //= OAccessibleContextHelper 89 //===================================================================== 90 //--------------------------------------------------------------------- OAccessibleContextHelper()91 OAccessibleContextHelper::OAccessibleContextHelper( ) 92 :OAccessibleContextHelper_Base( GetMutex() ) 93 ,m_pImpl( NULL ) 94 { 95 m_pImpl = new OContextHelper_Impl( this ); 96 } 97 98 //--------------------------------------------------------------------- OAccessibleContextHelper(IMutex * _pExternalLock)99 OAccessibleContextHelper::OAccessibleContextHelper( IMutex* _pExternalLock ) 100 :OAccessibleContextHelper_Base( GetMutex() ) 101 ,m_pImpl( NULL ) 102 { 103 m_pImpl = new OContextHelper_Impl( this ); 104 m_pImpl->setExternalLock( _pExternalLock ); 105 } 106 107 //--------------------------------------------------------------------- forgetExternalLock()108 void OAccessibleContextHelper::forgetExternalLock() 109 { 110 m_pImpl->setExternalLock( NULL ); 111 } 112 113 //--------------------------------------------------------------------- ~OAccessibleContextHelper()114 OAccessibleContextHelper::~OAccessibleContextHelper( ) 115 { 116 forgetExternalLock(); 117 // this ensures that the lock, which may be already destroyed as part of the derivee, 118 // is not used anymore 119 120 ensureDisposed(); 121 122 delete m_pImpl; 123 m_pImpl = NULL; 124 } 125 126 //--------------------------------------------------------------------- getExternalLock()127 IMutex* OAccessibleContextHelper::getExternalLock( ) 128 { 129 return m_pImpl->getExternalLock(); 130 } 131 132 //--------------------------------------------------------------------- disposing()133 void SAL_CALL OAccessibleContextHelper::disposing() 134 { 135 ::osl::ClearableMutexGuard aGuard( GetMutex() ); 136 137 if ( m_pImpl->getClientId( ) ) 138 { 139 AccessibleEventNotifier::revokeClientNotifyDisposing( m_pImpl->getClientId( ), *this ); 140 m_pImpl->setClientId( 0 ); 141 } 142 } 143 144 //--------------------------------------------------------------------- addEventListener(const Reference<XAccessibleEventListener> & _rxListener)145 void SAL_CALL OAccessibleContextHelper::addEventListener( const Reference< XAccessibleEventListener >& _rxListener ) throw (RuntimeException) 146 { 147 OMutexGuard aGuard( getExternalLock() ); 148 // don't use the OContextEntryGuard - it will throw an exception if we're not alive 149 // anymore, while the most recent specification for XComponent states that we should 150 // silently ignore the call in such a situation 151 if ( !isAlive() ) 152 { 153 if ( _rxListener.is() ) 154 _rxListener->disposing( EventObject( *this ) ); 155 return; 156 } 157 158 if ( _rxListener.is() ) 159 { 160 if ( !m_pImpl->getClientId( ) ) 161 m_pImpl->setClientId( AccessibleEventNotifier::registerClient( ) ); 162 163 AccessibleEventNotifier::addEventListener( m_pImpl->getClientId( ), _rxListener ); 164 } 165 } 166 167 //--------------------------------------------------------------------- removeEventListener(const Reference<XAccessibleEventListener> & _rxListener)168 void SAL_CALL OAccessibleContextHelper::removeEventListener( const Reference< XAccessibleEventListener >& _rxListener ) throw (RuntimeException) 169 { 170 OMutexGuard aGuard( getExternalLock() ); 171 // don't use the OContextEntryGuard - it will throw an exception if we're not alive 172 // anymore, while the most recent specification for XComponent states that we should 173 // silently ignore the call in such a situation 174 if ( !isAlive() ) 175 return; 176 177 if ( _rxListener.is() ) 178 { 179 sal_Int32 nListenerCount = AccessibleEventNotifier::removeEventListener( m_pImpl->getClientId( ), _rxListener ); 180 if ( !nListenerCount ) 181 { 182 // no listeners anymore 183 // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client), 184 // and at least to us not firing any events anymore, in case somebody calls 185 // NotifyAccessibleEvent, again 186 AccessibleEventNotifier::revokeClient( m_pImpl->getClientId( ) ); 187 m_pImpl->setClientId( 0 ); 188 } 189 } 190 } 191 192 //--------------------------------------------------------------------- NotifyAccessibleEvent(const sal_Int16 _nEventId,const Any & _rOldValue,const Any & _rNewValue)193 void SAL_CALL OAccessibleContextHelper::NotifyAccessibleEvent( const sal_Int16 _nEventId, 194 const Any& _rOldValue, const Any& _rNewValue ) 195 { 196 if ( !m_pImpl->getClientId( ) ) 197 // if we don't have a client id for the notifier, then we don't have listeners, then 198 // we don't need to notify anything 199 return; 200 201 // build an event object 202 AccessibleEventObject aEvent; 203 aEvent.Source = *this; 204 aEvent.EventId = _nEventId; 205 aEvent.OldValue = _rOldValue; 206 aEvent.NewValue = _rNewValue; 207 208 // let the notifier handle this event 209 AccessibleEventNotifier::addEvent( m_pImpl->getClientId( ), aEvent ); 210 } 211 212 //--------------------------------------------------------------------- BufferAccessibleEvent(const sal_Int16 _nEventId,const Any & _rOldValue,const Any & _rNewValue,AccessibleEventBuffer & _rBuffer)213 void SAL_CALL OAccessibleContextHelper::BufferAccessibleEvent( const sal_Int16 _nEventId, 214 const Any& _rOldValue, const Any& _rNewValue, 215 AccessibleEventBuffer & _rBuffer ) 216 { 217 // TODO: this whole method (as well as the class AccessibleEventBuffer) should be removed 218 // The reasons why they have been introduces id that we needed to collect a set of events 219 // before notifying them altogether (after releasing our mutex). With the other 220 // NotifyAccessibleEvent being asynchronous now, this should not be necessary anymore 221 // - clients could use the other version now. 222 223 // copy our current listeners 224 Sequence< Reference< XInterface > > aListeners; 225 if ( m_pImpl->getClientId( ) ) 226 aListeners = AccessibleEventNotifier::getEventListeners( m_pImpl->getClientId( ) ); 227 228 if ( aListeners.getLength() ) 229 { 230 AccessibleEventObject aEvent; 231 aEvent.Source = *this; 232 OSL_ENSURE( aEvent.Source.is(), "OAccessibleContextHelper::BufferAccessibleEvent: invalid creator!" ); 233 aEvent.EventId = _nEventId; 234 aEvent.OldValue = _rOldValue; 235 aEvent.NewValue = _rNewValue; 236 237 _rBuffer.addEvent( aEvent, aListeners ); 238 } 239 } 240 241 //--------------------------------------------------------------------- isAlive() const242 sal_Bool OAccessibleContextHelper::isAlive() const 243 { 244 return !GetBroadcastHelper().bDisposed && !GetBroadcastHelper().bInDispose; 245 } 246 247 //--------------------------------------------------------------------- ensureAlive() const248 void OAccessibleContextHelper::ensureAlive() const SAL_THROW( ( DisposedException ) ) 249 { 250 if( !isAlive() ) 251 throw DisposedException(); 252 } 253 254 //--------------------------------------------------------------------- ensureDisposed()255 void OAccessibleContextHelper::ensureDisposed( ) 256 { 257 if ( !GetBroadcastHelper().bDisposed ) 258 { 259 OSL_ENSURE( 0 == m_refCount, "OAccessibleContextHelper::ensureDisposed: this method _has_ to be called from without your dtor only!" ); 260 acquire(); 261 dispose(); 262 } 263 } 264 265 //--------------------------------------------------------------------- lateInit(const Reference<XAccessible> & _rxAccessible)266 void OAccessibleContextHelper::lateInit( const Reference< XAccessible >& _rxAccessible ) 267 { 268 m_pImpl->setCreator( _rxAccessible ); 269 } 270 271 //--------------------------------------------------------------------- getAccessibleCreator() const272 Reference< XAccessible > OAccessibleContextHelper::getAccessibleCreator( ) const 273 { 274 return m_pImpl->getCreator(); 275 } 276 277 //--------------------------------------------------------------------- getAccessibleIndexInParent()278 sal_Int32 SAL_CALL OAccessibleContextHelper::getAccessibleIndexInParent( ) throw (RuntimeException) 279 { 280 OExternalLockGuard aGuard( this ); 281 282 // -1 for child not found/no parent (according to specification) 283 sal_Int32 nRet = -1; 284 285 try 286 { 287 288 Reference< XAccessibleContext > xParentContext( implGetParentContext() ); 289 290 // iterate over parent's children and search for this object 291 if ( xParentContext.is() ) 292 { 293 // our own XAccessible for comparing with the children of our parent 294 Reference< XAccessible > xCreator( m_pImpl->getCreator() ); 295 296 OSL_ENSURE( xCreator.is(), "OAccessibleContextHelper::getAccessibleIndexInParent: invalid creator!" ); 297 // two ideas why this could be NULL: 298 // * nobody called our late ctor (init), so we never had a creator at all -> bad 299 // * the creator is already dead. In this case, we should have been disposed, and 300 // never survived the above OContextEntryGuard. 301 // in all other situations the creator should be non-NULL 302 303 if ( xCreator.is() ) 304 { 305 sal_Int32 nChildCount = xParentContext->getAccessibleChildCount(); 306 for ( sal_Int32 nChild = 0; ( nChild < nChildCount ) && ( -1 == nRet ); ++nChild ) 307 { 308 Reference< XAccessible > xChild( xParentContext->getAccessibleChild( nChild ) ); 309 if ( xChild.get() == xCreator.get() ) 310 nRet = nChild; 311 } 312 } 313 } 314 } 315 catch( const Exception& ) 316 { 317 OSL_ENSURE( sal_False, "OAccessibleContextHelper::getAccessibleIndexInParent: caught an exception!" ); 318 } 319 320 return nRet; 321 } 322 323 //--------------------------------------------------------------------- getLocale()324 Locale SAL_CALL OAccessibleContextHelper::getLocale( ) throw (IllegalAccessibleComponentStateException, RuntimeException) 325 { 326 // simply ask the parent 327 Reference< XAccessible > xParent = getAccessibleParent(); 328 Reference< XAccessibleContext > xParentContext; 329 if ( xParent.is() ) 330 xParentContext = xParent->getAccessibleContext(); 331 332 if ( !xParentContext.is() ) 333 throw IllegalAccessibleComponentStateException( ::rtl::OUString(), *this ); 334 335 return xParentContext->getLocale(); 336 } 337 338 //--------------------------------------------------------------------- implGetParentContext()339 Reference< XAccessibleContext > OAccessibleContextHelper::implGetParentContext() SAL_THROW( ( RuntimeException ) ) 340 { 341 Reference< XAccessible > xParent = getAccessibleParent(); 342 Reference< XAccessibleContext > xParentContext; 343 if ( xParent.is() ) 344 xParentContext = xParent->getAccessibleContext(); 345 return xParentContext; 346 } 347 348 //......................................................................... 349 } // namespace comphelper 350 //......................................................................... 351 352 353