include/svl/poolitem.hxx | 10 ++++++ svl/source/items/itemset.cxx | 2 - svl/source/items/poolitem.cxx | 1 sw/source/core/attr/cellatr.cxx | 3 + sw/source/core/attr/swatrset.cxx | 61 ++++++++++++++++++++++++++++++++++++++- sw/source/core/layout/atrfrm.cxx | 6 +++ sw/source/core/para/paratr.cxx | 6 +++ 7 files changed, 87 insertions(+), 2 deletions(-)
New commits: commit b45ab5256c9768914bcad854ce32b06caa0539b8 Author: Armin Le Grand (allotropia) <armin.le.grand.ext...@allotropia.de> AuthorDate: Thu Sep 28 17:44:36 2023 +0200 Commit: Armin Le Grand <armin.le.gr...@me.com> CommitDate: Fri Sep 29 11:18:52 2023 +0200 ITEM: Correct handling of Items in sw ...that use internally an sw::BroadcastingModify*. This caused an error/crash with the testfile ooo58307-1.sxw, thanks to Caolan to find it. For BG info please see comments in the changed sw/source/core/attr/swatrset.cxx Change-Id: Ia91fff19ffb39873c7e2bd5ff8806b95fc5ac7ba Reviewed-on: https://gerrit.libreoffice.org/c/core/+/157383 Tested-by: Jenkins Reviewed-by: Armin Le Grand <armin.le.gr...@me.com> diff --git a/include/svl/poolitem.hxx b/include/svl/poolitem.hxx index ce4d94e782f9..4714e2f44be6 100644 --- a/include/svl/poolitem.hxx +++ b/include/svl/poolitem.hxx @@ -121,6 +121,15 @@ friend class SfxItemSet; bool m_bDeleteOnIdle : 1; bool m_bStaticDefault : 1; bool m_bPoolDefault : 1; + +protected: + // Defines if the Item can be shared/RefCounted else it will be cloned. + // Default is true - as it should be for all Items. It is needed by some + // SW items, so protected to let them set it in constructor. If this could + // be fixed at that Items we may remove this again. + bool m_bShareable : 1; + +private: #ifdef DBG_UTIL // this flag will make debugging item stuff much simpler bool m_bDeleted : 1; @@ -151,6 +160,7 @@ public: bool isDeleteOnIdle() const { return m_bDeleteOnIdle; } bool isStaticDefault() const { return m_bStaticDefault; } bool isPoolDefault() const { return m_bPoolDefault; } + bool isShareable() const { return m_bShareable; } private: inline sal_uInt32 ReleaseRef(sal_uInt32 n = 1) const diff --git a/svl/source/items/itemset.cxx b/svl/source/items/itemset.cxx index 4f71a8aafb5e..48f0679e481d 100644 --- a/svl/source/items/itemset.cxx +++ b/svl/source/items/itemset.cxx @@ -159,7 +159,7 @@ SfxPoolItem const* SfxItemSet::implCreateItemEntry(SfxPoolItem const* pSource, s // these need to be cloned return pSource->Clone(); - if (bItemIsSetMember && IsPooledItem(pSource))//!IsPoolDefaultItem(pSource) && GetPool()->IsItemPoolable(*pSource)) + if (pSource->isShareable() && bItemIsSetMember && IsPooledItem(pSource))//!IsPoolDefaultItem(pSource) && GetPool()->IsItemPoolable(*pSource)) { // shortcut: if we know that the Item is already a member // of another SfxItemSet we can just copy the pointer and increase RefCount diff --git a/svl/source/items/poolitem.cxx b/svl/source/items/poolitem.cxx index a4f02d19b4e3..928ac3de3430 100644 --- a/svl/source/items/poolitem.cxx +++ b/svl/source/items/poolitem.cxx @@ -475,6 +475,7 @@ SfxPoolItem::SfxPoolItem(sal_uInt16 const nWhich) , m_bDeleteOnIdle(false) , m_bStaticDefault(false) , m_bPoolDefault(false) + , m_bShareable(true) #ifdef DBG_UTIL , m_bDeleted(false) #endif diff --git a/sw/source/core/attr/cellatr.cxx b/sw/source/core/attr/cellatr.cxx index 9023cca2f793..09b070e42c3a 100644 --- a/sw/source/core/attr/cellatr.cxx +++ b/sw/source/core/attr/cellatr.cxx @@ -58,6 +58,9 @@ SwTableBoxFormula::SwTableBoxFormula( const OUString& rFormula ) SwTableFormula( rFormula ), m_pDefinedIn( nullptr ) { + // ITEM: mark this Item to be non-shareable/non-RefCountable. For more + // info see comment @SwAttrSet::SetModifyAtAttr + m_bShareable = false; } bool SwTableBoxFormula::operator==( const SfxPoolItem& rAttr ) const diff --git a/sw/source/core/attr/swatrset.cxx b/sw/source/core/attr/swatrset.cxx index 6d778c1cb6b2..d909d8597e54 100644 --- a/sw/source/core/attr/swatrset.cxx +++ b/sw/source/core/attr/swatrset.cxx @@ -95,6 +95,10 @@ SwAttrPool::~SwAttrPool() /// Notification callback void SwAttrSet::changeCallback(const SfxPoolItem* pOld, const SfxPoolItem* pNew) const { + // will have no effect, return + if (nullptr == m_pOldSet && nullptr == m_pNewSet) + return; + // at least one SfxPoolItem has to be provided, else this is an error assert(nullptr != pOld || nullptr != pNew); sal_uInt16 nWhich(0); @@ -233,6 +237,10 @@ SwAttrSet SwAttrSet::CloneAsValue( bool bItems ) const bool SwAttrSet::Put_BC( const SfxPoolItem& rAttr, SwAttrSet* pOld, SwAttrSet* pNew ) { + // direct call when neither pOld nor pNew is set, no need for callback + if (nullptr == pOld && nullptr == pNew) + return nullptr != SfxItemSet::Put( rAttr ); + m_pNewSet = pNew; m_pOldSet = pOld; setCallback(m_aCallbackHolder); @@ -245,6 +253,10 @@ bool SwAttrSet::Put_BC( const SfxPoolItem& rAttr, bool SwAttrSet::Put_BC( const SfxItemSet& rSet, SwAttrSet* pOld, SwAttrSet* pNew ) { + // direct call when neither pOld nor pNew is set, no need for callback + if (nullptr == pOld && nullptr == pNew) + return SfxItemSet::Put( rSet ); + m_pNewSet = pNew; m_pOldSet = pOld; setCallback(m_aCallbackHolder); @@ -257,6 +269,10 @@ bool SwAttrSet::Put_BC( const SfxItemSet& rSet, sal_uInt16 SwAttrSet::ClearItem_BC( sal_uInt16 nWhich, SwAttrSet* pOld, SwAttrSet* pNew ) { + // direct call when neither pOld nor pNew is set, no need for callback + if (nullptr == pOld && nullptr == pNew) + return SfxItemSet::ClearItem( nWhich ); + m_pNewSet = pNew; m_pOldSet = pOld; setCallback(m_aCallbackHolder); @@ -270,10 +286,19 @@ sal_uInt16 SwAttrSet::ClearItem_BC( sal_uInt16 nWhich1, sal_uInt16 nWhich2, SwAttrSet* pOld, SwAttrSet* pNew ) { OSL_ENSURE( nWhich1 <= nWhich2, "no valid range" ); + sal_uInt16 nRet = 0; + + // direct call when neither pOld nor pNew is set, no need for callback + if (nullptr == pOld && nullptr == pNew) + { + for( ; nWhich1 <= nWhich2; ++nWhich1 ) + nRet = nRet + SfxItemSet::ClearItem( nWhich1 ); + return nRet; + } + m_pNewSet = pNew; m_pOldSet = pOld; setCallback(m_aCallbackHolder); - sal_uInt16 nRet = 0; for( ; nWhich1 <= nWhich2; ++nWhich1 ) nRet = nRet + SfxItemSet::ClearItem( nWhich1 ); clearCallback(); @@ -284,6 +309,13 @@ sal_uInt16 SwAttrSet::ClearItem_BC( sal_uInt16 nWhich1, sal_uInt16 nWhich2, int SwAttrSet::Intersect_BC( const SfxItemSet& rSet, SwAttrSet* pOld, SwAttrSet* pNew ) { + // direct call when neither pOld nor pNew is set, no need for callback + if (nullptr == pOld && nullptr == pNew) + { + SfxItemSet::Intersect( rSet ); + return 0; // as below when neither pOld nor pNew is set + } + m_pNewSet = pNew; m_pOldSet = pOld; setCallback(m_aCallbackHolder); @@ -305,6 +337,33 @@ bool SwAttrSet::SetModifyAtAttr( const sw::BroadcastingModify* pModify ) { bool bSet = false; + // ITEM: At ths place the paradigm of Item/Set/Pool gets broken: + // The three Items in Writer + // - SwFormatPageDesc + // - SwFormatDrop + // - SwTableBoxFormula + // contain a unique ptr to SwFormat (or: sw::BroadcastingModify + // if you wish), so the Item *cannot* be shared or re-used. + // But that is the intended nature of Items: + // - they are read-only (note the bad const_cast's below) + // - they are ref-counted to be re-usable in as many Sets as + // possible + // Thus if we need to change that ptr @ Item we *need* to make + // sure that Item is *unique*. This is now done by using the + // 'm_bShareable' at the Item (see where that gets set). + // This was done in the past using the 'poolable' flag, but that + // flag was/is for a completely different purpose - uniqueness is + // a side-effect. It already leaded to massively cloning that Item. + // That something is not 'clean' here can also be seen by using + // const_cast to *change* values at Items *in* the Set - these + // are returned by const reference for a purpose. + // If info/data at an Item has to be changed, the official/clean + // way is to create a new one (e.g. Clone), set the values (if + // not given to the constructor) and then set that Item at the Set. + // NOTE: I do not know if and how it would be possible, but holding + // a SwFormat*/sw::BroadcastingModify* at those Items should + // be fixed/removed ASAP + const SwFormatPageDesc* pPageDescItem = GetItemIfSet( RES_PAGEDESC, false ); if( pPageDescItem && pPageDescItem->GetDefinedIn() != pModify ) diff --git a/sw/source/core/layout/atrfrm.cxx b/sw/source/core/layout/atrfrm.cxx index 5e3c26e708c7..b3dd57ca6c7f 100644 --- a/sw/source/core/layout/atrfrm.cxx +++ b/sw/source/core/layout/atrfrm.cxx @@ -634,6 +634,9 @@ SwFormatPageDesc::SwFormatPageDesc( const SwFormatPageDesc &rCpy ) m_oNumOffset( rCpy.m_oNumOffset ), m_pDefinedIn( nullptr ) { + // ITEM: mark this Item to be non-shareable/non-RefCountable. For more + // info see comment @SwAttrSet::SetModifyAtAttr + m_bShareable = false; } SwFormatPageDesc::SwFormatPageDesc( const SwPageDesc *pDesc ) @@ -641,6 +644,9 @@ SwFormatPageDesc::SwFormatPageDesc( const SwPageDesc *pDesc ) SwClient( const_cast<SwPageDesc*>(pDesc) ), m_pDefinedIn( nullptr ) { + // ITEM: mark this Item to be non-shareable/non-RefCountable. For more + // info see comment @SwAttrSet::SetModifyAtAttr + m_bShareable = false; } SwFormatPageDesc &SwFormatPageDesc::operator=(const SwFormatPageDesc &rCpy) diff --git a/sw/source/core/para/paratr.cxx b/sw/source/core/para/paratr.cxx index f724ac21c0fe..7d57bcd3c7a4 100644 --- a/sw/source/core/para/paratr.cxx +++ b/sw/source/core/para/paratr.cxx @@ -44,6 +44,9 @@ SwFormatDrop::SwFormatDrop() m_nChars( 0 ), m_bWholeWord( false ) { + // ITEM: mark this Item to be non-shareable/non-RefCountable. For more + // info see comment @SwAttrSet::SetModifyAtAttr + m_bShareable = false; } SwFormatDrop::SwFormatDrop( const SwFormatDrop &rCpy ) @@ -55,6 +58,9 @@ SwFormatDrop::SwFormatDrop( const SwFormatDrop &rCpy ) m_nChars( rCpy.GetChars() ), m_bWholeWord( rCpy.GetWholeWord() ) { + // ITEM: mark this Item to be non-shareable/non-RefCountable. For more + // info see comment @SwAttrSet::SetModifyAtAttr + m_bShareable = false; } SwFormatDrop::~SwFormatDrop()