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.Grand@me.com>
diff --git a/include/svl/poolitem.hxx b/include/svl/poolitem.hxx
index ce4d94e..4714e2f 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 4f71a8a..48f0679 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 a4f02d1..928ac3d 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 9023cca..09b070e 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 6d778c1..d909d859 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 5e3c26e..b3dd57c 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 f724ac2..7d57bcd 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()