tdf#155951 sw: fix crash using XTextRange::getString() in selectionChanged()
XTextRange::getString() triggered selection change event,
resulting infinite recursion, when getString() used
in selectionChanged() of the listener.
Steps to reproduce (used by the unit test, too):
Add a XSelectionChangeListener to the document with a
selectionChanged() calling the getString() of the selected
text range. Select a word in the document editor using
the Ctrl-Shift-arrow keys.
Change-Id: I87a0f60cee3663f5303d6eb6980058ccdcc373e1
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/153356
Tested-by: Jenkins
Reviewed-by: László Németh <nemeth@numbertext.org>
(cherry picked from commit 0534715608aad7cc68f83ad4b72d8be0a35d0d6f)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/153345
diff --git a/sw/qa/core/unocore/unocore.cxx b/sw/qa/core/unocore/unocore.cxx
index a1e931e..199da2e 100644
--- a/sw/qa/core/unocore/unocore.cxx
+++ b/sw/qa/core/unocore/unocore.cxx
@@ -15,6 +15,8 @@
#include <com/sun/star/text/XTextViewCursorSupplier.hpp>
#include <com/sun/star/text/XDependentTextField.hpp>
#include <com/sun/star/document/XDocumentInsertable.hpp>
#include <com/sun/star/view/XSelectionSupplier.hpp>
#include <com/sun/star/text/XTextViewCursorSupplier.hpp>
#include <comphelper/propertyvalue.hxx>
#include <comphelper/sequenceashashmap.hxx>
@@ -912,6 +914,57 @@ CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testConvertToTextFrame)
CPPUNIT_ASSERT_EQUAL(aPaM.GetPoint()->nNode, aFrame3Anchor);
}
namespace
{
/// This selection listener calls XTextRange::getString() on a selection change, which triggered
/// a new selection change event by accident, resulting infinite recursion and crash
struct SelectionChangeListener : public cppu::WeakImplHelper<view::XSelectionChangeListener>
{
public:
SelectionChangeListener();
// view::XSelectionChangeListener
void SAL_CALL selectionChanged(const lang::EventObject& rEvent) override;
// lang::XEventListener
void SAL_CALL disposing(const lang::EventObject& rSource) override;
};
}
SelectionChangeListener::SelectionChangeListener() {}
void SelectionChangeListener::selectionChanged(const lang::EventObject& rEvent)
{
uno::Reference<view::XSelectionSupplier> xSelectionSupplier(rEvent.Source, uno::UNO_QUERY);
css::uno::Reference<css::container::XIndexAccess> xSelection(xSelectionSupplier->getSelection(),
css::uno::UNO_QUERY_THROW);
CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSelection->getCount());
css::uno::Reference<css::text::XTextRange> xTextRange(xSelection->getByIndex(0),
css::uno::UNO_QUERY_THROW);
CPPUNIT_ASSERT(xTextRange->getString().startsWith("test"));
}
void SelectionChangeListener::disposing(const lang::EventObject& /*rSource*/) {}
CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testTdf155951)
{
createSwDoc();
uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
uno::Reference<text::XText> xText = xTextDocument->getText();
uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
xText->insertString(xCursor, "test", /*bAbsorb=*/false);
uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
uno::Reference<view::XSelectionSupplier> xController(xModel->getCurrentController(),
uno::UNO_QUERY);
xController->addSelectionChangeListener(new SelectionChangeListener());
// This crashed here because of infinite recursion
dispatchCommand(mxComponent, ".uno:WordLeftSel", {});
// this needs to wait for dispatching (trigger also a second selection change)
xText->insertString(xCursor, "test", /*bAbsorb=*/false);
}
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unoobj.cxx b/sw/source/core/unocore/unoobj.cxx
index 2e65cf9..ccbcc53 100644
--- a/sw/source/core/unocore/unoobj.cxx
+++ b/sw/source/core/unocore/unoobj.cxx
@@ -74,6 +74,8 @@
#include <comphelper/propertyvalue.hxx>
#include <comphelper/servicehelper.hxx>
#include <comphelper/profilezone.hxx>
#include <comphelper/flagguard.hxx>
#include <swmodule.hxx>
using namespace ::com::sun::star;
@@ -158,6 +160,11 @@ void SwUnoCursorHelper::GetTextFromPam(SwPaM & rPam, OUString & rBuffer,
const bool bOldShowProgress = xWrt->m_bShowProgress;
xWrt->m_bShowProgress = false;
xWrt->m_bHideDeleteRedlines = pLayout && pLayout->IsHideRedlines();
// tdf#155951 SwWriter::Write calls EndAllAction, and that
// called SelectShell(), triggering selection change event, which
// resulted infinite recursion, if selectionChanged() calls
// XTextRange::getString() e.g. on the selected range.
::comphelper::FlagRestorationGuard g(g_bNoInterrupt, true);
if( ! aWriter.Write( xWrt ).IsError() )
{