editeng/inc/editdoc.hxx                      |    3 
 editeng/source/editeng/editdoc.cxx           |   95 +++++++++++++++++++++++++--
 editeng/source/editeng/editview.cxx          |    7 +
 include/editeng/editview.hxx                 |    2 
 include/editeng/yrs.hxx                      |    1 
 include/sal/log-areas.dox                    |    3 
 sfx2/source/view/frmload.cxx                 |    4 -
 sw/source/core/crsr/viscrs.cxx               |    2 
 sw/source/core/doc/DocumentStateManager.cxx  |   79 ++++++++++++++++------
 sw/source/uibase/docvw/PostItMgr.cxx         |    2 
 sw/source/uibase/docvw/SidebarTxtControl.cxx |    4 -
 11 files changed, 171 insertions(+), 31 deletions(-)

New commits:
commit 4727305dd2e6fa1e92408d9fd7fef70b8d1fb913
Author:     Michael Stahl <[email protected]>
AuthorDate: Mon May 12 09:20:29 2025 +0200
Commit:     Michael Stahl <[email protected]>
CommitDate: Tue May 13 10:00:31 2025 +0200

    LOCRCT editeng,sfx2,sw: replace temporary SAL_DEBUG
    
    Change-Id: Ib6f3b732a7eb6f2dbf423ffd2206b0474ff6c681
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/185200
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <[email protected]>

