Files
libreoffice/sw/source/core/access/acccontext.cxx

1494 lines
47 KiB
C++
Raw Normal View History

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
re-base on ALv2 code. Includes: Patches contributed by Oliver-Rainer Wittmann sw34bf06: #i117783# - Writer's implementation of XPagePrintable - apply print settings to new printing routines http://svn.apache.org/viewvc?view=revision&revision=1172115 sw34bf06: #o12311627# use <rtl_random> methods to create unique ids for list styles and list ids http://svn.apache.org/viewvc?view=revision&revision=1172112 sw34bf06 #i114725#,#i115828# - method <SwDoc::ClearDoc()> - clear list structures completely http://svn.apache.org/viewvc?view=revision&revision=1172122 i#118572 - remove ui string and help content regarding usage of Java Mail in Writer's Mail Merge as Java Mail is not used. http://svn.apache.org/viewvc?view=revision&revision=1197035 Patches contributed by Mathias Bauer cws mba34issues01: #i117718#: provide filter name in case storage of medium does not allow to detect one http://svn.apache.org/viewvc?view=revision&revision=1172350 cws mba34issues01: #i117721#: directly provide parameters retrieved from SfxMedium http://svn.apache.org/viewvc?view=revision&revision=1172353 gnumake4 work variously http://svn.apache.org/viewvc?view=revision&revision=1394707 http://svn.apache.org/viewvc?view=revision&revision=1394326 http://svn.apache.org/viewvc?view=revision&revision=1396797 http://svn.apache.org/viewvc?view=revision&revision=1397315 cws mba34issues01: #i117723#: convert assertion into trace http://svn.apache.org/viewvc?view=revision&revision=1172355 cws mba34issues01: #i117699#: keep layout alive until swdoc dies http://svn.apache.org/viewvc?view=revision&revision=1172362 cws mba34issues01: #i117943#: missing color attributes in RTF clipboard http://svn.apache.org/viewvc?view=revision&revision=1172363 Patch contributed by Henning Brinkmann imported patch i#103878 http://svn.apache.org/viewvc?view=revision&revision=1172109 Patches contributed by Michael Stahl sw34bf06: #i117955#: WW8 export: disable storing of section breaks in endnotes http://svn.apache.org/viewvc?view=revision&revision=1172119 Patch contributed by imacat Fixed the Asian language work count. http://svn.apache.org/viewvc?view=revision&revision=1241345 Patch contributed by Pedro Giffuni i#20878 - Add comment with BZ issue for reference. http://svn.apache.org/viewvc?view=revision&revision=1244517 Patch contributed by Andre Fischer Do not add targets for junit tests when junit is disabled. http://svn.apache.org/viewvc?view=revision&revision=1241508 add writerperfect dependency.
2011-03-31 10:05:04 +02:00
/*
* 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 .
*/
2002-02-04 13:10:18 +00:00
#include <vcl/window.hxx>
#include <swtypes.hxx>
2002-02-04 13:10:18 +00:00
#include <com/sun/star/accessibility/XAccessible.hpp>
#include <com/sun/star/accessibility/XAccessibleStateSet.hpp>
#include <com/sun/star/accessibility/AccessibleRole.hpp>
#include <com/sun/star/accessibility/AccessibleStateType.hpp>
#include <com/sun/star/accessibility/AccessibleEventId.hpp>
#include <osl/mutex.hxx>
2002-02-04 13:10:18 +00:00
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
2002-02-05 14:52:06 +00:00
#include <unotools/accessiblestatesethelper.hxx>
#include <unotools/accessiblerelationsethelper.hxx>
#include <viewsh.hxx>
#include <crsrsh.hxx>
#include <fesh.hxx>
#include <wrtsh.hxx>
2002-12-05 13:10:33 +00:00
#include <txtfrm.hxx>
#include <ndtxt.hxx>
#include <pagefrm.hxx>
#include <flyfrm.hxx>
#include <dflyobj.hxx>
2002-12-05 13:10:33 +00:00
#include <pam.hxx>
#include <viewimp.hxx>
2002-02-04 13:10:18 +00:00
#include <accmap.hxx>
#include "accfrmobjslist.hxx"
#include "acccontext.hxx"
2002-05-06 11:26:27 +00:00
#include <svx/AccessibleShape.hxx>
#include <comphelper/accessibleeventnotifier.hxx>
#include <cppuhelper/supportsservice.hxx>
#include "accpara.hxx"
#include <PostItMgr.hxx>
2002-02-04 13:10:18 +00:00
using namespace sw::access;
2002-02-04 13:10:18 +00:00
using namespace ::com::sun::star;
using namespace ::com::sun::star::accessibility;
2002-02-04 13:10:18 +00:00
2002-04-11 13:04:40 +00:00
void SwAccessibleContext::InitStates()
{
m_isShowingState = GetMap() && IsShowing( *(GetMap()) );
SwViewShell *pVSh = GetMap()->GetShell();
m_isEditableState = pVSh && IsEditable( pVSh );
m_isOpaqueState = pVSh && IsOpaque( pVSh );
m_isDefuncState = false;
}
void SwAccessibleContext::SetParent( SwAccessibleContext *pParent )
{
osl::MutexGuard aGuard( m_Mutex );
uno::Reference < XAccessible > xParent( pParent );
m_xWeakParent = xParent;
}
uno::Reference< XAccessible > SwAccessibleContext::GetWeakParent() const
2002-04-05 11:18:25 +00:00
{
osl::MutexGuard aGuard( m_Mutex );
2002-04-05 11:18:25 +00:00
uno::Reference< XAccessible > xParent( m_xWeakParent );
2002-04-05 11:18:25 +00:00
return xParent;
}
vcl::Window *SwAccessibleContext::GetWindow()
2002-02-11 11:51:16 +00:00
{
vcl::Window *pWin = nullptr;
2002-02-11 11:51:16 +00:00
if( GetMap() )
2002-02-11 11:51:16 +00:00
{
const SwViewShell *pVSh = GetMap()->GetShell();
OSL_ENSURE( pVSh, "no view shell" );
2002-04-11 13:04:40 +00:00
if( pVSh )
pWin = pVSh->GetWin();
2002-02-11 11:51:16 +00:00
OSL_ENSURE( pWin, "no window" );
2002-02-11 11:51:16 +00:00
}
2002-02-04 13:10:18 +00:00
2002-04-11 13:04:40 +00:00
return pWin;
2002-02-04 13:10:18 +00:00
}
// get SwViewShell from accessibility map, and cast to cursor shell
SwCursorShell* SwAccessibleContext::GetCursorShell()
{
SwCursorShell* pCursorShell;
SwViewShell* pViewShell = GetMap() ? GetMap()->GetShell() : nullptr;
OSL_ENSURE( pViewShell, "no view shell" );
if( pViewShell && dynamic_cast<const SwCursorShell*>( pViewShell) != nullptr )
pCursorShell = static_cast<SwCursorShell*>( pViewShell );
else
pCursorShell = nullptr;
return pCursorShell;
}
const SwCursorShell* SwAccessibleContext::GetCursorShell() const
{
// just like non-const GetCursorShell
const SwCursorShell* pCursorShell;
const SwViewShell* pViewShell = GetMap() ? GetMap()->GetShell() : nullptr;
OSL_ENSURE( pViewShell, "no view shell" );
if( pViewShell && dynamic_cast<const SwCursorShell*>( pViewShell) != nullptr )
pCursorShell = static_cast<const SwCursorShell*>( pViewShell );
else
pCursorShell = nullptr;
return pCursorShell;
}
enum class Action { NONE, SCROLLED, SCROLLED_WITHIN,
2002-04-11 13:04:40 +00:00
SCROLLED_IN, SCROLLED_OUT };
void SwAccessibleContext::ChildrenScrolled( const SwFrame *pFrame,
const SwRect& rOldVisArea )
2002-02-04 13:10:18 +00:00
{
2002-04-11 13:04:40 +00:00
const SwRect& rNewVisArea = GetVisArea();
const bool bVisibleChildrenOnly = SwAccessibleChild( pFrame ).IsVisibleChildrenOnly();
2002-02-11 11:51:16 +00:00
const SwAccessibleChildSList aList( *pFrame, *(GetMap()) );
SwAccessibleChildSList::const_iterator aIter( aList.begin() );
2002-04-11 13:04:40 +00:00
while( aIter != aList.end() )
{
const SwAccessibleChild& rLower = *aIter;
const SwRect aBox( rLower.GetBox( *(GetMap()) ) );
if( rLower.IsAccessible( GetShell()->IsPreview() ) )
{
Action eAction = Action::NONE;
2002-05-15 12:22:47 +00:00
if( aBox.IsOver( rNewVisArea ) )
2002-04-11 13:04:40 +00:00
{
2002-05-15 12:22:47 +00:00
if( aBox.IsOver( rOldVisArea ) )
2002-04-11 13:04:40 +00:00
{
eAction = Action::SCROLLED_WITHIN;
2002-04-11 13:04:40 +00:00
}
2002-05-15 12:22:47 +00:00
else
2002-04-11 13:04:40 +00:00
{
if ( bVisibleChildrenOnly &&
!rLower.AlwaysIncludeAsChild() )
{
eAction = Action::SCROLLED_IN;
}
2002-04-11 13:04:40 +00:00
else
{
eAction = Action::SCROLLED;
}
2002-04-11 13:04:40 +00:00
}
2002-05-15 12:22:47 +00:00
}
else if( aBox.IsOver( rOldVisArea ) )
{
if ( bVisibleChildrenOnly &&
!rLower.AlwaysIncludeAsChild() )
{
eAction = Action::SCROLLED_OUT;
}
2002-05-15 12:22:47 +00:00
else
{
eAction = Action::SCROLLED;
}
2002-05-15 12:22:47 +00:00
}
else if( !bVisibleChildrenOnly ||
rLower.AlwaysIncludeAsChild() )
2002-05-15 12:22:47 +00:00
{
// This wouldn't be required if the SwAccessibleFrame,
// wouldn't know about the visible area.
eAction = Action::SCROLLED;
2002-05-15 12:22:47 +00:00
}
if( Action::NONE != eAction )
2002-05-15 12:22:47 +00:00
{
if ( rLower.GetSwFrame() )
2002-04-11 13:04:40 +00:00
{
OSL_ENSURE( !rLower.AlwaysIncludeAsChild(),
"<SwAccessibleContext::ChildrenScrolled(..)> - always included child not considered!" );
const SwFrame* pLower( rLower.GetSwFrame() );
2010-10-15 11:44:38 -05:00
::rtl::Reference< SwAccessibleContext > xAccImpl =
GetMap()->GetContextImpl( pLower );
2010-10-15 11:44:38 -05:00
if( xAccImpl.is() )
2002-04-11 13:04:40 +00:00
{
switch( eAction )
{
case Action::SCROLLED:
2002-04-11 13:04:40 +00:00
xAccImpl->Scrolled( rOldVisArea );
break;
case Action::SCROLLED_WITHIN:
2002-04-11 13:04:40 +00:00
xAccImpl->ScrolledWithin( rOldVisArea );
break;
case Action::SCROLLED_IN:
2002-04-11 13:04:40 +00:00
xAccImpl->ScrolledIn();
break;
case Action::SCROLLED_OUT:
2002-04-11 13:04:40 +00:00
xAccImpl->ScrolledOut( rOldVisArea );
break;
case Action::NONE:
break;
2002-04-11 13:04:40 +00:00
}
}
else
{
ChildrenScrolled( pLower, rOldVisArea );
}
}
else if ( rLower.GetDrawObject() )
2002-05-15 12:22:47 +00:00
{
OSL_ENSURE( !rLower.AlwaysIncludeAsChild(),
"<SwAccessibleContext::ChildrenScrolled(..)> - always included child not considered!" );
2010-10-15 11:44:38 -05:00
::rtl::Reference< ::accessibility::AccessibleShape > xAccImpl =
GetMap()->GetContextImpl( rLower.GetDrawObject(),
this );
2010-10-15 11:44:38 -05:00
if( xAccImpl.is() )
2002-05-15 12:22:47 +00:00
{
switch( eAction )
{
case Action::SCROLLED:
case Action::SCROLLED_WITHIN:
xAccImpl->ViewForwarderChanged();
2002-05-15 12:22:47 +00:00
break;
case Action::SCROLLED_IN:
ScrolledInShape( xAccImpl.get() );
2002-05-15 12:22:47 +00:00
break;
case Action::SCROLLED_OUT:
2002-05-15 12:22:47 +00:00
{
xAccImpl->ViewForwarderChanged();
// this DisposeShape call was removed by
// IAccessibility2 implementation
// without giving any reason why
DisposeShape( rLower.GetDrawObject(),
xAccImpl.get() );
2002-05-15 12:22:47 +00:00
}
break;
// coverity[dead_error_begin] - following conditions exist to avoid compiler warning
case Action::NONE:
break;
2002-05-15 12:22:47 +00:00
}
}
}
else if ( rLower.GetWindow() )
{
// nothing to do - as such children are always included as children.
OSL_ENSURE( rLower.AlwaysIncludeAsChild(),
"<SwAccessibleContext::ChildrenScrolled(..)> - not always included child not considered!" );
}
2002-04-11 13:04:40 +00:00
}
}
else if ( rLower.GetSwFrame() &&
( !bVisibleChildrenOnly ||
aBox.IsOver( rOldVisArea ) ||
aBox.IsOver( rNewVisArea ) ) )
{
2002-04-11 13:04:40 +00:00
// There are no unaccessible SdrObjects that need to be notified
ChildrenScrolled( rLower.GetSwFrame(), rOldVisArea );
}
2002-04-11 13:04:40 +00:00
++aIter;
}
}
2002-04-11 13:04:40 +00:00
void SwAccessibleContext::Scrolled( const SwRect& rOldVisArea )
2002-02-04 13:10:18 +00:00
{
2002-04-11 13:04:40 +00:00
SetVisArea( GetMap()->GetVisArea() );
ChildrenScrolled( GetFrame(), rOldVisArea );
bool bIsOldShowingState;
bool bIsNewShowingState = IsShowing( *(GetMap()) );
2002-04-11 13:04:40 +00:00
{
osl::MutexGuard aGuard( m_Mutex );
bIsOldShowingState = m_isShowingState;
m_isShowingState = bIsNewShowingState;
}
2002-04-11 13:04:40 +00:00
if( bIsOldShowingState != bIsNewShowingState )
FireStateChangedEvent( AccessibleStateType::SHOWING,
bIsNewShowingState );
}
2002-04-11 13:04:40 +00:00
void SwAccessibleContext::ScrolledWithin( const SwRect& rOldVisArea )
{
2002-04-11 13:04:40 +00:00
SetVisArea( GetMap()->GetVisArea() );
ChildrenScrolled( GetFrame(), rOldVisArea );
2002-04-11 13:04:40 +00:00
FireVisibleDataEvent();
}
2002-04-11 13:04:40 +00:00
void SwAccessibleContext::ScrolledIn()
{
2002-04-11 13:04:40 +00:00
// This accessible should be freshly created, because it
// was not visible before. Therefore, its visible area must already
2002-04-11 13:04:40 +00:00
// reflect the scrolling.
OSL_ENSURE( GetVisArea() == GetMap()->GetVisArea(),
"Visible area of child is wrong. Did it exist already?" );
2002-04-11 13:04:40 +00:00
// Send child event at parent. That's all we have to do here.
const SwFrame* pParent = GetParent();
2010-10-15 11:44:38 -05:00
::rtl::Reference< SwAccessibleContext > xParentImpl(
GetMap()->GetContextImpl( pParent, false ) );
uno::Reference < XAccessibleContext > xThis( this );
2010-10-15 11:44:38 -05:00
if( xParentImpl.is() )
2002-02-11 11:51:16 +00:00
{
2010-10-15 11:44:38 -05:00
SetParent( xParentImpl.get() );
2002-02-11 11:51:16 +00:00
AccessibleEventObject aEvent;
aEvent.EventId = AccessibleEventId::CHILD;
2002-04-11 13:04:40 +00:00
aEvent.NewValue <<= xThis;
2002-04-11 13:04:40 +00:00
xParentImpl->FireAccessibleEvent( aEvent );
if( HasCursor() )
{
vcl::Window *pWin = GetWindow();
if( pWin && pWin->HasFocus() )
{
FireStateChangedEvent( AccessibleStateType::FOCUSED, true );
}
}
}
2002-02-11 11:51:16 +00:00
}
2002-04-11 13:04:40 +00:00
void SwAccessibleContext::ScrolledOut( const SwRect& rOldVisArea )
2002-02-11 11:51:16 +00:00
{
2002-04-11 13:04:40 +00:00
SetVisArea( GetMap()->GetVisArea() );
// First of all, update the children. That's required to dispose
// all children that are existing only if they are visible. They
2014-04-11 08:23:23 +02:00
// are not disposed by the recursive Dispose call that follows later on,
2002-04-11 13:04:40 +00:00
// because this call will only dispose children that are in the
// new visible area. The children we want to dispose however are in the
// old visible area all.
ChildrenScrolled( GetFrame(), rOldVisArea );
2002-04-11 13:04:40 +00:00
// Broadcast a state changed event for the showing state.
// It might be that the child is freshly created just to send
// the child event. In this case no listener will exist.
FireStateChangedEvent( AccessibleStateType::SHOWING, false );
// this Dispose call was removed by IAccessibility2 implementation
// without giving any reason why - without it we get stale
// entries in SwAccessibleMap::mpFrameMap.
Dispose(true);
2002-04-11 13:04:40 +00:00
}
2011-01-30 04:19:53 +09:00
// #i27301# - use new type definition for <_nStates>
void SwAccessibleContext::InvalidateChildrenStates( const SwFrame* _pFrame,
AccessibleStates _nStates )
2002-04-11 13:04:40 +00:00
{
const SwAccessibleChildSList aVisList( GetVisArea(), *_pFrame, *(GetMap()) );
2002-04-11 13:04:40 +00:00
SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
2002-04-11 13:04:40 +00:00
while( aIter != aVisList.end() )
2002-02-11 11:51:16 +00:00
{
const SwAccessibleChild& rLower = *aIter;
const SwFrame* pLower = rLower.GetSwFrame();
2002-04-11 13:04:40 +00:00
if( pLower )
{
2010-10-15 11:44:38 -05:00
::rtl::Reference< SwAccessibleContext > xAccImpl;
if( rLower.IsAccessible( GetShell()->IsPreview() ) )
xAccImpl = GetMap()->GetContextImpl( pLower, false );
2010-10-15 11:44:38 -05:00
if( xAccImpl.is() )
xAccImpl->InvalidateStates( _nStates );
2002-04-11 13:04:40 +00:00
else
InvalidateChildrenStates( pLower, _nStates );
}
else if ( rLower.GetDrawObject() )
2002-04-11 13:04:40 +00:00
{
// TODO: SdrObjects
}
else if ( rLower.GetWindow() )
{
// nothing to do ?
}
2002-04-11 13:04:40 +00:00
++aIter;
2002-02-11 11:51:16 +00:00
}
2002-02-04 13:10:18 +00:00
}
void SwAccessibleContext::DisposeChildren(const SwFrame *pFrame,
bool bRecursive,
bool bCanSkipInvisible)
2002-02-11 11:51:16 +00:00
{
const SwAccessibleChildSList aVisList( GetVisArea(), *pFrame, *(GetMap()) );
SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
2002-04-11 13:04:40 +00:00
while( aIter != aVisList.end() )
2002-02-11 11:51:16 +00:00
{
const SwAccessibleChild& rLower = *aIter;
const SwFrame* pLower = rLower.GetSwFrame();
2002-04-11 13:04:40 +00:00
if( pLower )
2002-02-11 11:51:16 +00:00
{
2010-10-15 11:44:38 -05:00
::rtl::Reference< SwAccessibleContext > xAccImpl;
if( rLower.IsAccessible( GetShell()->IsPreview() ) )
xAccImpl = GetMap()->GetContextImpl( pLower, false );
2010-10-15 11:44:38 -05:00
if( xAccImpl.is() )
2002-04-11 13:04:40 +00:00
xAccImpl->Dispose( bRecursive );
tdf#58624 sw: fix ~SwAccessibleContext() use-after-free race As seen in JunitTest_toolkit_unoapi_1: Method doAccessibleAction() finished with state OK LOG> doAccessibleAction(): COMPLETED.OK debug:27272:12: -SwAccessibleParagraph mutexwait 0x3fd9f50 debug:27272:9: SwAccessibleContext::Dispose 0x3872620 11SwRootFrame debug:27272:9: SwAccessibleContext::DisposeChildren 0x4047c80 0x386d600 11SwPageFrame debug:27272:9: SwAccessibleContext::DisposeChildren xAcc 0 debug:27272:9: SwAccessibleContext::DisposeChildren 0x4047c80 0x386cef0 11SwBodyFrame debug:27272:9: SwAccessibleContext::DisposeChildren xAcc 0 debug:27272:9: SwAccessibleContext::DisposeChildren 0x4047c80 0x3878fe0 11SwTextFrame debug:27272:9: SwAccessibleContext::DisposeChildren xAcc 0 debug:27272:9: SwAccessibleMap::RemoveContext erase 0x3872620 debug:27272:9: ~SwAccessibleMap: frame entry 0x3878fe0 debug:27272:9: ~SwAccessibleMap: mpFrameMap 0x3eb64a0 debug:27272:9: ~SwAccessibleMap: mpShapeMap 0 soffice.bin: sw/source/core/access/accmap.cxx:1726: virtual SwAccessibleMap::~SwAccessibleMap(): Assertion `(!mpFrameMap || mpFrameMap->empty()) && "Frame map should be empty after disposing the root frame"' The problem here is that thread 12 is blocked on SolarMutex in ~SwAccessibleParagraph(), while thread 9 is in ~SwAccessibleMap(). This means that in SwAccessibleContext::DisposeChildren(), the WeakReference to the SwAccessibleParagraph cannot create a uno::Reference because its reference count is 0, so SwAccessibleContext::Dispose() is not called on it and it remains in the SwAccessibleMap::mpFrameMap. This triggers the assert and later on ~SwAccessibleContext() would access the deleted SwAccessibleMap and crash. To fix this, introduce a weak reference from SwAccessibleContext to SwAccessibleMap; use a std::weak_ptr because that is not derived from OWeakObject. The weak_ptr is only used in the dtor ~SwAccessibleContext(); as long as the ref-count of SwAccessibleContext is > 0 it is guaranteed that the SwAccessibleContext::m_pMap is either null or valid as the recursive Dispose() will work fine. It is possible that additional temporary owning references could delay the destruction of SwAccessibleMap, and the order of destruction of Writer documents is very fragile, so rely on the SolarMutex lock to prevent that; the only shared_ptr that owns SwAccessibleMap while SolarMutex is not locked is the one in SwViewShellImp. (An alternative fix would be to represent the 3 lifecycle stages of SwAccessibleContext by adding a C++-pointer to the SwAccessibleMap::mpFrameMap, so that DisposeChildren() can, if the WeakReference is no longer valid due to ref-count 0, use the pointer and clear SwAccessibleContext::m_pMap - this and the corresponding call to SwAccessibleMap::RemoveContext() from ~SwAccessibleContext() under a mutex that is shared_ptr-owned by SwAccessibleMap and all SwAccessibleContext.) Change-Id: If2b44c79189e3b3d276491a5c57d5404bb2be71a
2017-03-24 13:04:32 +01:00
else
{
// it's possible that the xAccImpl *does* exist with a
// ref-count of 0 and blocked in its dtor in another thread -
// this call here could be from SwAccessibleMap dtor so
// remove it from any maps now!
GetMap()->RemoveContext(pLower);
// in this case the context will check with a weak_ptr
// that the map is still alive so it's not necessary
// to clear its m_pMap here.
if (bRecursive)
{
DisposeChildren(pLower, bRecursive, bCanSkipInvisible);
}
}
2002-02-11 11:51:16 +00:00
}
else if ( rLower.GetDrawObject() )
2002-04-11 13:04:40 +00:00
{
2010-10-15 11:44:38 -05:00
::rtl::Reference< ::accessibility::AccessibleShape > xAccImpl(
GetMap()->GetContextImpl( rLower.GetDrawObject(),
this, false ) );
2010-10-15 11:44:38 -05:00
if( xAccImpl.is() )
DisposeShape( rLower.GetDrawObject(), xAccImpl.get() );
}
else if ( rLower.GetWindow() )
{
DisposeChild(rLower, false, bCanSkipInvisible);
2002-04-11 13:04:40 +00:00
}
++aIter;
2002-02-11 11:51:16 +00:00
}
}
void SwAccessibleContext::InvalidateContent_( bool )
{
}
void SwAccessibleContext::InvalidateCursorPos_()
{
}
void SwAccessibleContext::InvalidateFocus_()
2002-05-16 07:22:32 +00:00
{
}
void SwAccessibleContext::FireAccessibleEvent( AccessibleEventObject& rEvent )
2002-02-04 13:10:18 +00:00
{
OSL_ENSURE( GetFrame(), "fire event for disposed frame?" );
if( !GetFrame() )
2002-04-11 13:04:40 +00:00
return;
if( !rEvent.Source.is() )
{
uno::Reference < XAccessibleContext > xThis( this );
rEvent.Source = xThis;
}
2002-02-04 13:10:18 +00:00
if (m_nClientId)
comphelper::AccessibleEventNotifier::addEvent( m_nClientId, rEvent );
}
2002-04-11 13:04:40 +00:00
void SwAccessibleContext::FireVisibleDataEvent()
2002-03-21 11:50:31 +00:00
{
2002-04-11 13:04:40 +00:00
AccessibleEventObject aEvent;
aEvent.EventId = AccessibleEventId::VISIBLE_DATA_CHANGED;
2002-04-11 13:04:40 +00:00
FireAccessibleEvent( aEvent );
2002-03-21 11:50:31 +00:00
}
2002-04-11 13:04:40 +00:00
void SwAccessibleContext::FireStateChangedEvent( sal_Int16 nState,
bool bNewState )
{
2002-04-11 13:04:40 +00:00
AccessibleEventObject aEvent;
aEvent.EventId = AccessibleEventId::STATE_CHANGED;
2002-04-11 13:04:40 +00:00
if( bNewState )
aEvent.NewValue <<= nState;
else
aEvent.OldValue <<= nState;
FireAccessibleEvent( aEvent );
}
2002-04-11 13:04:40 +00:00
void SwAccessibleContext::GetStates(
::utl::AccessibleStateSetHelper& rStateSet )
{
SolarMutexGuard aGuard;
2002-04-11 13:04:40 +00:00
// SHOWING
if (m_isShowingState)
2002-04-11 13:04:40 +00:00
rStateSet.AddState( AccessibleStateType::SHOWING );
// EDITABLE
if (m_isEditableState)
//Set editable state to graphic and other object when the document is editable
{
2002-04-11 13:04:40 +00:00
rStateSet.AddState( AccessibleStateType::EDITABLE );
rStateSet.AddState( AccessibleStateType::RESIZABLE );
rStateSet.AddState( AccessibleStateType::MOVEABLE );
}
2002-04-11 13:04:40 +00:00
// ENABLED
rStateSet.AddState( AccessibleStateType::ENABLED );
// OPAQUE
if (m_isOpaqueState)
2002-04-11 13:04:40 +00:00
rStateSet.AddState( AccessibleStateType::OPAQUE );
// VISIBLE
rStateSet.AddState( AccessibleStateType::VISIBLE );
if (m_isDefuncState)
2002-04-11 13:04:40 +00:00
rStateSet.AddState( AccessibleStateType::DEFUNC );
}
bool SwAccessibleContext::IsEditableState()
{
bool bRet;
{
osl::MutexGuard aGuard( m_Mutex );
bRet = m_isEditableState;
}
return bRet;
}
void SwAccessibleContext::ThrowIfDisposed()
{
if (!(GetFrame() && GetMap()))
{
throw lang::DisposedException("object is nonfunctional",
static_cast<cppu::OWeakObject*>(this));
}
}
tdf#58624 sw: fix ~SwAccessibleContext() use-after-free race As seen in JunitTest_toolkit_unoapi_1: Method doAccessibleAction() finished with state OK LOG> doAccessibleAction(): COMPLETED.OK debug:27272:12: -SwAccessibleParagraph mutexwait 0x3fd9f50 debug:27272:9: SwAccessibleContext::Dispose 0x3872620 11SwRootFrame debug:27272:9: SwAccessibleContext::DisposeChildren 0x4047c80 0x386d600 11SwPageFrame debug:27272:9: SwAccessibleContext::DisposeChildren xAcc 0 debug:27272:9: SwAccessibleContext::DisposeChildren 0x4047c80 0x386cef0 11SwBodyFrame debug:27272:9: SwAccessibleContext::DisposeChildren xAcc 0 debug:27272:9: SwAccessibleContext::DisposeChildren 0x4047c80 0x3878fe0 11SwTextFrame debug:27272:9: SwAccessibleContext::DisposeChildren xAcc 0 debug:27272:9: SwAccessibleMap::RemoveContext erase 0x3872620 debug:27272:9: ~SwAccessibleMap: frame entry 0x3878fe0 debug:27272:9: ~SwAccessibleMap: mpFrameMap 0x3eb64a0 debug:27272:9: ~SwAccessibleMap: mpShapeMap 0 soffice.bin: sw/source/core/access/accmap.cxx:1726: virtual SwAccessibleMap::~SwAccessibleMap(): Assertion `(!mpFrameMap || mpFrameMap->empty()) && "Frame map should be empty after disposing the root frame"' The problem here is that thread 12 is blocked on SolarMutex in ~SwAccessibleParagraph(), while thread 9 is in ~SwAccessibleMap(). This means that in SwAccessibleContext::DisposeChildren(), the WeakReference to the SwAccessibleParagraph cannot create a uno::Reference because its reference count is 0, so SwAccessibleContext::Dispose() is not called on it and it remains in the SwAccessibleMap::mpFrameMap. This triggers the assert and later on ~SwAccessibleContext() would access the deleted SwAccessibleMap and crash. To fix this, introduce a weak reference from SwAccessibleContext to SwAccessibleMap; use a std::weak_ptr because that is not derived from OWeakObject. The weak_ptr is only used in the dtor ~SwAccessibleContext(); as long as the ref-count of SwAccessibleContext is > 0 it is guaranteed that the SwAccessibleContext::m_pMap is either null or valid as the recursive Dispose() will work fine. It is possible that additional temporary owning references could delay the destruction of SwAccessibleMap, and the order of destruction of Writer documents is very fragile, so rely on the SolarMutex lock to prevent that; the only shared_ptr that owns SwAccessibleMap while SolarMutex is not locked is the one in SwViewShellImp. (An alternative fix would be to represent the 3 lifecycle stages of SwAccessibleContext by adding a C++-pointer to the SwAccessibleMap::mpFrameMap, so that DisposeChildren() can, if the WeakReference is no longer valid due to ref-count 0, use the pointer and clear SwAccessibleContext::m_pMap - this and the corresponding call to SwAccessibleMap::RemoveContext() from ~SwAccessibleContext() under a mutex that is shared_ptr-owned by SwAccessibleMap and all SwAccessibleContext.) Change-Id: If2b44c79189e3b3d276491a5c57d5404bb2be71a
2017-03-24 13:04:32 +01:00
SwAccessibleContext::SwAccessibleContext(std::shared_ptr<SwAccessibleMap> const& pMap,
sal_Int16 const nRole,
const SwFrame *pF )
: SwAccessibleFrame( pMap->GetVisArea().SVRect(), pF,
pMap->GetShell()->IsPreview() )
tdf#58624 sw: fix ~SwAccessibleContext() use-after-free race As seen in JunitTest_toolkit_unoapi_1: Method doAccessibleAction() finished with state OK LOG> doAccessibleAction(): COMPLETED.OK debug:27272:12: -SwAccessibleParagraph mutexwait 0x3fd9f50 debug:27272:9: SwAccessibleContext::Dispose 0x3872620 11SwRootFrame debug:27272:9: SwAccessibleContext::DisposeChildren 0x4047c80 0x386d600 11SwPageFrame debug:27272:9: SwAccessibleContext::DisposeChildren xAcc 0 debug:27272:9: SwAccessibleContext::DisposeChildren 0x4047c80 0x386cef0 11SwBodyFrame debug:27272:9: SwAccessibleContext::DisposeChildren xAcc 0 debug:27272:9: SwAccessibleContext::DisposeChildren 0x4047c80 0x3878fe0 11SwTextFrame debug:27272:9: SwAccessibleContext::DisposeChildren xAcc 0 debug:27272:9: SwAccessibleMap::RemoveContext erase 0x3872620 debug:27272:9: ~SwAccessibleMap: frame entry 0x3878fe0 debug:27272:9: ~SwAccessibleMap: mpFrameMap 0x3eb64a0 debug:27272:9: ~SwAccessibleMap: mpShapeMap 0 soffice.bin: sw/source/core/access/accmap.cxx:1726: virtual SwAccessibleMap::~SwAccessibleMap(): Assertion `(!mpFrameMap || mpFrameMap->empty()) && "Frame map should be empty after disposing the root frame"' The problem here is that thread 12 is blocked on SolarMutex in ~SwAccessibleParagraph(), while thread 9 is in ~SwAccessibleMap(). This means that in SwAccessibleContext::DisposeChildren(), the WeakReference to the SwAccessibleParagraph cannot create a uno::Reference because its reference count is 0, so SwAccessibleContext::Dispose() is not called on it and it remains in the SwAccessibleMap::mpFrameMap. This triggers the assert and later on ~SwAccessibleContext() would access the deleted SwAccessibleMap and crash. To fix this, introduce a weak reference from SwAccessibleContext to SwAccessibleMap; use a std::weak_ptr because that is not derived from OWeakObject. The weak_ptr is only used in the dtor ~SwAccessibleContext(); as long as the ref-count of SwAccessibleContext is > 0 it is guaranteed that the SwAccessibleContext::m_pMap is either null or valid as the recursive Dispose() will work fine. It is possible that additional temporary owning references could delay the destruction of SwAccessibleMap, and the order of destruction of Writer documents is very fragile, so rely on the SolarMutex lock to prevent that; the only shared_ptr that owns SwAccessibleMap while SolarMutex is not locked is the one in SwViewShellImp. (An alternative fix would be to represent the 3 lifecycle stages of SwAccessibleContext by adding a C++-pointer to the SwAccessibleMap::mpFrameMap, so that DisposeChildren() can, if the WeakReference is no longer valid due to ref-count 0, use the pointer and clear SwAccessibleContext::m_pMap - this and the corresponding call to SwAccessibleMap::RemoveContext() from ~SwAccessibleContext() under a mutex that is shared_ptr-owned by SwAccessibleMap and all SwAccessibleContext.) Change-Id: If2b44c79189e3b3d276491a5c57d5404bb2be71a
2017-03-24 13:04:32 +01:00
, m_pMap(pMap.get())
, m_wMap(pMap)
, m_nClientId(0)
, m_nRole(nRole)
, m_isDisposing( false )
, m_isRegisteredAtAccessibleMap( true )
, m_isSelectedInDoc(false)
2002-02-04 13:10:18 +00:00
{
InitStates();
2002-02-04 13:10:18 +00:00
}
SwAccessibleContext::~SwAccessibleContext()
{
tdf#58624 sw: fix ~SwAccessibleContext() use-after-free race As seen in JunitTest_toolkit_unoapi_1: Method doAccessibleAction() finished with state OK LOG> doAccessibleAction(): COMPLETED.OK debug:27272:12: -SwAccessibleParagraph mutexwait 0x3fd9f50 debug:27272:9: SwAccessibleContext::Dispose 0x3872620 11SwRootFrame debug:27272:9: SwAccessibleContext::DisposeChildren 0x4047c80 0x386d600 11SwPageFrame debug:27272:9: SwAccessibleContext::DisposeChildren xAcc 0 debug:27272:9: SwAccessibleContext::DisposeChildren 0x4047c80 0x386cef0 11SwBodyFrame debug:27272:9: SwAccessibleContext::DisposeChildren xAcc 0 debug:27272:9: SwAccessibleContext::DisposeChildren 0x4047c80 0x3878fe0 11SwTextFrame debug:27272:9: SwAccessibleContext::DisposeChildren xAcc 0 debug:27272:9: SwAccessibleMap::RemoveContext erase 0x3872620 debug:27272:9: ~SwAccessibleMap: frame entry 0x3878fe0 debug:27272:9: ~SwAccessibleMap: mpFrameMap 0x3eb64a0 debug:27272:9: ~SwAccessibleMap: mpShapeMap 0 soffice.bin: sw/source/core/access/accmap.cxx:1726: virtual SwAccessibleMap::~SwAccessibleMap(): Assertion `(!mpFrameMap || mpFrameMap->empty()) && "Frame map should be empty after disposing the root frame"' The problem here is that thread 12 is blocked on SolarMutex in ~SwAccessibleParagraph(), while thread 9 is in ~SwAccessibleMap(). This means that in SwAccessibleContext::DisposeChildren(), the WeakReference to the SwAccessibleParagraph cannot create a uno::Reference because its reference count is 0, so SwAccessibleContext::Dispose() is not called on it and it remains in the SwAccessibleMap::mpFrameMap. This triggers the assert and later on ~SwAccessibleContext() would access the deleted SwAccessibleMap and crash. To fix this, introduce a weak reference from SwAccessibleContext to SwAccessibleMap; use a std::weak_ptr because that is not derived from OWeakObject. The weak_ptr is only used in the dtor ~SwAccessibleContext(); as long as the ref-count of SwAccessibleContext is > 0 it is guaranteed that the SwAccessibleContext::m_pMap is either null or valid as the recursive Dispose() will work fine. It is possible that additional temporary owning references could delay the destruction of SwAccessibleMap, and the order of destruction of Writer documents is very fragile, so rely on the SolarMutex lock to prevent that; the only shared_ptr that owns SwAccessibleMap while SolarMutex is not locked is the one in SwViewShellImp. (An alternative fix would be to represent the 3 lifecycle stages of SwAccessibleContext by adding a C++-pointer to the SwAccessibleMap::mpFrameMap, so that DisposeChildren() can, if the WeakReference is no longer valid due to ref-count 0, use the pointer and clear SwAccessibleContext::m_pMap - this and the corresponding call to SwAccessibleMap::RemoveContext() from ~SwAccessibleContext() under a mutex that is shared_ptr-owned by SwAccessibleMap and all SwAccessibleContext.) Change-Id: If2b44c79189e3b3d276491a5c57d5404bb2be71a
2017-03-24 13:04:32 +01:00
// must have for 2 reasons: 2. as long as this thread has SolarMutex
// another thread cannot destroy the SwAccessibleMap so our temporary
// taking a hard ref to SwAccessibleMap won't delay its destruction
SolarMutexGuard aGuard;
tdf#58624 sw: fix ~SwAccessibleContext() use-after-free race As seen in JunitTest_toolkit_unoapi_1: Method doAccessibleAction() finished with state OK LOG> doAccessibleAction(): COMPLETED.OK debug:27272:12: -SwAccessibleParagraph mutexwait 0x3fd9f50 debug:27272:9: SwAccessibleContext::Dispose 0x3872620 11SwRootFrame debug:27272:9: SwAccessibleContext::DisposeChildren 0x4047c80 0x386d600 11SwPageFrame debug:27272:9: SwAccessibleContext::DisposeChildren xAcc 0 debug:27272:9: SwAccessibleContext::DisposeChildren 0x4047c80 0x386cef0 11SwBodyFrame debug:27272:9: SwAccessibleContext::DisposeChildren xAcc 0 debug:27272:9: SwAccessibleContext::DisposeChildren 0x4047c80 0x3878fe0 11SwTextFrame debug:27272:9: SwAccessibleContext::DisposeChildren xAcc 0 debug:27272:9: SwAccessibleMap::RemoveContext erase 0x3872620 debug:27272:9: ~SwAccessibleMap: frame entry 0x3878fe0 debug:27272:9: ~SwAccessibleMap: mpFrameMap 0x3eb64a0 debug:27272:9: ~SwAccessibleMap: mpShapeMap 0 soffice.bin: sw/source/core/access/accmap.cxx:1726: virtual SwAccessibleMap::~SwAccessibleMap(): Assertion `(!mpFrameMap || mpFrameMap->empty()) && "Frame map should be empty after disposing the root frame"' The problem here is that thread 12 is blocked on SolarMutex in ~SwAccessibleParagraph(), while thread 9 is in ~SwAccessibleMap(). This means that in SwAccessibleContext::DisposeChildren(), the WeakReference to the SwAccessibleParagraph cannot create a uno::Reference because its reference count is 0, so SwAccessibleContext::Dispose() is not called on it and it remains in the SwAccessibleMap::mpFrameMap. This triggers the assert and later on ~SwAccessibleContext() would access the deleted SwAccessibleMap and crash. To fix this, introduce a weak reference from SwAccessibleContext to SwAccessibleMap; use a std::weak_ptr because that is not derived from OWeakObject. The weak_ptr is only used in the dtor ~SwAccessibleContext(); as long as the ref-count of SwAccessibleContext is > 0 it is guaranteed that the SwAccessibleContext::m_pMap is either null or valid as the recursive Dispose() will work fine. It is possible that additional temporary owning references could delay the destruction of SwAccessibleMap, and the order of destruction of Writer documents is very fragile, so rely on the SolarMutex lock to prevent that; the only shared_ptr that owns SwAccessibleMap while SolarMutex is not locked is the one in SwViewShellImp. (An alternative fix would be to represent the 3 lifecycle stages of SwAccessibleContext by adding a C++-pointer to the SwAccessibleMap::mpFrameMap, so that DisposeChildren() can, if the WeakReference is no longer valid due to ref-count 0, use the pointer and clear SwAccessibleContext::m_pMap - this and the corresponding call to SwAccessibleMap::RemoveContext() from ~SwAccessibleContext() under a mutex that is shared_ptr-owned by SwAccessibleMap and all SwAccessibleContext.) Change-Id: If2b44c79189e3b3d276491a5c57d5404bb2be71a
2017-03-24 13:04:32 +01:00
// must check with weak_ptr that m_pMap is still alive
std::shared_ptr<SwAccessibleMap> pMap(m_wMap.lock());
if (m_isRegisteredAtAccessibleMap && GetFrame() && pMap)
{
pMap->RemoveContext( GetFrame() );
}
2002-02-04 13:10:18 +00:00
}
uno::Reference< XAccessibleContext > SAL_CALL
SwAccessibleContext::getAccessibleContext()
2002-02-04 13:10:18 +00:00
{
uno::Reference < XAccessibleContext > xRet( this );
2002-02-04 13:10:18 +00:00
return xRet;
}
sal_Int32 SAL_CALL SwAccessibleContext::getAccessibleChildCount()
2002-02-04 13:10:18 +00:00
{
SolarMutexGuard aGuard;
2002-02-04 13:10:18 +00:00
ThrowIfDisposed();
return m_isDisposing ? 0 : GetChildCount( *(GetMap()) );
2002-02-04 13:10:18 +00:00
}
uno::Reference< XAccessible> SAL_CALL
SwAccessibleContext::getAccessibleChild( sal_Int32 nIndex )
2002-02-04 13:10:18 +00:00
{
SolarMutexGuard aGuard;
2002-02-04 13:10:18 +00:00
ThrowIfDisposed();
2002-02-04 13:10:18 +00:00
const SwAccessibleChild aChild( GetChild( *(GetMap()), nIndex ) );
2002-04-05 11:18:25 +00:00
if( !aChild.IsValid() )
2002-02-04 13:10:18 +00:00
{
uno::Reference < XAccessibleContext > xThis( this );
lang::IndexOutOfBoundsException aExcept(
"index out of bounds",
xThis );
2002-02-04 13:10:18 +00:00
throw aExcept;
}
uno::Reference< XAccessible > xChild;
if( aChild.GetSwFrame() )
2002-04-05 11:18:25 +00:00
{
2010-10-15 11:44:38 -05:00
::rtl::Reference < SwAccessibleContext > xChildImpl(
GetMap()->GetContextImpl( aChild.GetSwFrame(), !m_isDisposing ) );
2010-10-15 11:44:38 -05:00
if( xChildImpl.is() )
2002-04-05 11:18:25 +00:00
{
xChildImpl->SetParent( this );
2010-10-15 11:44:38 -05:00
xChild = xChildImpl.get();
2002-04-05 11:18:25 +00:00
}
}
else if ( aChild.GetDrawObject() )
2002-04-05 11:18:25 +00:00
{
2010-10-15 11:44:38 -05:00
::rtl::Reference < ::accessibility::AccessibleShape > xChildImpl(
GetMap()->GetContextImpl( aChild.GetDrawObject(),
this, !m_isDisposing) );
2010-10-15 11:44:38 -05:00
if( xChildImpl.is() )
xChild = xChildImpl.get();
2002-04-05 11:18:25 +00:00
}
else if ( aChild.GetWindow() )
{
xChild = aChild.GetWindow()->GetAccessible();
}
2002-02-04 13:10:18 +00:00
2002-02-11 11:51:16 +00:00
return xChild;
2002-02-04 13:10:18 +00:00
}
uno::Reference< XAccessible> SAL_CALL SwAccessibleContext::getAccessibleParentImpl()
2002-02-04 13:10:18 +00:00
{
SolarMutexGuard aGuard;
2002-02-04 13:10:18 +00:00
const SwFrame *pUpper = GetParent();
OSL_ENSURE( pUpper != nullptr || m_isDisposing, "no upper found" );
2002-02-04 13:10:18 +00:00
uno::Reference< XAccessible > xAcc;
2002-02-04 13:10:18 +00:00
if( pUpper )
xAcc = GetMap()->GetContext( pUpper, !m_isDisposing );
2002-02-04 13:10:18 +00:00
OSL_ENSURE( xAcc.is() || m_isDisposing, "no parent found" );
2002-02-04 13:10:18 +00:00
2002-02-11 11:51:16 +00:00
// Remember the parent as weak ref.
{
osl::MutexGuard aWeakParentGuard( m_Mutex );
m_xWeakParent = xAcc;
}
2002-02-11 11:51:16 +00:00
2002-02-04 13:10:18 +00:00
return xAcc;
}
uno::Reference< XAccessible> SAL_CALL SwAccessibleContext::getAccessibleParent()
{
SolarMutexGuard aGuard;
ThrowIfDisposed();
return getAccessibleParentImpl();
}
sal_Int32 SAL_CALL SwAccessibleContext::getAccessibleIndexInParent()
2002-02-04 13:10:18 +00:00
{
SolarMutexGuard aGuard;
2002-02-04 13:10:18 +00:00
ThrowIfDisposed();
2002-02-04 13:10:18 +00:00
const SwFrame *pUpper = GetParent();
OSL_ENSURE( pUpper != nullptr || m_isDisposing, "no upper found" );
2002-02-04 13:10:18 +00:00
sal_Int32 nIndex = -1;
if( pUpper )
{
2010-10-15 11:44:38 -05:00
::rtl::Reference < SwAccessibleContext > xAccImpl(
GetMap()->GetContextImpl(pUpper, !m_isDisposing) );
OSL_ENSURE( xAccImpl.is() || m_isDisposing, "no parent found" );
2010-10-15 11:44:38 -05:00
if( xAccImpl.is() )
nIndex = xAccImpl->GetChildIndex( *(GetMap()), SwAccessibleChild(GetFrame()) );
2002-02-04 13:10:18 +00:00
}
return nIndex;
}
sal_Int16 SAL_CALL SwAccessibleContext::getAccessibleRole()
2002-02-04 13:10:18 +00:00
{
return m_nRole;
2002-02-04 13:10:18 +00:00
}
OUString SAL_CALL SwAccessibleContext::getAccessibleName()
2002-02-04 13:10:18 +00:00
{
return m_sName;
2002-02-04 13:10:18 +00:00
}
uno::Reference< XAccessibleRelationSet> SAL_CALL
SwAccessibleContext::getAccessibleRelationSet()
2002-02-04 13:10:18 +00:00
{
// by default there are no relations
uno::Reference< XAccessibleRelationSet> xRet( new utl::AccessibleRelationSetHelper() );
2002-02-04 13:10:18 +00:00
return xRet;
}
uno::Reference<XAccessibleStateSet> SAL_CALL
SwAccessibleContext::getAccessibleStateSet()
2002-02-04 13:10:18 +00:00
{
SolarMutexGuard aGuard;
2002-05-03 11:34:00 +00:00
ThrowIfDisposed();
2002-05-03 11:34:00 +00:00
2002-02-05 14:52:06 +00:00
::utl::AccessibleStateSetHelper *pStateSet =
new ::utl::AccessibleStateSetHelper;
if (m_isSelectedInDoc)
pStateSet->AddState( AccessibleStateType::SELECTED );
uno::Reference<XAccessibleStateSet> xStateSet( pStateSet );
GetStates( *pStateSet );
2002-02-05 14:52:06 +00:00
return xStateSet;
2002-02-04 13:10:18 +00:00
}
lang::Locale SAL_CALL SwAccessibleContext::getLocale()
2002-02-04 13:10:18 +00:00
{
SolarMutexGuard aGuard;
2002-02-04 13:10:18 +00:00
lang::Locale aLoc( Application::GetSettings().GetLanguageTag().getLocale() );
2002-02-04 13:10:18 +00:00
return aLoc;
}
void SAL_CALL SwAccessibleContext::addAccessibleEventListener(
const uno::Reference< XAccessibleEventListener >& xListener )
{
if (xListener.is())
{
SolarMutexGuard aGuard;
if (!m_nClientId)
m_nClientId = comphelper::AccessibleEventNotifier::registerClient( );
comphelper::AccessibleEventNotifier::addEventListener( m_nClientId, xListener );
}
}
void SAL_CALL SwAccessibleContext::removeAccessibleEventListener(
const uno::Reference< XAccessibleEventListener >& xListener )
{
if (xListener.is() && m_nClientId)
{
SolarMutexGuard aGuard;
sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( m_nClientId, xListener );
if ( !nListenerCount )
{
// no listeners anymore
// -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
// and at least to us not firing any events anymore, in case somebody calls
// NotifyAccessibleEvent, again
comphelper::AccessibleEventNotifier::revokeClient( m_nClientId );
m_nClientId = 0;
}
}
2002-02-04 13:10:18 +00:00
}
static bool lcl_PointInRectangle(const awt::Point & aPoint,
const awt::Rectangle & aRect)
2002-02-04 13:10:18 +00:00
{
long nDiffX = aPoint.X - aRect.X;
long nDiffY = aPoint.Y - aRect.Y;
2002-02-04 13:10:18 +00:00
return
nDiffX >= 0 && nDiffX < aRect.Width && nDiffY >= 0 &&
nDiffY < aRect.Height;
2002-02-04 13:10:18 +00:00
}
2002-02-04 13:10:18 +00:00
sal_Bool SAL_CALL SwAccessibleContext::containsPoint(
const awt::Point& aPoint )
{
awt::Rectangle aPixBounds = getBoundsImpl(true);
aPixBounds.X = 0;
aPixBounds.Y = 0;
2002-02-04 13:10:18 +00:00
return lcl_PointInRectangle(aPoint, aPixBounds);
2002-02-04 13:10:18 +00:00
}
uno::Reference< XAccessible > SAL_CALL SwAccessibleContext::getAccessibleAtPoint(
2002-02-04 13:10:18 +00:00
const awt::Point& aPoint )
{
SolarMutexGuard aGuard;
2002-02-04 13:10:18 +00:00
ThrowIfDisposed();
2002-02-04 13:10:18 +00:00
uno::Reference< XAccessible > xAcc;
2002-02-04 13:10:18 +00:00
vcl::Window *pWin = GetWindow();
if (!pWin)
{
throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject*>(this));
}
2002-02-04 13:10:18 +00:00
Point aPixPoint( aPoint.X, aPoint.Y ); // px rel to parent
if( !GetFrame()->IsRootFrame() )
{
SwRect aLogBounds( GetBounds( *(GetMap()), GetFrame() ) ); // twip rel to doc root
Point aPixPos( GetMap()->CoreToPixel( aLogBounds.SVRect() ).TopLeft() );
aPixPoint.setX(aPixPoint.getX() + aPixPos.getX());
aPixPoint.setY(aPixPoint.getY() + aPixPos.getY());
}
2002-02-04 13:10:18 +00:00
const SwAccessibleChild aChild( GetChildAtPixel( aPixPoint, *(GetMap()) ) );
if( aChild.GetSwFrame() )
2002-04-05 11:18:25 +00:00
{
xAcc = GetMap()->GetContext( aChild.GetSwFrame() );
2002-04-05 11:18:25 +00:00
}
else if( aChild.GetDrawObject() )
{
xAcc = GetMap()->GetContext( aChild.GetDrawObject(), this );
}
else if ( aChild.GetWindow() )
2002-04-05 11:18:25 +00:00
{
xAcc = aChild.GetWindow()->GetAccessible();
2002-04-05 11:18:25 +00:00
}
2002-02-04 13:10:18 +00:00
return xAcc;
}
/**
Get bounding box.
There are two modes.
- relative
Return bounding box relative to parent if parent is no root
frame. Otherwise return the absolute bounding box.
- absolute
Return the absolute bounding box.
@param bRelative
true: Use relative mode.
false: Use absolute mode.
*/
awt::Rectangle SAL_CALL SwAccessibleContext::getBoundsImpl(bool bRelative)
2002-02-04 13:10:18 +00:00
{
SolarMutexGuard aGuard;
2002-02-04 13:10:18 +00:00
ThrowIfDisposed();
2002-02-04 13:10:18 +00:00
const SwFrame *pParent = GetParent();
OSL_ENSURE( pParent, "no Parent found" );
vcl::Window *pWin = GetWindow();
2002-02-04 13:10:18 +00:00
if (!pParent)
{
throw uno::RuntimeException("no Parent", static_cast<cppu::OWeakObject*>(this));
}
if (!pWin)
{
throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject*>(this));
}
2002-02-04 13:10:18 +00:00
SwRect aLogBounds( GetBounds( *(GetMap()), GetFrame() ) ); // twip relative to document root
tools::Rectangle aPixBounds( 0, 0, 0, 0 );
if( GetFrame()->IsPageFrame() &&
static_cast < const SwPageFrame * >( GetFrame() )->IsEmptyPage() )
2002-05-29 13:59:06 +00:00
{
OSL_ENSURE( GetShell()->IsPreview(), "empty page accessible?" );
if( GetShell()->IsPreview() )
{
// adjust method call <GetMap()->GetPreviewPageSize()>
sal_uInt16 nPageNum =
static_cast < const SwPageFrame * >( GetFrame() )->GetPhyPageNum();
aLogBounds.SSize( GetMap()->GetPreviewPageSize( nPageNum ) );
}
2002-05-29 13:59:06 +00:00
}
2002-04-11 13:04:40 +00:00
if( !aLogBounds.IsEmpty() )
2002-02-27 08:32:33 +00:00
{
2002-05-29 13:59:06 +00:00
aPixBounds = GetMap()->CoreToPixel( aLogBounds.SVRect() );
if( !pParent->IsRootFrame() && bRelative)
2002-04-11 13:04:40 +00:00
{
SwRect aParentLogBounds( GetBounds( *(GetMap()), pParent ) ); // twip rel to doc root
Point aParentPixPos( GetMap()->CoreToPixel( aParentLogBounds.SVRect() ).TopLeft() );
aPixBounds.Move( -aParentPixPos.getX(), -aParentPixPos.getY() );
2002-04-11 13:04:40 +00:00
}
2002-02-27 08:32:33 +00:00
}
2002-02-04 13:10:18 +00:00
awt::Rectangle aBox( aPixBounds.Left(), aPixBounds.Top(),
aPixBounds.GetWidth(), aPixBounds.GetHeight() );
return aBox;
}
awt::Rectangle SAL_CALL SwAccessibleContext::getBounds()
2002-02-04 13:10:18 +00:00
{
return getBoundsImpl(true);
}
2002-05-29 13:59:06 +00:00
awt::Point SAL_CALL SwAccessibleContext::getLocation()
{
awt::Rectangle aRect = getBoundsImpl(true);
awt::Point aPoint(aRect.X, aRect.Y);
2002-02-04 13:10:18 +00:00
return aPoint;
2002-02-04 13:10:18 +00:00
}
awt::Point SAL_CALL SwAccessibleContext::getLocationOnScreen()
2002-02-04 13:10:18 +00:00
{
awt::Rectangle aRect = getBoundsImpl(false);
2002-05-29 13:59:06 +00:00
Point aPixPos(aRect.X, aRect.Y);
2002-05-29 13:59:06 +00:00
vcl::Window *pWin = GetWindow();
if (!pWin)
{
throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject*>(this));
}
aPixPos = pWin->OutputToAbsoluteScreenPixel(aPixPos);
awt::Point aPoint(aPixPos.getX(), aPixPos.getY());
2002-02-04 13:10:18 +00:00
return aPoint;
2002-02-04 13:10:18 +00:00
}
awt::Size SAL_CALL SwAccessibleContext::getSize()
2002-02-04 13:10:18 +00:00
{
awt::Rectangle aRect = getBoundsImpl(false);
awt::Size aSize( aRect.Width, aRect.Height );
2002-02-04 13:10:18 +00:00
return aSize;
}
void SAL_CALL SwAccessibleContext::grabFocus()
{
SolarMutexGuard aGuard;
2002-12-05 13:10:33 +00:00
ThrowIfDisposed();;
2002-12-05 13:10:33 +00:00
if( GetFrame()->IsFlyFrame() )
2002-12-05 13:10:33 +00:00
{
const SdrObject *pObj =
static_cast < const SwFlyFrame * >( GetFrame() )->GetVirtDrawObj();
2002-12-05 13:10:33 +00:00
if( pObj )
Select( const_cast < SdrObject * >( pObj ), false );
2002-12-05 13:10:33 +00:00
}
else
{
const SwContentFrame *pCFrame = nullptr;
if( GetFrame()->IsContentFrame() )
pCFrame = static_cast< const SwContentFrame * >( GetFrame() );
else if( GetFrame()->IsLayoutFrame() )
pCFrame = static_cast< const SwLayoutFrame * >( GetFrame() )->ContainsContent();
2002-12-05 13:10:33 +00:00
if( pCFrame && pCFrame->IsTextFrame() )
2002-12-05 13:10:33 +00:00
{
const SwTextFrame *pTextFrame = static_cast< const SwTextFrame * >( pCFrame );
const SwTextNode *pTextNd = pTextFrame->GetTextNode();
if( pTextNd )
2002-12-05 13:10:33 +00:00
{
// create pam for selection
SwIndex aIndex( const_cast< SwTextNode * >( pTextNd ),
pTextFrame->GetOfst() );
SwPosition aStartPos( *pTextNd, aIndex );
2002-12-05 13:10:33 +00:00
SwPaM aPaM( aStartPos );
// set PaM at cursor shell
Select( aPaM );
}
}
}
2002-02-04 13:10:18 +00:00
}
sal_Int32 SAL_CALL SwAccessibleContext::getForeground()
{
return COL_BLACK;
}
sal_Int32 SAL_CALL SwAccessibleContext::getBackground()
{
return COL_WHITE;
}
sal_Bool SAL_CALL SwAccessibleContext::supportsService (const OUString& ServiceName)
2002-02-04 13:10:18 +00:00
{
return cppu::supportsService(this, ServiceName);
2002-02-04 13:10:18 +00:00
}
2002-05-15 12:22:47 +00:00
void SwAccessibleContext::DisposeShape( const SdrObject *pObj,
::accessibility::AccessibleShape *pAccImpl )
2002-05-15 12:22:47 +00:00
{
2010-10-15 11:44:38 -05:00
::rtl::Reference< ::accessibility::AccessibleShape > xAccImpl( pAccImpl );
if( !xAccImpl.is() )
xAccImpl = GetMap()->GetContextImpl( pObj, this );
2002-05-15 12:22:47 +00:00
AccessibleEventObject aEvent;
aEvent.EventId = AccessibleEventId::CHILD;
2010-10-15 11:44:38 -05:00
uno::Reference< XAccessible > xAcc( xAccImpl.get() );
2002-05-15 12:22:47 +00:00
aEvent.OldValue <<= xAcc;
FireAccessibleEvent( aEvent );
GetMap()->RemoveContext( pObj );
xAccImpl->dispose();
}
void SwAccessibleContext::ScrolledInShape( ::accessibility::AccessibleShape *pAccImpl )
2002-05-15 12:22:47 +00:00
{
if(nullptr == pAccImpl)
{
return ;
}
2002-05-15 12:22:47 +00:00
AccessibleEventObject aEvent;
aEvent.EventId = AccessibleEventId::CHILD;
uno::Reference< XAccessible > xAcc( pAccImpl );
2002-05-15 12:22:47 +00:00
aEvent.NewValue <<= xAcc;
FireAccessibleEvent( aEvent );
if( pAccImpl->GetState( AccessibleStateType::FOCUSED ) )
{
vcl::Window *pWin = GetWindow();
if( pWin && pWin->HasFocus() )
{
AccessibleEventObject aStateChangedEvent;
aStateChangedEvent.EventId = AccessibleEventId::STATE_CHANGED;
aStateChangedEvent.NewValue <<= AccessibleStateType::FOCUSED;
aStateChangedEvent.Source = xAcc;
FireAccessibleEvent( aStateChangedEvent );
}
}
2002-05-15 12:22:47 +00:00
}
void SwAccessibleContext::Dispose(bool bRecursive, bool bCanSkipInvisible)
2002-04-11 13:04:40 +00:00
{
SolarMutexGuard aGuard;
2002-04-11 13:04:40 +00:00
OSL_ENSURE( GetFrame() && GetMap(), "already disposed" );
OSL_ENSURE( GetMap()->GetVisArea() == GetVisArea(),
"invalid visible area for dispose" );
2002-04-11 13:04:40 +00:00
m_isDisposing = true;
2002-04-11 13:04:40 +00:00
// dispose children
if( bRecursive )
DisposeChildren(GetFrame(), bRecursive, bCanSkipInvisible);
2002-04-11 13:04:40 +00:00
// get parent
uno::Reference< XAccessible > xParent( GetWeakParent() );
uno::Reference < XAccessibleContext > xThis( this );
2002-04-11 13:04:40 +00:00
// send child event at parent
if( xParent.is() )
{
SwAccessibleContext *pAcc = static_cast<SwAccessibleContext *>(xParent.get());
2002-04-11 13:04:40 +00:00
AccessibleEventObject aEvent;
aEvent.EventId = AccessibleEventId::CHILD;
2002-04-11 13:04:40 +00:00
aEvent.OldValue <<= xThis;
pAcc->FireAccessibleEvent( aEvent );
}
// set defunc state (it's not required to broadcast a state changed
2014-04-11 08:25:35 +02:00
// event if the object is disposed afterwards)
2002-04-11 13:04:40 +00:00
{
osl::MutexGuard aDefuncStateGuard( m_Mutex );
m_isDefuncState = true;
2002-04-11 13:04:40 +00:00
}
// broadcast dispose event
if (m_nClientId)
2002-04-11 13:04:40 +00:00
{
comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( m_nClientId, *this );
m_nClientId = 0;
2002-04-11 13:04:40 +00:00
}
RemoveFrameFromAccessibleMap();
ClearFrame();
m_pMap = nullptr;
tdf#58624 sw: fix ~SwAccessibleContext() use-after-free race As seen in JunitTest_toolkit_unoapi_1: Method doAccessibleAction() finished with state OK LOG> doAccessibleAction(): COMPLETED.OK debug:27272:12: -SwAccessibleParagraph mutexwait 0x3fd9f50 debug:27272:9: SwAccessibleContext::Dispose 0x3872620 11SwRootFrame debug:27272:9: SwAccessibleContext::DisposeChildren 0x4047c80 0x386d600 11SwPageFrame debug:27272:9: SwAccessibleContext::DisposeChildren xAcc 0 debug:27272:9: SwAccessibleContext::DisposeChildren 0x4047c80 0x386cef0 11SwBodyFrame debug:27272:9: SwAccessibleContext::DisposeChildren xAcc 0 debug:27272:9: SwAccessibleContext::DisposeChildren 0x4047c80 0x3878fe0 11SwTextFrame debug:27272:9: SwAccessibleContext::DisposeChildren xAcc 0 debug:27272:9: SwAccessibleMap::RemoveContext erase 0x3872620 debug:27272:9: ~SwAccessibleMap: frame entry 0x3878fe0 debug:27272:9: ~SwAccessibleMap: mpFrameMap 0x3eb64a0 debug:27272:9: ~SwAccessibleMap: mpShapeMap 0 soffice.bin: sw/source/core/access/accmap.cxx:1726: virtual SwAccessibleMap::~SwAccessibleMap(): Assertion `(!mpFrameMap || mpFrameMap->empty()) && "Frame map should be empty after disposing the root frame"' The problem here is that thread 12 is blocked on SolarMutex in ~SwAccessibleParagraph(), while thread 9 is in ~SwAccessibleMap(). This means that in SwAccessibleContext::DisposeChildren(), the WeakReference to the SwAccessibleParagraph cannot create a uno::Reference because its reference count is 0, so SwAccessibleContext::Dispose() is not called on it and it remains in the SwAccessibleMap::mpFrameMap. This triggers the assert and later on ~SwAccessibleContext() would access the deleted SwAccessibleMap and crash. To fix this, introduce a weak reference from SwAccessibleContext to SwAccessibleMap; use a std::weak_ptr because that is not derived from OWeakObject. The weak_ptr is only used in the dtor ~SwAccessibleContext(); as long as the ref-count of SwAccessibleContext is > 0 it is guaranteed that the SwAccessibleContext::m_pMap is either null or valid as the recursive Dispose() will work fine. It is possible that additional temporary owning references could delay the destruction of SwAccessibleMap, and the order of destruction of Writer documents is very fragile, so rely on the SolarMutex lock to prevent that; the only shared_ptr that owns SwAccessibleMap while SolarMutex is not locked is the one in SwViewShellImp. (An alternative fix would be to represent the 3 lifecycle stages of SwAccessibleContext by adding a C++-pointer to the SwAccessibleMap::mpFrameMap, so that DisposeChildren() can, if the WeakReference is no longer valid due to ref-count 0, use the pointer and clear SwAccessibleContext::m_pMap - this and the corresponding call to SwAccessibleMap::RemoveContext() from ~SwAccessibleContext() under a mutex that is shared_ptr-owned by SwAccessibleMap and all SwAccessibleContext.) Change-Id: If2b44c79189e3b3d276491a5c57d5404bb2be71a
2017-03-24 13:04:32 +01:00
m_wMap.reset();
2002-04-11 13:04:40 +00:00
m_isDisposing = false;
2002-04-11 13:04:40 +00:00
}
void SwAccessibleContext::DisposeChild( const SwAccessibleChild& rChildFrameOrObj,
bool bRecursive, bool bCanSkipInvisible )
2002-04-17 13:07:39 +00:00
{
SolarMutexGuard aGuard;
2002-04-17 13:07:39 +00:00
if ( !bCanSkipInvisible ||
rChildFrameOrObj.AlwaysIncludeAsChild() ||
IsShowing( *(GetMap()), rChildFrameOrObj ) ||
!SwAccessibleChild( GetFrame() ).IsVisibleChildrenOnly() )
2002-04-17 13:07:39 +00:00
{
// If the object could have existed before, than there is nothing to do,
// because no wrapper exists now and therefore no one is interested to
2002-04-17 13:07:39 +00:00
// get notified of the movement.
if( rChildFrameOrObj.GetSwFrame() )
2002-05-15 12:22:47 +00:00
{
2010-10-15 11:44:38 -05:00
::rtl::Reference< SwAccessibleContext > xAccImpl =
GetMap()->GetContextImpl( rChildFrameOrObj.GetSwFrame() );
2002-05-15 12:22:47 +00:00
xAccImpl->Dispose( bRecursive );
}
else if ( rChildFrameOrObj.GetDrawObject() )
2002-05-15 12:22:47 +00:00
{
2010-10-15 11:44:38 -05:00
::rtl::Reference< ::accessibility::AccessibleShape > xAccImpl =
GetMap()->GetContextImpl( rChildFrameOrObj.GetDrawObject(),
this );
DisposeShape( rChildFrameOrObj.GetDrawObject(),
2010-10-15 11:44:38 -05:00
xAccImpl.get() );
2002-05-15 12:22:47 +00:00
}
else if ( rChildFrameOrObj.GetWindow() )
{
AccessibleEventObject aEvent;
aEvent.EventId = AccessibleEventId::CHILD;
uno::Reference< XAccessible > xAcc =
rChildFrameOrObj.GetWindow()->GetAccessible();
aEvent.OldValue <<= xAcc;
FireAccessibleEvent( aEvent );
}
2002-04-17 13:07:39 +00:00
}
else if( bRecursive && rChildFrameOrObj.GetSwFrame() )
DisposeChildren(rChildFrameOrObj.GetSwFrame(), bRecursive, bCanSkipInvisible);
2002-04-17 13:07:39 +00:00
}
void SwAccessibleContext::InvalidatePosOrSize( const SwRect& )
2002-04-11 13:04:40 +00:00
{
SolarMutexGuard aGuard;
2002-04-11 13:04:40 +00:00
OSL_ENSURE( GetFrame() && !GetFrame()->Frame().IsEmpty(), "context should have a size" );
2002-08-15 10:57:22 +00:00
bool bIsOldShowingState;
bool bIsNewShowingState = IsShowing( *(GetMap()) );
2002-04-11 13:04:40 +00:00
{
osl::MutexGuard aShowingStateGuard( m_Mutex );
bIsOldShowingState = m_isShowingState;
m_isShowingState = bIsNewShowingState;
2002-04-11 13:04:40 +00:00
}
if( bIsOldShowingState != bIsNewShowingState )
{
FireStateChangedEvent( AccessibleStateType::SHOWING,
bIsNewShowingState );
}
else if( bIsNewShowingState )
{
// The frame stays visible -> broadcast event
FireVisibleDataEvent();
}
// note: InvalidatePosOrSize must call InvalidateContent_ so that
// SwAccessibleParagraph updates its portions, or dispose it
// (see accmap.cxx: INVALID_CONTENT is contained in POS_CHANGED)
if( !bIsNewShowingState &&
SwAccessibleChild( GetParent() ).IsVisibleChildrenOnly() )
{
// this Dispose call was removed by IAccessibility2 implementation
// without giving any reason why - without it we get stale
// entries in SwAccessibleMap::mpFrameMap.
Dispose(true);
}
else
{
InvalidateContent_( true );
}
2002-04-11 13:04:40 +00:00
}
2002-05-15 12:22:47 +00:00
void SwAccessibleContext::InvalidateChildPosOrSize(
const SwAccessibleChild& rChildFrameOrObj,
const SwRect& rOldFrame )
2002-04-11 13:04:40 +00:00
{
SolarMutexGuard aGuard;
2002-04-11 13:04:40 +00:00
// this happens during layout, e.g. when a page is deleted and next page's
// header/footer moves backward such an event is generated
SAL_INFO_IF(rChildFrameOrObj.GetSwFrame() &&
rChildFrameOrObj.GetSwFrame()->Frame().IsEmpty(),
"sw.a11y", "child context should have a size");
2002-08-15 10:57:22 +00:00
if ( rChildFrameOrObj.AlwaysIncludeAsChild() )
{
// nothing to do;
return;
}
const bool bVisibleChildrenOnly = SwAccessibleChild( GetFrame() ).IsVisibleChildrenOnly();
const bool bNew = rOldFrame.IsEmpty() ||
( rOldFrame.Left() == 0 && rOldFrame.Top() == 0 );
if( IsShowing( *(GetMap()), rChildFrameOrObj ) )
2002-04-11 13:04:40 +00:00
{
// If the object could have existed before, than there is nothing to do,
// because no wrapper exists now and therefore no one is interested to
2002-04-11 13:04:40 +00:00
// get notified of the movement.
if( bNew || (bVisibleChildrenOnly && !IsShowing( rOldFrame )) )
2002-04-11 13:04:40 +00:00
{
if( rChildFrameOrObj.GetSwFrame() )
2002-05-15 12:22:47 +00:00
{
// The frame becomes visible. A child event must be send.
2010-10-15 11:44:38 -05:00
::rtl::Reference< SwAccessibleContext > xAccImpl =
GetMap()->GetContextImpl( rChildFrameOrObj.GetSwFrame() );
2002-05-15 12:22:47 +00:00
xAccImpl->ScrolledIn();
}
else if ( rChildFrameOrObj.GetDrawObject() )
2002-05-15 12:22:47 +00:00
{
2010-10-15 11:44:38 -05:00
::rtl::Reference< ::accessibility::AccessibleShape > xAccImpl =
GetMap()->GetContextImpl( rChildFrameOrObj.GetDrawObject(),
this );
2011-01-30 04:19:53 +09:00
// #i37790#
2010-10-15 11:44:38 -05:00
if ( xAccImpl.is() )
{
ScrolledInShape( xAccImpl.get() );
}
else
{
OSL_FAIL( "<SwAccessibleContext::InvalidateChildPosOrSize(..)> - no accessible shape found." );
}
2002-05-15 12:22:47 +00:00
}
else if ( rChildFrameOrObj.GetWindow() )
{
AccessibleEventObject aEvent;
aEvent.EventId = AccessibleEventId::CHILD;
aEvent.NewValue <<= (rChildFrameOrObj.GetWindow()->GetAccessible());
FireAccessibleEvent( aEvent );
}
2002-04-11 13:04:40 +00:00
}
}
else
{
// If the frame was visible before, than a child event for the parent
// needs to be send. However, there is no wrapper existing, and so
// no notifications for grandchildren are required. If the are
// grandgrandchildren, they would be notified by the layout.
if( bVisibleChildrenOnly &&
!bNew && IsShowing( rOldFrame ) )
2002-04-11 13:04:40 +00:00
{
if( rChildFrameOrObj.GetSwFrame() )
2002-05-15 12:22:47 +00:00
{
2010-10-15 11:44:38 -05:00
::rtl::Reference< SwAccessibleContext > xAccImpl =
GetMap()->GetContextImpl( rChildFrameOrObj.GetSwFrame() );
2002-05-15 12:22:47 +00:00
xAccImpl->SetParent( this );
xAccImpl->Dispose( true );
2002-05-15 12:22:47 +00:00
}
else if ( rChildFrameOrObj.GetDrawObject() )
2002-05-15 12:22:47 +00:00
{
2010-10-15 11:44:38 -05:00
::rtl::Reference< ::accessibility::AccessibleShape > xAccImpl =
GetMap()->GetContextImpl( rChildFrameOrObj.GetDrawObject(),
this );
DisposeShape( rChildFrameOrObj.GetDrawObject(),
2010-10-15 11:44:38 -05:00
xAccImpl.get() );
2002-05-15 12:22:47 +00:00
}
else if ( rChildFrameOrObj.GetWindow() )
{
OSL_FAIL( "<SwAccessibleContext::InvalidateChildPosOrSize(..)> - not expected to handle dispose of child of type <vcl::Window>." );
}
2002-04-11 13:04:40 +00:00
}
}
}
void SwAccessibleContext::InvalidateContent()
{
SolarMutexGuard aGuard;
2002-04-11 13:04:40 +00:00
InvalidateContent_( false );
2002-04-11 13:04:40 +00:00
}
void SwAccessibleContext::InvalidateCursorPos()
{
SolarMutexGuard aGuard;
2002-04-11 13:04:40 +00:00
InvalidateCursorPos_();
2002-04-11 13:04:40 +00:00
}
2002-05-16 07:22:32 +00:00
void SwAccessibleContext::InvalidateFocus()
{
SolarMutexGuard aGuard;
2002-05-16 07:22:32 +00:00
InvalidateFocus_();
2002-05-16 07:22:32 +00:00
}
2011-01-30 04:19:53 +09:00
// #i27301# - use new type definition for <_nStates>
void SwAccessibleContext::InvalidateStates( AccessibleStates _nStates )
2002-04-11 13:04:40 +00:00
{
if( GetMap() )
{
SwViewShell *pVSh = GetMap()->GetShell();
2002-04-11 13:04:40 +00:00
if( pVSh )
{
if( _nStates & AccessibleStates::EDITABLE )
2002-04-11 13:04:40 +00:00
{
bool bIsOldEditableState;
bool bIsNewEditableState = IsEditable( pVSh );
2002-04-11 13:04:40 +00:00
{
osl::MutexGuard aGuard( m_Mutex );
bIsOldEditableState = m_isEditableState;
m_isEditableState = bIsNewEditableState;
2002-04-11 13:04:40 +00:00
}
if( bIsOldEditableState != bIsNewEditableState )
FireStateChangedEvent( AccessibleStateType::EDITABLE,
bIsNewEditableState );
}
if( _nStates & AccessibleStates::OPAQUE )
2002-04-11 13:04:40 +00:00
{
bool bIsOldOpaqueState;
bool bIsNewOpaqueState = IsOpaque( pVSh );
2002-04-11 13:04:40 +00:00
{
osl::MutexGuard aGuard( m_Mutex );
bIsOldOpaqueState = m_isOpaqueState;
m_isOpaqueState = bIsNewOpaqueState;
2002-04-11 13:04:40 +00:00
}
if( bIsOldOpaqueState != bIsNewOpaqueState )
FireStateChangedEvent( AccessibleStateType::OPAQUE,
bIsNewOpaqueState );
}
}
InvalidateChildrenStates( GetFrame(), _nStates );
2002-04-11 13:04:40 +00:00
}
}
void SwAccessibleContext::InvalidateRelation( sal_uInt16 nType )
{
AccessibleEventObject aEvent;
aEvent.EventId = nType;
FireAccessibleEvent( aEvent );
}
2011-01-30 04:19:53 +09:00
/** #i27301# - text selection has changed */
void SwAccessibleContext::InvalidateTextSelection()
{
AccessibleEventObject aEvent;
aEvent.EventId = AccessibleEventId::TEXT_SELECTION_CHANGED;
FireAccessibleEvent( aEvent );
}
2011-01-30 04:19:53 +09:00
/** #i88069# - attributes has changed */
void SwAccessibleContext::InvalidateAttr()
{
AccessibleEventObject aEvent;
aEvent.EventId = AccessibleEventId::TEXT_ATTRIBUTE_CHANGED;
FireAccessibleEvent( aEvent );
}
bool SwAccessibleContext::HasCursor()
2002-04-11 13:04:40 +00:00
{
return false;
2002-04-11 13:04:40 +00:00
}
bool SwAccessibleContext::Select( SwPaM *pPaM, SdrObject *pObj,
bool bAdd )
{
SwCursorShell* pCursorShell = GetCursorShell();
if( !pCursorShell )
return false;
SwFEShell* pFEShell = dynamic_cast<const SwFEShell*>( pCursorShell) != nullptr
? static_cast<SwFEShell*>( pCursorShell )
: nullptr;
// Get rid of activated OLE object
if( pFEShell )
pFEShell->FinishOLEObj();
SwWrtShell* pWrtShell = dynamic_cast<const SwWrtShell*>( pCursorShell) != nullptr
? static_cast<SwWrtShell*>( pCursorShell )
: nullptr;
bool bRet = false;
if( pObj )
{
if( pFEShell )
{
Point aDummy;
sal_uInt8 nFlags = bAdd ? SW_ADD_SELECT : 0;
pFEShell->SelectObj( aDummy, nFlags, pObj );
bRet = true;
}
}
else if( pPaM )
{
// Get rid of frame selection. If there is one, make text cursor
// visible again.
bool bCallShowCursor = false;
if( pFEShell && (pFEShell->IsFrameSelected() ||
pFEShell->IsObjSelected()) )
{
Point aPt( LONG_MIN, LONG_MIN );
pFEShell->SelectObj( aPt );
bCallShowCursor = true;
}
pCursorShell->KillPams();
if( pWrtShell && pPaM->HasMark() )
// We have to do this or SwWrtShell can't figure out that it needs
// to kill the selection later, when the user moves the cursor.
pWrtShell->SttSelect();
pCursorShell->SetSelection( *pPaM );
if( pPaM->HasMark() && *pPaM->GetPoint() == *pPaM->GetMark())
// Setting a "Selection" that starts and ends at the same spot
// should remove the selection rather than create an empty one, so
// that we get defined behavior if accessibility sets the cursor
// later.
pCursorShell->ClearMark();
if( bCallShowCursor )
pCursorShell->ShowCursor();
bRet = true;
}
return bRet;
}
migrate to boost::gettext * all .ui files go from <interface> to <interface domain="MODULE"> e.g. vcl * all .src files go away and the english source strings folded into the .hrc as NC_("context", "source string") * ResMgr is dropped in favour of std::locale imbued by boost::locale::generator pointed at matching MODULE .mo files * UIConfig translations are folded into the module .mo, so e.g. UIConfig_cui goes from l10n target to normal one, so the res/lang.zips of UI files go away * translation via Translation::get(hrc-define-key, imbued-std::locale) * python can now be translated with its inbuilt gettext support (we keep the name strings.hrc there to keep finding the .hrc file uniform) so magic numbers can go away there * java and starbasic components can be translated via the pre-existing css.resource.StringResourceWithLocation mechanism * en-US res files go away, their strings are now the .hrc keys in the source code * remaining .res files are replaced by .mo files * in .res/.ui-lang-zip files, the old scheme missing translations of strings results in inserting the english original so something can be found, now the standard fallback of using the english original from the source key is used, so partial translations shrink dramatically in size * extract .hrc strings with hrcex which backs onto xgettext -C --add-comments --keyword=NC_:1c,2 --from-code=UTF-8 --no-wrap * extract .ui strings with uiex which backs onto xgettext --add-comments --no-wrap * qtz for gettext translations is generated at runtime as ascii-ified crc32 of content + "|" + msgid * [API CHANGE] remove deprecated binary .res resouce loader related uno apis com::sun::star::resource::OfficeResourceLoader com::sun::star::resource::XResourceBundleLoader com::sun::star::resource::XResourceBundle when translating strings via uno apis com.sun.star.resource.StringResourceWithLocation can continue to be used Change-Id: Ia2594a2672b7301d9c3421fdf31b6cfe7f3f8d0a
2017-06-11 20:56:30 +01:00
OUString SwAccessibleContext::GetResource(const char* pResId,
const OUString *pArg1,
const OUString *pArg2)
2002-04-11 13:04:40 +00:00
{
migrate to boost::gettext * all .ui files go from <interface> to <interface domain="MODULE"> e.g. vcl * all .src files go away and the english source strings folded into the .hrc as NC_("context", "source string") * ResMgr is dropped in favour of std::locale imbued by boost::locale::generator pointed at matching MODULE .mo files * UIConfig translations are folded into the module .mo, so e.g. UIConfig_cui goes from l10n target to normal one, so the res/lang.zips of UI files go away * translation via Translation::get(hrc-define-key, imbued-std::locale) * python can now be translated with its inbuilt gettext support (we keep the name strings.hrc there to keep finding the .hrc file uniform) so magic numbers can go away there * java and starbasic components can be translated via the pre-existing css.resource.StringResourceWithLocation mechanism * en-US res files go away, their strings are now the .hrc keys in the source code * remaining .res files are replaced by .mo files * in .res/.ui-lang-zip files, the old scheme missing translations of strings results in inserting the english original so something can be found, now the standard fallback of using the english original from the source key is used, so partial translations shrink dramatically in size * extract .hrc strings with hrcex which backs onto xgettext -C --add-comments --keyword=NC_:1c,2 --from-code=UTF-8 --no-wrap * extract .ui strings with uiex which backs onto xgettext --add-comments --no-wrap * qtz for gettext translations is generated at runtime as ascii-ified crc32 of content + "|" + msgid * [API CHANGE] remove deprecated binary .res resouce loader related uno apis com::sun::star::resource::OfficeResourceLoader com::sun::star::resource::XResourceBundleLoader com::sun::star::resource::XResourceBundle when translating strings via uno apis com.sun.star.resource.StringResourceWithLocation can continue to be used Change-Id: Ia2594a2672b7301d9c3421fdf31b6cfe7f3f8d0a
2017-06-11 20:56:30 +01:00
OUString sStr = SwResId(pResId);
2002-04-11 13:04:40 +00:00
if( pArg1 )
{
sStr = sStr.replaceFirst( "$(ARG1)", *pArg1 );
2002-04-11 13:04:40 +00:00
}
if( pArg2 )
{
sStr = sStr.replaceFirst( "$(ARG2)", *pArg2 );
2002-04-11 13:04:40 +00:00
}
return sStr;
tdf#58624 sw: fix ~SwAccessibleContext() use-after-free race As seen in JunitTest_toolkit_unoapi_1: Method doAccessibleAction() finished with state OK LOG> doAccessibleAction(): COMPLETED.OK debug:27272:12: -SwAccessibleParagraph mutexwait 0x3fd9f50 debug:27272:9: SwAccessibleContext::Dispose 0x3872620 11SwRootFrame debug:27272:9: SwAccessibleContext::DisposeChildren 0x4047c80 0x386d600 11SwPageFrame debug:27272:9: SwAccessibleContext::DisposeChildren xAcc 0 debug:27272:9: SwAccessibleContext::DisposeChildren 0x4047c80 0x386cef0 11SwBodyFrame debug:27272:9: SwAccessibleContext::DisposeChildren xAcc 0 debug:27272:9: SwAccessibleContext::DisposeChildren 0x4047c80 0x3878fe0 11SwTextFrame debug:27272:9: SwAccessibleContext::DisposeChildren xAcc 0 debug:27272:9: SwAccessibleMap::RemoveContext erase 0x3872620 debug:27272:9: ~SwAccessibleMap: frame entry 0x3878fe0 debug:27272:9: ~SwAccessibleMap: mpFrameMap 0x3eb64a0 debug:27272:9: ~SwAccessibleMap: mpShapeMap 0 soffice.bin: sw/source/core/access/accmap.cxx:1726: virtual SwAccessibleMap::~SwAccessibleMap(): Assertion `(!mpFrameMap || mpFrameMap->empty()) && "Frame map should be empty after disposing the root frame"' The problem here is that thread 12 is blocked on SolarMutex in ~SwAccessibleParagraph(), while thread 9 is in ~SwAccessibleMap(). This means that in SwAccessibleContext::DisposeChildren(), the WeakReference to the SwAccessibleParagraph cannot create a uno::Reference because its reference count is 0, so SwAccessibleContext::Dispose() is not called on it and it remains in the SwAccessibleMap::mpFrameMap. This triggers the assert and later on ~SwAccessibleContext() would access the deleted SwAccessibleMap and crash. To fix this, introduce a weak reference from SwAccessibleContext to SwAccessibleMap; use a std::weak_ptr because that is not derived from OWeakObject. The weak_ptr is only used in the dtor ~SwAccessibleContext(); as long as the ref-count of SwAccessibleContext is > 0 it is guaranteed that the SwAccessibleContext::m_pMap is either null or valid as the recursive Dispose() will work fine. It is possible that additional temporary owning references could delay the destruction of SwAccessibleMap, and the order of destruction of Writer documents is very fragile, so rely on the SolarMutex lock to prevent that; the only shared_ptr that owns SwAccessibleMap while SolarMutex is not locked is the one in SwViewShellImp. (An alternative fix would be to represent the 3 lifecycle stages of SwAccessibleContext by adding a C++-pointer to the SwAccessibleMap::mpFrameMap, so that DisposeChildren() can, if the WeakReference is no longer valid due to ref-count 0, use the pointer and clear SwAccessibleContext::m_pMap - this and the corresponding call to SwAccessibleMap::RemoveContext() from ~SwAccessibleContext() under a mutex that is shared_ptr-owned by SwAccessibleMap and all SwAccessibleContext.) Change-Id: If2b44c79189e3b3d276491a5c57d5404bb2be71a
2017-03-24 13:04:32 +01:00
}
void SwAccessibleContext::RemoveFrameFromAccessibleMap()
{
tdf#58624 sw: fix ~SwAccessibleContext() use-after-free race As seen in JunitTest_toolkit_unoapi_1: Method doAccessibleAction() finished with state OK LOG> doAccessibleAction(): COMPLETED.OK debug:27272:12: -SwAccessibleParagraph mutexwait 0x3fd9f50 debug:27272:9: SwAccessibleContext::Dispose 0x3872620 11SwRootFrame debug:27272:9: SwAccessibleContext::DisposeChildren 0x4047c80 0x386d600 11SwPageFrame debug:27272:9: SwAccessibleContext::DisposeChildren xAcc 0 debug:27272:9: SwAccessibleContext::DisposeChildren 0x4047c80 0x386cef0 11SwBodyFrame debug:27272:9: SwAccessibleContext::DisposeChildren xAcc 0 debug:27272:9: SwAccessibleContext::DisposeChildren 0x4047c80 0x3878fe0 11SwTextFrame debug:27272:9: SwAccessibleContext::DisposeChildren xAcc 0 debug:27272:9: SwAccessibleMap::RemoveContext erase 0x3872620 debug:27272:9: ~SwAccessibleMap: frame entry 0x3878fe0 debug:27272:9: ~SwAccessibleMap: mpFrameMap 0x3eb64a0 debug:27272:9: ~SwAccessibleMap: mpShapeMap 0 soffice.bin: sw/source/core/access/accmap.cxx:1726: virtual SwAccessibleMap::~SwAccessibleMap(): Assertion `(!mpFrameMap || mpFrameMap->empty()) && "Frame map should be empty after disposing the root frame"' The problem here is that thread 12 is blocked on SolarMutex in ~SwAccessibleParagraph(), while thread 9 is in ~SwAccessibleMap(). This means that in SwAccessibleContext::DisposeChildren(), the WeakReference to the SwAccessibleParagraph cannot create a uno::Reference because its reference count is 0, so SwAccessibleContext::Dispose() is not called on it and it remains in the SwAccessibleMap::mpFrameMap. This triggers the assert and later on ~SwAccessibleContext() would access the deleted SwAccessibleMap and crash. To fix this, introduce a weak reference from SwAccessibleContext to SwAccessibleMap; use a std::weak_ptr because that is not derived from OWeakObject. The weak_ptr is only used in the dtor ~SwAccessibleContext(); as long as the ref-count of SwAccessibleContext is > 0 it is guaranteed that the SwAccessibleContext::m_pMap is either null or valid as the recursive Dispose() will work fine. It is possible that additional temporary owning references could delay the destruction of SwAccessibleMap, and the order of destruction of Writer documents is very fragile, so rely on the SolarMutex lock to prevent that; the only shared_ptr that owns SwAccessibleMap while SolarMutex is not locked is the one in SwViewShellImp. (An alternative fix would be to represent the 3 lifecycle stages of SwAccessibleContext by adding a C++-pointer to the SwAccessibleMap::mpFrameMap, so that DisposeChildren() can, if the WeakReference is no longer valid due to ref-count 0, use the pointer and clear SwAccessibleContext::m_pMap - this and the corresponding call to SwAccessibleMap::RemoveContext() from ~SwAccessibleContext() under a mutex that is shared_ptr-owned by SwAccessibleMap and all SwAccessibleContext.) Change-Id: If2b44c79189e3b3d276491a5c57d5404bb2be71a
2017-03-24 13:04:32 +01:00
assert(m_refCount > 0); // must be alive to do this without using m_wMap
if (m_isRegisteredAtAccessibleMap && GetFrame() && GetMap())
GetMap()->RemoveContext( GetFrame() );
}
bool SwAccessibleContext::HasAdditionalAccessibleChildren()
{
bool bRet( false );
if ( GetFrame()->IsTextFrame() )
{
SwPostItMgr* pPostItMgr = GetMap()->GetShell()->GetPostItMgr();
if ( pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() )
{
bRet = pPostItMgr->HasFrameConnectedSidebarWins( *(GetFrame()) );
}
}
return bRet;
}
2011-01-30 04:19:53 +09:00
/** #i88070# - get additional accessible child by index */
vcl::Window* SwAccessibleContext::GetAdditionalAccessibleChild( const sal_Int32 nIndex )
{
vcl::Window* pAdditionalAccessibleChild( nullptr );
if ( GetFrame()->IsTextFrame() )
{
SwPostItMgr* pPostItMgr = GetMap()->GetShell()->GetPostItMgr();
if ( pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() )
{
pAdditionalAccessibleChild =
pPostItMgr->GetSidebarWinForFrameByIndex( *(GetFrame()), nIndex );
}
}
return pAdditionalAccessibleChild;
}
2011-01-30 04:19:53 +09:00
/** #i88070# - get all additional accessible children */
void SwAccessibleContext::GetAdditionalAccessibleChildren( std::vector< vcl::Window* >* pChildren )
{
if ( GetFrame()->IsTextFrame() )
{
SwPostItMgr* pPostItMgr = GetMap()->GetShell()->GetPostItMgr();
if ( pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() )
{
pPostItMgr->GetAllSidebarWinForFrame( *(GetFrame()), pChildren );
}
}
}
2002-04-11 13:04:40 +00:00
bool SwAccessibleContext::SetSelectedState(bool const bSelected)
{
if (m_isSelectedInDoc != bSelected)
{
m_isSelectedInDoc = bSelected;
FireStateChangedEvent( AccessibleStateType::SELECTED, bSelected );
return true;
}
return false;
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */