sw/qa/extras/uiwriter/data/tdf158459_tracked_changes_across_nodes.fodt |   45 
++++++++++
 sw/qa/extras/uiwriter/uiwriter8.cxx                                    |   27 
++++++
 sw/source/core/doc/DocumentContentOperationsManager.cxx                |    9 
+-
 3 files changed, 79 insertions(+), 2 deletions(-)

New commits:
commit d8713e13706ceb8046d1b5adf58a817ffbad72f8
Author:     Mike Kaganski <[email protected]>
AuthorDate: Thu Nov 30 17:53:26 2023 +0300
Commit:     Miklos Vajna <[email protected]>
CommitDate: Fri Dec 1 08:16:15 2023 +0100

    tdf#158459: call DeleteAndJoin in strict reverse order
    
    The crash was caused by removal of a paragraph break (pointed to by a
    PaM returned by pDelPam->GetNext()), that resulted in merge of the node
    pDelPam pointed to, with the previous node. The destructin of the node
    made pDelPam dangle. In debug builds, an assertion failed in destructor
    of SwContentIndexReg. I.e., since the code always called DeleteAndJoin
    on pDelPam->GetNext(), the processing went from second-to-last deletion
    towards the begin of the document, and the last deletion (represented
    by pDelPam) was handled last, when GetNext call wrapped.
    
    This change makes sure that the order is strict, from last to first.
    
    Change-Id: I5cb7fe2f06d4138d3c445eeca8220f0a87a82626
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160158
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <[email protected]>
    (cherry picked from commit 9c22a72a2fc92146d24c6b673d9c07b1e6ff83f2)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160048
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git 
a/sw/qa/extras/uiwriter/data/tdf158459_tracked_changes_across_nodes.fodt 
b/sw/qa/extras/uiwriter/data/tdf158459_tracked_changes_across_nodes.fodt
new file mode 100644
index 000000000000..bdd03bc0be70
--- /dev/null
+++ b/sw/qa/extras/uiwriter/data/tdf158459_tracked_changes_across_nodes.fodt
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document 
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 
xmlns:dc="http://purl.org/dc/elements/1.1/"; 
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" 
office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:body>
+  <office:text>
+   <text:tracked-changes text:track-changes="false">
+    <text:changed-region xml:id="ct1" text:id="ct1">
+     <text:deletion>
+      <office:change-info>
+       <dc:creator>x</dc:creator>
+       <dc:date>2000-01-01</dc:date>
+      </office:change-info>
+     </text:deletion>
+    </text:changed-region>
+    <text:changed-region xml:id="ct2" text:id="ct2">
+     <text:insertion>
+      <office:change-info>
+       <dc:creator>x</dc:creator>
+       <dc:date>2000-01-02</dc:date>
+      </office:change-info>
+     </text:insertion>
+    </text:changed-region>
+    <text:changed-region xml:id="ct3" text:id="ct3">
+     <text:deletion>
+      <office:change-info>
+       <dc:creator>x</dc:creator>
+       <dc:date>2000-01-03</dc:date>
+      </office:change-info>
+     </text:deletion>
+    </text:changed-region>
+    <text:changed-region xml:id="ct4" text:id="ct4">
+     <text:deletion>
+      <office:change-info>
+       <dc:creator>x</dc:creator>
+       <dc:date>2000-01-04</dc:date>
+      </office:change-info>
+     </text:deletion>
+    </text:changed-region>
+   </text:tracked-changes>
+   <text:p/>
+   <text:p>a<text:change-start text:change-id="ct1"/></text:p>
+   <text:p><text:change-end text:change-id="ct1"/><text:change-start 
text:change-id="ct2"/>b<text:change-end 
text:change-id="ct2"/><text:change-start 
text:change-id="ct3"/>c<text:change-end 
text:change-id="ct3"/>d<text:change-start 
text:change-id="ct4"/>e<text:change-end text:change-id="ct4"/>f</text:p>
+  </office:text>
+ </office:body>
+</office:document>
\ No newline at end of file
diff --git a/sw/qa/extras/uiwriter/uiwriter8.cxx 
b/sw/qa/extras/uiwriter/uiwriter8.cxx
index cedac55734c8..f35aeb0cf6ef 100644
--- a/sw/qa/extras/uiwriter/uiwriter8.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter8.cxx
@@ -2636,6 +2636,33 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest8, testTdf73483)
     assertXPath(pXml, para_style_path, "master-page-name", "Right_20_Page");
 }
 
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest8, testTdf158459)
+{
+    createSwDoc("tdf158459_tracked_changes_across_nodes.fodt");
+    SwDoc* pDoc = getSwDoc();
+
+    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+    CPPUNIT_ASSERT(pWrtShell);
+    pWrtShell->FwdPara(); // Skip first paragraph
+    pWrtShell->EndOfSection(true); // Select everything to the end
+
+    SwDoc aClipboard;
+    pWrtShell->Copy(aClipboard); // This must not crash
+
+    pWrtShell->SelAll();
+    pWrtShell->Delete();
+    pWrtShell->Paste(aClipboard); // Replace everything with the copied stuff
+
+    SwNodes& rNodes = pDoc->GetNodes();
+    SwNodeIndex aIdx(rNodes.GetEndOfExtras());
+    SwContentNode* pContentNode = rNodes.GoNext(&aIdx);
+    CPPUNIT_ASSERT(pContentNode);
+    SwTextNode* pTextNode = pContentNode->GetTextNode();
+    CPPUNIT_ASSERT(pTextNode);
+    // Check that deleted parts (paragraph break, "c", "e") haven't been pasted
+    CPPUNIT_ASSERT_EQUAL(OUString("abdf"), pTextNode->GetText());
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentContentOperationsManager.cxx 
b/sw/source/core/doc/DocumentContentOperationsManager.cxx
index e2ba9748c503..4922c802b501 100644
--- a/sw/source/core/doc/DocumentContentOperationsManager.cxx
+++ b/sw/source/core/doc/DocumentContentOperationsManager.cxx
@@ -440,11 +440,16 @@ namespace
 
         ::sw::UndoGuard const undoGuard(rDestDoc.GetIDocumentUndoRedo());
 
+        // At this point, pDelPam points to the last of maybe several disjoint 
selections, organized
+        // in reverse order in document (so every GetNext() returns a PaM 
closer to document start,
+        // until wrap to pDelPam). Removal of the selections must be from last 
in document to first,
+        // to avoid situations when another PaM in chain points into the node 
that will be destroyed
+        // (joined to previous) by removal of the currently processed PaM.
         do {
-            rDestDoc.getIDocumentContentOperations().DeleteAndJoin( 
*pDelPam->GetNext() );
+            rDestDoc.getIDocumentContentOperations().DeleteAndJoin(*pDelPam);
             if( !pDelPam->IsMultiSelection() )
                 break;
-            delete pDelPam->GetNext();
+            pDelPam.reset(pDelPam->GetNext());
         } while( true );
 
         rDestDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );

Reply via email to