sw: fix issue with copying stashed frame format

When the PageDesc is copied from one document to another, we
don't make sure the stashed FrameFormat(s) are also properly
copied to the new document (which can happen at copy/paste). This
can cause a crash if the stashed FrameFormats are accessed or
destructed after the original document is destroyed.

This fixes the issue so that when we detect the PageDesc belong
to different documents, the stashed FrameFormats are copied just
like the non-stashed FrameFormats (used for headers and footers).

Change-Id: I948068dba4d39bb47c3725dfa8491c53c5833c7e
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160065
Tested-by: Jenkins
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
diff --git a/sw/inc/pagedesc.hxx b/sw/inc/pagedesc.hxx
index 382bbb5..11bb347 100644
--- a/sw/inc/pagedesc.hxx
+++ b/sw/inc/pagedesc.hxx
@@ -223,7 +223,7 @@ public:
    const SwFrameFormat* GetStashedFrameFormat(bool bHeader, bool bLeft, bool bFirst) const;

    /// Checks if the pagedescriptor has a stashed format according to the parameters or not.
    bool HasStashedFormat(bool bHeader, bool bLeft, bool bFirst);
    bool HasStashedFormat(bool bHeader, bool bLeft, bool bFirst) const;

    /// Gives the feature of removing the stashed format by hand if it is necessary.
    void RemoveStashedFormat(bool bHeader, bool bLeft, bool bFirst);
diff --git a/sw/qa/core/header_footer/HeaderFooterTest.cxx b/sw/qa/core/header_footer/HeaderFooterTest.cxx
index 8b78363..b411632 100644
--- a/sw/qa/core/header_footer/HeaderFooterTest.cxx
+++ b/sw/qa/core/header_footer/HeaderFooterTest.cxx
@@ -48,6 +48,46 @@ public:
    }
};

CPPUNIT_TEST_FIXTURE(HeaderFooterTest, testStashedHeaderFooter)
{
    createSwDoc();
    SwDoc* pSourceDocument = getSwDoc();
    uno::Reference<lang::XComponent> xSourceDocument = mxComponent;
    mxComponent.clear();

    createSwDoc();
    SwDoc* pTargetDocument = getSwDoc();
    uno::Reference<lang::XComponent> xTargetDocument = mxComponent;
    mxComponent.clear();

    // Source
    SwPageDesc* pSourcePageDesc = pSourceDocument->MakePageDesc("SourceStyle");
    pSourcePageDesc->ChgFirstShare(false);
    CPPUNIT_ASSERT(!pSourcePageDesc->IsFirstShared());
    pSourcePageDesc->StashFrameFormat(pSourcePageDesc->GetFirstMaster(), true, false, true);
    pSourceDocument->ChgPageDesc("SourceStyle", *pSourcePageDesc);
    CPPUNIT_ASSERT(pSourcePageDesc->HasStashedFormat(true, false, true));

    // Target
    SwPageDesc* pTargetPageDesc = pTargetDocument->MakePageDesc("TargetStyle");

    // Copy source to target
    pTargetDocument->CopyPageDesc(*pSourcePageDesc, *pTargetPageDesc);

    // Check the stashed frame format is copied
    CPPUNIT_ASSERT(pTargetPageDesc->HasStashedFormat(true, false, true));

    // Check document instance
    auto pSourceStashedFormat = pSourcePageDesc->GetStashedFrameFormat(true, false, true);
    CPPUNIT_ASSERT_EQUAL(true, pSourceStashedFormat->GetDoc() == pSourceDocument);

    auto pTargetStashedFormat = pTargetPageDesc->GetStashedFrameFormat(true, false, true);
    CPPUNIT_ASSERT_EQUAL(true, pTargetStashedFormat->GetDoc() == pTargetDocument);

    xSourceDocument->dispose();
    xTargetDocument->dispose();
}

CPPUNIT_TEST_FIXTURE(HeaderFooterTest, testNonFirstHeaderIsDisabled)
{
    // related to tdf#127778
diff --git a/sw/source/core/doc/docfmt.cxx b/sw/source/core/doc/docfmt.cxx
index d5854b3..57c42c5 100644
--- a/sw/source/core/doc/docfmt.cxx
+++ b/sw/source/core/doc/docfmt.cxx
@@ -1540,14 +1540,43 @@ void SwDoc::CopyPageDesc( const SwPageDesc& rSrcDesc, SwPageDesc& rDstDesc,

    // Copy the stashed formats as well between the page descriptors...
    for (bool bFirst : { true, false })
    {
        for (bool bLeft : { true, false })
        {
            for (bool bHeader : { true, false })
            {
                if (!bLeft && !bFirst)
                    continue;
                if (auto pStashedFormat = rSrcDesc.GetStashedFrameFormat(bHeader, bLeft, bFirst))
                    rDstDesc.StashFrameFormat(*pStashedFormat, bHeader, bLeft, bFirst);

                // Copy format only if it exists
                if (auto pStashedFormatSrc = rSrcDesc.GetStashedFrameFormat(bHeader, bLeft, bFirst))
                {
                    if (pStashedFormatSrc->GetDoc() != this)
                    {
                        SwFrameFormat* pNewFormat = new SwFrameFormat(GetAttrPool(), "CopyDesc", GetDfltFrameFormat());

                        SfxItemSet aAttrSet(pStashedFormatSrc->GetAttrSet());
                        aAttrSet.ClearItem(RES_HEADER);
                        aAttrSet.ClearItem(RES_FOOTER);

                        pNewFormat->DelDiffs( aAttrSet );
                        pNewFormat->SetFormatAttr( aAttrSet );

                        if (bHeader)
                            CopyHeader(*pStashedFormatSrc, *pNewFormat);
                        else
                            CopyFooter(*pStashedFormatSrc, *pNewFormat);

                        rDstDesc.StashFrameFormat(*pNewFormat, bHeader, bLeft, bFirst);
                    }
                    else
                    {
                        rDstDesc.StashFrameFormat(*pStashedFormatSrc, bHeader, bLeft, bFirst);
                    }
                }
            }
        }
    }
}

void SwDoc::ReplaceStyles( const SwDoc& rSource, bool bIncludePageStyles )
diff --git a/sw/source/core/layout/pagedesc.cxx b/sw/source/core/layout/pagedesc.cxx
index edfee20..2b78823 100644
--- a/sw/source/core/layout/pagedesc.cxx
+++ b/sw/source/core/layout/pagedesc.cxx
@@ -477,7 +477,7 @@ const SwFrameFormat* SwPageDesc::GetStashedFrameFormat(bool bHeader, bool bLeft,
    }
}

bool SwPageDesc::HasStashedFormat(bool bHeader, bool bLeft, bool bFirst)
bool SwPageDesc::HasStashedFormat(bool bHeader, bool bLeft, bool bFirst) const
{
    if (bHeader)
    {