diff --git a/editeng/source/editeng/editdoc.cxx 
b/editeng/source/editeng/editdoc.cxx
index 86a523ca9f6a..7073723bda16 100644
--- a/editeng/source/editeng/editdoc.cxx
+++ b/editeng/source/editeng/editdoc.cxx
@@ -1281,7 +1281,7 @@ void YrsAddPara(IYrsTransactionSupplier *const 
pYrsSupplier,
     {
         return;
     }
-    SAL_DEBUG("YRS YrsAddPara");
+    SAL_INFO("editeng.yrs", "YRS YrsAddPara");
     // need to encode into 1 YText
     char const para[]{ CH_PARA, '
     uint32_t i{0};
@@ -3146,7 +3146,7 @@ EditDoc::EditDoc( SfxItemPool* pPool ) :
     mbDisableAttributeExpanding(false)
 {
 #if defined(YRS)
-    SAL_DEBUG("YRS +EditDoc");
+    SAL_INFO("editeng.yrs", "YRS +EditDoc");
 #endif
     // Don't create an empty node, Clear() will be called in EditEngine-CTOR
 };
@@ -3154,7 +3154,7 @@ EditDoc::EditDoc( SfxItemPool* pPool ) :
 EditDoc::~EditDoc()
 {
 #if defined(YRS)
-    SAL_DEBUG("YRS -EditDoc");
+    SAL_INFO("editeng.yrs", "YRS -EditDoc");
 #endif
     maContents.clear();
 }
diff --git a/editeng/source/editeng/editview.cxx 
b/editeng/source/editeng/editview.cxx
index 982b17d3ab30..4a59be87ae2b 100644
--- a/editeng/source/editeng/editview.cxx
+++ b/editeng/source/editeng/editview.cxx
@@ -384,7 +384,7 @@ void EditView::YrsApplyEECursor(OString const& rPeerId, 
OUString const& rAuthor,
     ::std::optional<EditSelection> const 
oSel{getEditEngine().GetEditDoc().YrsReadEECursor(point, oMark)};
     if (!oSel)
     {
-        SAL_DEBUG("YRS ignoring invalid cursor position");
+        SAL_INFO("editeng.yrs", "YRS ignoring invalid cursor position");
         return;
     }
     ESelection const esel{getEditEngine().getImpl().CreateESel(*oSel)};
diff --git a/include/sal/log-areas.dox b/include/sal/log-areas.dox
index 425edb201d41..7b4fc359997a 100644
--- a/include/sal/log-areas.dox
+++ b/include/sal/log-areas.dox
@@ -199,6 +199,7 @@ certain functionality.
 @li @c editeng.chaining
 @li @c editeng.items
 @li @c editeng.quicktextsize
+@li @c editeng.yrs
 
 @section embeddedobj
 
@@ -373,6 +374,7 @@ certain functionality.
 @li @c sfx.notify
 @li @c sfx.sidebar
 @li @c sfx.view
+@li @c sfx.yrs
 
 @section slideshow
 
@@ -566,6 +568,7 @@ certain functionality.
 @li @c sw.ww8 - .doc/.docx export filter, .doc import filter (not writerfilter)
 @li @c sw.ww8.level2 - further info for sw.ww8
 @li @c sw.xml - Writer .odt import/export
+@li @c sw.yrs
 
 @section writerfilter
 
diff --git a/sfx2/source/view/frmload.cxx b/sfx2/source/view/frmload.cxx
index 80615abe165f..16137f020a23 100644
--- a/sfx2/source/view/frmload.cxx
+++ b/sfx2/source/view/frmload.cxx
@@ -635,7 +635,7 @@ sal_Bool SAL_CALL SfxFrameLoader_Impl::load( const 
Sequence< PropertyValue >& rA
     uno::Reference<connection::XConnection> xConnection;
     if (!xModel.is() && aDescriptor.getOrDefault(u"URL"_ustr, OUString()) == 
"private:factory/swriter" && !getenv("YRSACCEPT"))
     {
-        SAL_DEBUG("YRS connect sfx2");
+        SAL_INFO("sfx.yrs", "YRS connect sfx2");
 
         // must read this SYNC
         auto const conn = u"pipe,name=ytest"_ustr;
@@ -652,7 +652,7 @@ sal_Bool SAL_CALL SfxFrameLoader_Impl::load( const 
Sequence< PropertyValue >& rA
                 | static_cast<sal_uInt8>(buf[3]) << 24};
         if (size != 0)
         {
-            SAL_DEBUG("YRS connect reading file of size " << size);
+            SAL_INFO("sfx.yrs", "YRS connect reading file of size " << size);
             uno::Sequence<sal_Int8> buff(size);
             if (xConnection->read(buff, size) != size)
             {
diff --git a/sw/source/core/crsr/viscrs.cxx b/sw/source/core/crsr/viscrs.cxx
index 2e640750dcb3..10acc7ab7c64 100644
--- a/sw/source/core/crsr/viscrs.cxx
+++ b/sw/source/core/crsr/viscrs.cxx
@@ -977,7 +977,7 @@ void SwShellCursor::FillRects()
             {
                 cursorRect.Width(20);
             }
-            SAL_DEBUG("YRS FillRects extra rect " << cursorRect);
+            SAL_INFO("sw.yrs", "YRS FillRects extra rect " << cursorRect);
             emplace_back(cursorRect);
             SwRect const temp{Point{cursorRect.Left() - cursorRect.Height()/2,
                 cursorRect.Bottom()}, Size{cursorRect.Height() + 20, 20}};
diff --git a/sw/source/core/doc/DocumentStateManager.cxx 
b/sw/source/core/doc/DocumentStateManager.cxx
index 323b82a82440..0e883ab21ebd 100644
--- a/sw/source/core/doc/DocumentStateManager.cxx
+++ b/sw/source/core/doc/DocumentStateManager.cxx
@@ -214,10 +214,10 @@ public:
                 | static_cast<sal_uInt8>(buf[3]) << 24};
             if (size == 0)
             {
-                SAL_DEBUG("YRS 0");
+                SAL_INFO("sw.yrs", "YRS 0");
                 break;
             }
-            SAL_DEBUG("YRS receive " << size);
+            SAL_INFO("sw.yrs", "YRS receive " << size);
             ::std::unique_ptr<uno::Sequence<sal_Int8>> pBuf{new 
uno::Sequence<sal_Int8>(size)};
             m_xConnection->read(*pBuf, size);
             Application::PostUserEvent(LINK(this, YrsThread, HandleMessage), 
pBuf.release());
@@ -238,7 +238,7 @@ struct ObserveState
 
 extern "C" void observe_comments(void *const pState, uint32_t count, YEvent 
const*const events)
 {
-    SAL_DEBUG("YRS observe_comments");
+    SAL_INFO("sw.yrs", "YRS observe_comments");
     ObserveState & rState{*static_cast<ObserveState*>(pState)};
     // DO NOT call rState.rYrsSupplier.GetWriteTransaction()!
     YTransaction *const pTxn{rState.pTxn};
@@ -566,7 +566,7 @@ extern "C" void observe_comments(void *const pState, 
uint32_t count, YEvent cons
         yvalidate(rNode.IsTextNode());
         yvalidate(it.first.second <= 
o3tl::make_unsigned(rNode.GetTextNode()->Len()));
         SwPosition anchorPos{*rNode.GetTextNode(), 
static_cast<sal_Int32>(it.first.second)};
-        SAL_DEBUG("YRS " << anchorPos);
+        SAL_INFO("sw.yrs", "YRS " << anchorPos);
         SwWrtShell *const 
pShell{dynamic_cast<SwWrtShell*>(rState.rDoc.getIDocumentLayoutAccess().GetCurrentViewShell())};
         SwPostItFieldType* pType = 
static_cast<SwPostItFieldType*>(pShell->GetFieldType(0, SwFieldIds::Postit));
         auto pField{
@@ -824,7 +824,7 @@ void YrsReadCursor(ObserveCursorState & rState, OString 
const& rPeerId,
 
 extern "C" void observe_cursors(void *const pState, uint32_t count, YEvent 
const*const events)
 {
-    SAL_DEBUG("YRS observe_cursors");
+    SAL_INFO("sw.yrs", "YRS observe_cursors");
     // note: it (very rarely) happens that observe_cursors will be called
     // when a cursor is moved into a comment that is newly inserted, but
     // observe_comments hasn't been called to actually insert the comment yet
@@ -952,11 +952,11 @@ void writeLength(sal_Int8 *& rpBuf, sal_Int32 const len)
 
 IMPL_LINK(YrsThread, HandleMessage, void*, pVoid, void)
 {
-    SAL_DEBUG("YRS HandleMessage");
+    SAL_INFO("sw.yrs", "YRS HandleMessage");
     DBG_TESTSOLARMUTEX();
     if (!m_pDSM)
     {
-        SAL_DEBUG("m_pDSM died");
+        SAL_INFO("sw.yrs", "m_pDSM died");
         return;
     }
     // wrap this, not strictly needed but can't hurt?
@@ -968,7 +968,7 @@ IMPL_LINK(YrsThread, HandleMessage, void*, pVoid, void)
     {
         case ::std::underlying_type_t<Message>(Message::RequestStateVector):
         {
-            SAL_DEBUG("sending state vector");
+            SAL_INFO("sw.yrs", "sending state vector");
             YTransaction *const 
pTxn{m_pDSM->m_pYrsSupplier->GetWriteTransaction()};
             uint32_t len{0};
             char * pSV = ytransaction_state_vector_v1(pTxn, &len);
@@ -992,7 +992,7 @@ IMPL_LINK(YrsThread, HandleMessage, void*, pVoid, void)
         }
         case ::std::underlying_type_t<Message>(Message::SendStateVector):
         {
-            SAL_DEBUG("received state vector");
+            SAL_INFO("sw.yrs", "received state vector");
             YTransaction *const 
pTxn{m_pDSM->m_pYrsSupplier->GetWriteTransaction()};
             uint32_t len{0};
             char * pUpdate = ytransaction_state_diff_v1(pTxn,
@@ -1017,13 +1017,13 @@ IMPL_LINK(YrsThread, HandleMessage, void*, pVoid, void)
         }
         case ::std::underlying_type_t<Message>(Message::SendStateDiff):
         {
-            SAL_DEBUG("apply update: " << 
yupdate_debug_v1(reinterpret_cast<char const*>(pBuf->begin()) + 1, length - 1));
+            SAL_INFO("sw.yrs", "apply update: " << 
yupdate_debug_v1(reinterpret_cast<char const*>(pBuf->begin()) + 1, length - 1));
             YTransaction *const 
pTxn{m_pDSM->m_pYrsSupplier->GetWriteTransaction()};
             
m_pDSM->m_pYrsSupplier->SetMode(IYrsTransactionSupplier::Mode::Replay);
             auto const err = ytransaction_apply(pTxn, reinterpret_cast<char 
const*>(pBuf->begin()) + 1, length - 1);
             if (err != 0)
             {
-                SAL_DEBUG("ytransaction_apply error " << err);
+                SAL_INFO("sw.yrs", "ytransaction_apply error " << err);
                 abort();
             }
             // let's have one observe_deep instead of a observe on every
@@ -1066,7 +1066,7 @@ void DocumentStateManager::YrsNotifySetResolved(OString 
const& rCommentId, SwPos
 
 void DocumentStateManager::YrsAddCommentImpl(SwPosition const& rAnchorPos, 
OString const& commentId)
 {
-    SAL_DEBUG("YRS AddCommentImpl");
+    SAL_INFO("sw.yrs", "YRS AddCommentImpl");
     ::std::vector<SwAnnotationItem *> items;
     // ??? TODO how should this work for multiple viewshells? every shell has 
its own EditEngine? unclear.
     for (SwViewShell & rShell : 
m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell()->GetRingContainer())
@@ -1092,7 +1092,7 @@ void DocumentStateManager::YrsAddComment(SwPosition 
const& rPos,
     ::std::optional<SwPosition> const oAnchorStart, SwPostItField const& 
rField,
     bool const isInsert)
 {
-    SAL_DEBUG("YRS AddComment " << rPos);
+    SAL_INFO("sw.yrs", "YRS AddComment " << rPos);
     OString const commentId{m_pYrsSupplier->GenNewCommentId()};
     // this calls EditViewInvalidate so prevent destroying pTxn
     YrsAddCommentImpl(rPos, commentId);
@@ -1199,7 +1199,7 @@ void DocumentStateManager::YrsRemoveCommentImpl(OString 
const& rCommentId)
 
 void DocumentStateManager::YrsRemoveComment(SwPosition const& rPos, OString 
const& rCommentId)
 {
-    SAL_DEBUG("YRS RemoveComment");
+    SAL_INFO("sw.yrs", "YRS RemoveComment");
     YrsRemoveCommentImpl(rCommentId);
     YTransaction *const pTxn{m_pYrsSupplier->GetWriteTransaction()};
     if (!pTxn)
@@ -1270,7 +1270,7 @@ void DocumentStateManager::YrsNotifyCursorUpdate()
     {
         return;
     }
-    SAL_DEBUG("YRS NotifyCursorUpdate");
+    SAL_INFO("sw.yrs", "YRS NotifyCursorUpdate");
     YDoc *const pYDoc{m_pYrsSupplier->GetYDoc()};
     auto const id{ydoc_id(pYDoc)};
     ::std::unique_ptr<YOutput, YOutputDeleter> 
pEntry{ymap_get(m_pYrsSupplier->m_pCursors, pTxn, 
OString::number(id).getStr())};
@@ -1403,7 +1403,7 @@ void DocumentStateManager::YrsInitAcceptor()
     {
         auto const conn = u"pipe,name=ytest"_ustr;
         auto const xContext{comphelper::getProcessComponentContext()};
-        SAL_DEBUG("YRS accept");
+        SAL_INFO("sw.yrs", "YRS accept");
         m_xAcceptor = css::connection::Acceptor::create(xContext);
         // TODO move to thread?
         uno::Reference<connection::XConnection> xConnection = 
m_xAcceptor->accept(conn);
@@ -1416,7 +1416,7 @@ void DocumentStateManager::YrsInitAcceptor()
             {
                 try
                 {
-                    SAL_DEBUG("YRS send file: " << url);
+                    SAL_INFO("sw.yrs", "YRS send file: " << url);
                     ::ucbhelper::Content temp{url, {}, xContext};
                     uno::Reference<io::XInputStream> const 
xInStream{temp.openStreamNoLock()};
                     uno::Reference<io::XSeekable> const xSeekable{xInStream, 
uno::UNO_QUERY};
@@ -1467,7 +1467,7 @@ void DocumentStateManager::YrsInitAcceptor()
         m_pYrsReader = new YrsThread(xConnection, *this);
         //m_xAcceptor->stopAccepting();
         m_pYrsReader->launch();
-        SAL_DEBUG("YRS started");
+        SAL_INFO("sw.yrs", "YRS started");
         // inserting comments needs sidebar wins so needs a view shell first
         SwFieldType & 
rType{*m_rDoc.getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Postit, 
OUString(), false)};
         std::vector<SwFormatField*> fields;
@@ -1521,7 +1521,7 @@ void DocumentStateManager::YrsInitConnector(uno::Any 
const& raConnection)
 
     m_pYrsReader = new YrsThread(xConnection, *this);
     m_pYrsReader->launch();
-    SAL_DEBUG("YRS started (InitConnector)");
+    SAL_INFO("sw.yrs", "YRS started (InitConnector)");
 }
 
 #endif
@@ -1570,7 +1570,7 @@ void DocumentStateManager::SetModified()
         m_rDoc.DeleteAutoCorrExceptWord();
 
 #if defined(YRS)
-    SAL_DEBUG("YRS SetModified");
+    SAL_INFO("sw.yrs", "YRS SetModified");
     YrsCommitModified();
 #endif
 }
diff --git a/sw/source/uibase/docvw/PostItMgr.cxx 
b/sw/source/uibase/docvw/PostItMgr.cxx
index 70c30cf1e6e4..dd9c68e4668c 100644
--- a/sw/source/uibase/docvw/PostItMgr.cxx
+++ b/sw/source/uibase/docvw/PostItMgr.cxx
@@ -909,7 +909,7 @@ VclPtr<SwAnnotationWin> 
SwPostItMgr::GetOrCreateAnnotationWindow(SwAnnotationIte
         pPostIt->SetReadonly(mbReadOnly);
         rItem.mpPostIt = pPostIt;
 #if defined(YRS)
-        SAL_DEBUG("YRS GetOrCreateAnnotationWindow " << rItem.mpPostIt);
+        SAL_INFO("sw.yrs", "YRS GetOrCreateAnnotationWindow " << 
rItem.mpPostIt);
 #endif
         if (mpAnswer)
         {
diff --git a/sw/source/uibase/docvw/SidebarTxtControl.cxx 
b/sw/source/uibase/docvw/SidebarTxtControl.cxx
index ebc8cde72761..a1580a1924be 100644
--- a/sw/source/uibase/docvw/SidebarTxtControl.cxx
+++ b/sw/source/uibase/docvw/SidebarTxtControl.cxx
@@ -206,7 +206,7 @@ OUString SidebarTextControl::RequestHelp(tools::Rectangle& 
rHelpRect)
 #if defined(YRS)
 void SidebarTextControl::EditViewInvalidate(const tools::Rectangle& rRect)
 {
-    SAL_DEBUG("YRS EditViewInvalidate");
+    SAL_INFO("sw.yrs", "YRS EditViewInvalidate");
     
mrDocView.GetDocShell()->GetDoc()->getIDocumentState().YrsNotifyCursorUpdate();
     mrDocView.GetDocShell()->GetDoc()->getIDocumentState().YrsCommitModified();
     return WeldEditView::EditViewInvalidate(rRect);
@@ -214,7 +214,7 @@ void SidebarTextControl::EditViewInvalidate(const 
tools::Rectangle& rRect)
 
 void SidebarTextControl::EditViewSelectionChange()
 {
-    SAL_DEBUG("YRS EditViewSelectionChange");
+    SAL_INFO("sw.yrs", "YRS EditViewSelectionChange");
     
mrDocView.GetDocShell()->GetDoc()->getIDocumentState().YrsNotifyCursorUpdate();
     return WeldEditView::EditViewSelectionChange();
 }
commit ee7ab3d4634ae916f04811d0ce033637ea22578c
Author:     Michael Stahl <[email protected]>
AuthorDate: Thu Mar 20 14:47:02 2025 +0100
Commit:     Michael Stahl <[email protected]>
CommitDate: Tue May 13 10:00:20 2025 +0200

    LOCRDT editeng,sw: yrs weak link peer cursors for editengine
    
    Replace unreliable integer cursors with WeakRef cursors which maintain
    their position automatically.
    
    This requires yrs v0.23.1 with the yweak_read() function.
    
    Unfortunately this crashes currently, so hide it behind #if YRS_WEAK.
    
    https://github.com/y-crdt/y-crdt/issues/536
    
    Change-Id: I74ec1eaa25c7cb554efd38ee632df041ac92d5c2
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/183311
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <[email protected]>

diff --git a/editeng/inc/editdoc.hxx b/editeng/inc/editdoc.hxx
index cb252ee0c877..f348f17199e1 100644
--- a/editeng/inc/editdoc.hxx
+++ b/editeng/inc/editdoc.hxx
@@ -51,6 +51,8 @@ class ImpEditEngine;
 class IYrsTransactionSupplier;
 typedef struct TransactionInner YTransaction;
 typedef struct YTextEvent YTextEvent;
+typedef struct Branch Branch;
+typedef struct YOutput YOutput;
 #endif
 
 
@@ -134,6 +136,7 @@ public:
     void YrsWriteEEState();
     void YrsReadEEState(YTransaction *, ImpEditEngine & rIEE);
     void YrsApplyEEDelta(YTransaction *, YTextEvent const* pEvent, 
ImpEditEngine & rIEE);
+    bool YrsWriteEECursor(YTransaction *, Branch const& rArray, YOutput const* 
pCurrent, EditSelection const& rSelection);
     ::std::optional<EditSelection> YrsReadEECursor(::std::pair<int64_t, 
int64_t> point,
             ::std::optional<::std::pair<int64_t, int64_t>> oMark);
     void YrsSetStyle(sal_Int32 nPara, ::std::u16string_view rStyle);
diff --git a/editeng/source/editeng/editdoc.cxx 
b/editeng/source/editeng/editdoc.cxx
index 45c89ef0dcfd..86a523ca9f6a 100644
--- a/editeng/source/editeng/editdoc.cxx
+++ b/editeng/source/editeng/editdoc.cxx
@@ -2712,12 +2712,98 @@ void EditDoc::YrsReadEEState(YTransaction *const pTxn, 
ImpEditEngine & rIEE)
     rIEE.RemoveParagraph(nodes); // remove pre-existing one from InitDoc()
 }
 
+bool EditDoc::YrsWriteEECursor(YTransaction *const pTxn, Branch const& rArray,
+        YOutput const*const pCurrent, EditSelection const& rSelection)
+{
+    EditSelection sel{rSelection};
+    sel.Adjust(*this); // unfortunately min/max naming may be misleading...
+    YrsWrite const yw{GetYrsWrite(m_pYrsSupplier, m_CommentId)};
+    uint32_t start{0};
+    for (auto paras{GetPos(sel.Min().GetNode())}; paras != 0; --paras)
+    {
+        start += GetObject(paras-1)->Len() + 1;
+    }
+    uint32_t end{start};
+    start += sel.Min().GetIndex();
+    if (sel.HasRange())
+    {
+        auto const nStartNode{GetPos(sel.Min().GetNode())};
+        for (auto paras{GetPos(sel.Max().GetNode()) - nStartNode};
+            paras != 0; --paras)
+        {
+            end += GetObject(nStartNode + paras-1)->Len() + 1;
+        }
+        end += sel.Max().GetIndex();
+    }
+    else
+    {
+        end = start;
+    }
+    if (pCurrent != nullptr && pCurrent->tag == Y_ARRAY
+        && yarray_len(pCurrent->value.y_type) == 2
+        // could do without the comment id? no, empty-text leaves pBranch null!
+        && yarray_get(pCurrent->value.y_type, pTxn, 0)->tag == Y_JSON_STR
+        && strcmp(yarray_get(pCurrent->value.y_type, pTxn, 0)->value.str, 
m_CommentId.getStr()) == 0)
+    {
+        ::std::unique_ptr<YOutput, YOutputDeleter> const 
pWeak{yarray_get(pCurrent->value.y_type, pTxn, 1)};
+        yvalidate(pWeak->tag == Y_WEAK_LINK);
+        Branch * pBranch{nullptr};
+        uint32_t oldStart{SAL_MAX_UINT32};
+        uint32_t oldEnd{SAL_MAX_UINT32};
+        yweak_read(pWeak->value.y_type, pTxn, &pBranch, &oldStart, &oldEnd);
+        assert(oldStart != SAL_MAX_UINT32 && oldEnd != SAL_MAX_UINT32);
+        if (oldStart == start && oldEnd == end)
+        {
+            return false;
+        }
+    }
+
+    Weak const*const pWeak{ytext_quote(yw.pText, pTxn, start, end, Y_FALSE, 
Y_TRUE)};
+    ::std::vector<YInput> positions;
+    positions.push_back(yinput_string(m_CommentId.getStr()));
+    positions.push_back(yinput_weak(pWeak));
+    YInput const input{yinput_yarray(positions.data(), 2)};
+    yarray_remove_range(&rArray, pTxn, 1, 1);
+    yarray_insert_range(&rArray, pTxn, 1, &input, 1);
+//    yweak_destroy(pWeak); // NO! UAF
+    return true;
+}
+
 ::std::optional<EditSelection> EditDoc::YrsReadEECursor(
     ::std::pair<int64_t, int64_t> const i_point,
     ::std::optional<::std::pair<int64_t, int64_t>> const i_oMark)
 {
-    // TODO should not use ints here?
     ::std::optional<EditPaM> oMark;
+#if defined(YRS_WEAK)
+    auto start{i_point.first}; // returned from yweak_read(), need to convert
+    ContentNode * pNode{nullptr};
+    for (decltype(Count()) i = 0; i < Count(); ++i)
+    {
+        pNode = GetObject(i);
+        if (start <= o3tl::make_unsigned(pNode->Len()))
+        {
+            break;
+        }
+        start -= pNode->Len() + 1;
+    }
+    yvalidate(start <= o3tl::make_unsigned(pNode->Len()));
+    EditPaM point{pNode, static_cast<sal_Int32>(start)};
+    if (i_oMark)
+    {
+        auto end{i_oMark->first};
+        for (decltype(Count()) i = 0; i < Count(); ++i)
+        {
+            pNode = GetObject(i);
+            if (end <= o3tl::make_unsigned(pNode->Len()))
+            {
+                break;
+            }
+            end -= pNode->Len() + 1;
+        }
+        yvalidate(end <= o3tl::make_unsigned(pNode->Len()));
+        oMark.emplace(pNode, static_cast<sal_Int32>(end));
+    }
+#else
     if (i_oMark)
     {
 //        yvalidate(i_oMark->first < Count());
@@ -2750,6 +2836,7 @@ void EditDoc::YrsReadEEState(YTransaction *const pTxn, 
ImpEditEngine & rIEE)
         return {};
     }
     EditPaM point{&rNode, static_cast<sal_Int32>(i_point.second)};
+#endif
 
     return EditSelection{point, oMark ? *oMark : point};
 }
diff --git a/editeng/source/editeng/editview.cxx 
b/editeng/source/editeng/editview.cxx
index 65152d487577..982b17d3ab30 100644
--- a/editeng/source/editeng/editview.cxx
+++ b/editeng/source/editeng/editview.cxx
@@ -372,6 +372,11 @@ void EditView::YrsGetSelectionRectangles(
     }
 }
 
+bool EditView::YrsWriteEECursor(YTransaction *const pTxn, Branch const& 
rArray, YOutput const*const pCurrent)
+{
+    return getEditEngine().GetEditDoc().YrsWriteEECursor(pTxn, rArray, 
pCurrent, getImpl().GetEditSelection());
+}
+
 void EditView::YrsApplyEECursor(OString const& rPeerId, OUString const& 
rAuthor,
     ::std::pair<int64_t, int64_t> const point,
     ::std::optional<::std::pair<int64_t, int64_t>> const oMark)
diff --git a/include/editeng/editview.hxx b/include/editeng/editview.hxx
index 4e1ee56ef7ce..d31ee35ff81e 100644
--- a/include/editeng/editview.hxx
+++ b/include/editeng/editview.hxx
@@ -42,6 +42,7 @@ class IYrsTransactionSupplier;
 typedef struct TransactionInner YTransaction;
 typedef struct YTextEvent YTextEvent;
 typedef struct Branch Branch;
+typedef struct YOutput YOutput;
 #endif
 
 class EditTextObject;
@@ -417,6 +418,7 @@ public:
     void YrsReadEEState(YTransaction *);
     void YrsApplyEEDelta(YTransaction *, YTextEvent const* pEvent);
     OString GetYrsCommentId() const;
+    bool YrsWriteEECursor(YTransaction *, Branch const& rArray, YOutput const* 
pCurrent);
     void YrsApplyEECursor(OString const& rPeerId, OUString const& rAuthor,
             ::std::pair<int64_t, int64_t> point,
             ::std::optional<::std::pair<int64_t, int64_t>> oMark);
diff --git a/include/editeng/yrs.hxx b/include/editeng/yrs.hxx
index 4842d47540c4..17f41179c3bf 100644
--- a/include/editeng/yrs.hxx
+++ b/include/editeng/yrs.hxx
@@ -9,6 +9,7 @@
 
 #pragma once
 
+#define YRS_WEAK
 extern "C" {
 #if defined(__clang__)
 #pragma clang diagnostic push
diff --git a/sw/source/core/doc/DocumentStateManager.cxx 
b/sw/source/core/doc/DocumentStateManager.cxx
index c0735e01de94..323b82a82440 100644
--- a/sw/source/core/doc/DocumentStateManager.cxx
+++ b/sw/source/core/doc/DocumentStateManager.cxx
@@ -740,7 +740,12 @@ void YrsReadCursor(ObserveCursorState & rState, OString 
const& rPeerId,
         {
             Branch const*const pArray{rCursor.value.y_type};
             auto const len{yarray_len(pArray)};
+#if defined(YRS_WEAK)
+            if (len == 2
+                && yarray_get(pArray, rState.pTxn, 0)->tag == Y_JSON_STR)
+#else
             if (len == 3 || len == 5)
+#endif
             {
                 ::std::unique_ptr<YOutput, YOutputDeleter> const 
pComment{yarray_get(pArray, rState.pTxn, 0)};
                 yvalidate(pComment->tag == Y_JSON_STR && pComment->len < 
SAL_MAX_INT32);
@@ -748,6 +753,20 @@ void YrsReadCursor(ObserveCursorState & rState, OString 
const& rPeerId,
                 YrsInvalidateEECursors(rState, rPeerId, &commentId);
                 YrsInvalidateSwCursors(rState, rPeerId, rAuthor, false);
                 ::std::optional<::std::pair<int64_t, int64_t>> oMark;
+#if defined(YRS_WEAK)
+                ::std::unique_ptr<YOutput, YOutputDeleter> const 
pWeak{yarray_get(pArray, rState.pTxn, 1)};
+                yvalidate(pWeak->tag == Y_WEAK_LINK);
+                Branch * pBranch{nullptr};
+                uint32_t start{SAL_MAX_UINT32};
+                uint32_t end{SAL_MAX_UINT32};
+                yweak_read(pWeak->value.y_type, rState.pTxn, &pBranch, &start, 
&end);
+                yvalidate(start < SAL_MAX_INT32 && end < SAL_MAX_INT32);
+                if (start != end)
+                {
+                    oMark.emplace(end, -1);
+                }
+                ::std::pair<int64_t, int64_t> const pos{start, -1};
+#else
                 if (len == 5)
                 {
                     ::std::unique_ptr<YOutput, YOutputDeleter> const pNode{
@@ -763,6 +782,7 @@ void YrsReadCursor(ObserveCursorState & rState, OString 
const& rPeerId,
                 ::std::unique_ptr<YOutput, YOutputDeleter> const 
pContent{yarray_get(pArray, rState.pTxn, 2)};
                 yvalidate(pContent->tag == Y_JSON_INT);
                 ::std::pair<int64_t, int64_t> const pos{pNode->value.integer, 
pContent->value.integer};
+#endif
                 rState.CursorUpdates.emplace_back(rPeerId, 
::std::optional<OString>{commentId}, ::std::optional<OUString>{rAuthor}, pos, 
oMark);
             }
             else if (len == 2 || len == 4)
@@ -901,6 +921,15 @@ extern "C" void observe_cursors(void *const pState, 
uint32_t count, YEvent const
                 YrsReadCursor(rState, peerId, pChange[2].values[0], author, 
false);
                 break;
             }
+#if defined(YRS_WEAK)
+            case Y_WEAK_LINK:
+            {
+                // not sure what this is, but yffi doesn't have any API
+                // for it, let's hope we can just ignore it
+//                YWeakLinkEvent const*const pEvent{&events[i].content.weak};
+                break;
+            }
+#endif
             default:
                 assert(false);
         }
@@ -1263,6 +1292,12 @@ void DocumentStateManager::YrsNotifyCursorUpdate()
             pShell->GetView().GetPostItMgr()->GetActiveSidebarWin()})
     {
         // TODO StickyIndex cannot be inserted into YDoc ?
+#if defined(YRS_WEAK)
+        if (pWin->GetOutlinerView()->GetEditView().YrsWriteEECursor(pTxn, 
*pEntry->value.y_type, pCurrent.get()))
+        {
+            YrsCommitModified();
+        }
+#else
         ESelection const sel{pWin->GetOutlinerView()->GetSelection()};
         ::std::vector<YInput> positions;
         // the ID of the comment
@@ -1301,6 +1336,7 @@ void DocumentStateManager::YrsNotifyCursorUpdate()
                 YrsCommitModified();
             }
         }
+#endif
     }
     else if (!pShell->IsStdMode()
            || pShell->GetSelectedObjCount() != 0
@@ -1342,6 +1378,9 @@ void DocumentStateManager::YrsNotifyCursorUpdate()
         {
             if (pCurrent == nullptr || pCurrent->tag != Y_ARRAY
                 || yarray_len(pCurrent->value.y_type) != 2
+#if defined(YRS_WEAK)
+                || yarray_get(pCurrent->value.y_type, pTxn, 0)->tag != 
Y_JSON_INT
+#endif
                 || yarray_get(pCurrent->value.y_type, pTxn, 0)->value.integer 
!= positions[0].value.integer
                 || yarray_get(pCurrent->value.y_type, pTxn, 1)->value.integer 
!= positions[1].value.integer)
             {

Reply via email to