tdf#158913 secure Primitive 'visit' using mutex

See text in tdf task for detailed description.

Change-Id: I4677c9f2ecfdcb760d05fe916a2aa2dd25ad40b6
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/161543
Tested-by: Jenkins
Reviewed-by: Armin Le Grand <Armin.Le.Grand@me.com>
diff --git a/drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx b/drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx
index 96fd72e..a24816a 100644
--- a/drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx
+++ b/drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx
@@ -70,32 +70,38 @@ BufferedDecompositionGroupPrimitive2D::getBuffered2DDecomposition() const

void BufferedDecompositionGroupPrimitive2D::setBuffered2DDecomposition(Primitive2DContainer&& rNew)
{
    if (0 != maCallbackSeconds)
    if (0 == maCallbackSeconds)
    {
        if (maCallbackTimer.is())
        {
            if (rNew.empty())
            {
                // stop timer
                maCallbackTimer->stop();
            }
            else
            {
                // decomposition changed, touch
                maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
                if (!maCallbackTimer->isTicking())
                    maCallbackTimer->start();
            }
        }
        else if (!rNew.empty())
        {
            // decomposition defined/set/changed, init & start timer
            maCallbackTimer.set(new LocalCallbackTimer(*this));
            maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
            maCallbackTimer->start();
        }
        // no flush used, just set
        maBuffered2DDecomposition = std::move(rNew);
        return;
    }

    if (maCallbackTimer.is())
    {
        if (rNew.empty())
        {
            // stop timer
            maCallbackTimer->stop();
        }
        else
        {
            // decomposition changed, touch
            maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
            if (!maCallbackTimer->isTicking())
                maCallbackTimer->start();
        }
    }
    else if (!rNew.empty())
    {
        // decomposition defined/set/changed, init & start timer
        maCallbackTimer.set(new LocalCallbackTimer(*this));
        maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
        maCallbackTimer->start();
    }

    // tdf#158913 need to secure change when flush/multithreading is in use
    std::lock_guard Guard(maCallbackLock);
    maBuffered2DDecomposition = std::move(rNew);
}

@@ -103,6 +109,7 @@ BufferedDecompositionGroupPrimitive2D::BufferedDecompositionGroupPrimitive2D(
    Primitive2DContainer&& aChildren)
    : GroupPrimitive2D(std::move(aChildren))
    , maCallbackTimer()
    , maCallbackLock()
    , maCallbackSeconds(0)
{
}
@@ -129,6 +136,17 @@ void BufferedDecompositionGroupPrimitive2D::get2DDecomposition(
            std::move(aNewSequence));
    }

    if (0 == maCallbackSeconds)
    {
        // no flush/multithreading is in use, just call
        rVisitor.visit(getBuffered2DDecomposition());
        return;
    }

    // tdf#158913 need to secure 'visit' when flush/multithreading is in use,
    // so that the local non-ref-Counted instance of the decomposition gets not
    // manipulated (e.g. deleted)
    std::lock_guard Guard(maCallbackLock);
    rVisitor.visit(getBuffered2DDecomposition());
}

diff --git a/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx b/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx
index c7f3b78..22b6450e 100644
--- a/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx
+++ b/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx
@@ -69,38 +69,45 @@ const Primitive2DContainer& BufferedDecompositionPrimitive2D::getBuffered2DDecom

void BufferedDecompositionPrimitive2D::setBuffered2DDecomposition(Primitive2DContainer&& rNew)
{
    if (0 != maCallbackSeconds)
    if (0 == maCallbackSeconds)
    {
        if (maCallbackTimer.is())
        {
            if (rNew.empty())
            {
                // stop timer
                maCallbackTimer->stop();
            }
            else
            {
                // decomposition changed, touch
                maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
                if (!maCallbackTimer->isTicking())
                    maCallbackTimer->start();
            }
        }
        else if (!rNew.empty())
        {
            // decomposition defined/set/changed, init & start timer
            maCallbackTimer.set(new LocalCallbackTimer(*this));
            maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
            maCallbackTimer->start();
        }
        // no flush used, just set
        maBuffered2DDecomposition = std::move(rNew);
        return;
    }

    if (maCallbackTimer.is())
    {
        if (rNew.empty())
        {
            // stop timer
            maCallbackTimer->stop();
        }
        else
        {
            // decomposition changed, touch
            maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
            if (!maCallbackTimer->isTicking())
                maCallbackTimer->start();
        }
    }
    else if (!rNew.empty())
    {
        // decomposition defined/set/changed, init & start timer
        maCallbackTimer.set(new LocalCallbackTimer(*this));
        maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
        maCallbackTimer->start();
    }

    // tdf#158913 need to secure change when flush/multithreading is in use
    std::lock_guard Guard(maCallbackLock);
    maBuffered2DDecomposition = std::move(rNew);
}

BufferedDecompositionPrimitive2D::BufferedDecompositionPrimitive2D()
    : maBuffered2DDecomposition()
    , maCallbackTimer()
    , maCallbackLock()
    , maCallbackSeconds(0)
    , mnTransparenceForShadow(0)
{
@@ -128,6 +135,17 @@ void BufferedDecompositionPrimitive2D::get2DDecomposition(
            std::move(aNewSequence));
    }

    if (0 == maCallbackSeconds)
    {
        // no flush/multithreading is in use, just call
        rVisitor.visit(getBuffered2DDecomposition());
        return;
    }

    // tdf#158913 need to secure 'visit' when flush/multithreading is in use,
    // so that the local non-ref-Counted instance of the decomposition gets not
    // manipulated (e.g. deleted)
    std::lock_guard Guard(maCallbackLock);
    rVisitor.visit(getBuffered2DDecomposition());
}

diff --git a/include/drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx b/include/drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx
index 031bd36..778c52c 100644
--- a/include/drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx
+++ b/include/drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx
@@ -44,6 +44,7 @@ private:

    /// offer callback mechanism to flush buffered content timer-based
    ::rtl::Reference<::salhelper::Timer> maCallbackTimer;
    mutable std::mutex maCallbackLock;
    sal_uInt16 maCallbackSeconds;

protected:
diff --git a/include/drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx b/include/drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx
index 55ba8866..cc36ddb 100644
--- a/include/drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx
+++ b/include/drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx
@@ -71,6 +71,7 @@ private:

    /// offer callback mechanism to flush buffered content timer-based
    ::rtl::Reference<::salhelper::Timer> maCallbackTimer;
    mutable std::mutex maCallbackLock;
    sal_uInt16 maCallbackSeconds;

    /// When a shadow wraps a list of primitives, this primitive wants to influence the transparency