sw/qa/extras/uiwriter/data/tdf151548_tabNavigation2.docx |binary
 sw/qa/extras/uiwriter/uiwriter4.cxx                      |   55 +++++++++++++++
 sw/source/core/crsr/crstrvl.cxx                          |   25 +++---
 3 files changed, 69 insertions(+), 11 deletions(-)

New commits:
commit 6188d8be864251dd6748957db196036c87014bd0
Author:     Justin Luth <justin.l...@collabora.com>
AuthorDate: Fri Jan 27 19:03:35 2023 -0500
Commit:     Justin Luth <jl...@mail.com>
CommitDate: Wed Mar 8 16:58:33 2023 +0000

    tdf#151548 sw content controls: actually gotoFormatContentControl
    
    This is a squashed commit, including Caolan's
    91fa7cc3dc124877f810599fe3c315cda6d03df6
        cid#1520798 Use after free
    
    The cursor kindof needs to change when going to a content control,
    so don't return early just because there will be no selection.
    Instead, set the cursor at the beginning of the control,
    and simply skip the selection part.
    
    Without this, it was not possible for the keyboard navigation
    to jump to rich/plain text or comboboxes that were no longer
    showing placeholder content.
    
    Change-Id: I055ece1d296c6cdf70d6bc7c1df7797bf09532c9
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/146266
    Tested-by: Jenkins
    Reviewed-by: Justin Luth <jl...@mail.com>
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/146663
    Tested-by: Justin Luth <jl...@mail.com>

diff --git a/sw/qa/extras/uiwriter/data/tdf151548_tabNavigation2.docx 
b/sw/qa/extras/uiwriter/data/tdf151548_tabNavigation2.docx
new file mode 100644
index 000000000000..d1b81dfca3ef
Binary files /dev/null and 
b/sw/qa/extras/uiwriter/data/tdf151548_tabNavigation2.docx differ
diff --git a/sw/qa/extras/uiwriter/uiwriter4.cxx 
b/sw/qa/extras/uiwriter/uiwriter4.cxx
index c8e99868a790..64ce10b9d833 100644
--- a/sw/qa/extras/uiwriter/uiwriter4.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter4.cxx
@@ -1491,6 +1491,61 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest4, testTdf95699)
                          pFieldMark->GetFieldname());
 }
 
+CPPUNIT_TEST_FIXTURE(SwUiWriterTest4, testTdf151548_tabNavigation2)
+{
+    // given a form-protected doc with 2 unchecked legacy fieldmark 
checkboxes, 1 modern
+    // checkbox, and a couple of other content controls that are not supposed 
to
+    // have their contents selected upon entry into the control (i.e. no 
placeholder text).
+    createSwDoc("tdf151548_tabNavigation2.docx");
+    SwDoc* pDoc = getSwDoc();
+    SwXTextDocument* pXTextDocument = 
dynamic_cast<SwXTextDocument*>(mxComponent.get());
+
+    IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(2), pMarkAccess->getFieldmarksCount());
+
+    // verify that the checkboxes start off in the unchecked state
+    for (auto it = pMarkAccess->getFieldmarksBegin(); it != 
pMarkAccess->getFieldmarksEnd(); ++it)
+    {
+        sw::mark::ICheckboxFieldmark* pCheckBox
+            = dynamic_cast<::sw::mark::ICheckboxFieldmark*>(*it);
+        CPPUNIT_ASSERT(!pCheckBox->IsChecked());
+    }
+
+    // Toggle on the legacy checkbox
+    pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 32, KEY_SPACE);
+    // Tab to the next control - the modern checkbox
+    pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_TAB);
+    // Tab to the next control - the second legacy checkbox, and toggle it on.
+    pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_TAB);
+    pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 32, KEY_SPACE);
+    // Tab to the next control - a plain text control without placeholder text
+    pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_TAB);
+    // Tab to the next control - a combobox with custom text
+    pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_TAB);
+    Scheduler::ProcessEventsToIdle();
+
+    for (auto it = pMarkAccess->getFieldmarksBegin(); it != 
pMarkAccess->getFieldmarksEnd(); ++it)
+    {
+        sw::mark::ICheckboxFieldmark* pCheckBox
+            = dynamic_cast<::sw::mark::ICheckboxFieldmark*>(*it);
+        // verify that the legacy checkbox became checked by the first loop.
+        CPPUNIT_ASSERT(pCheckBox->IsChecked());
+
+        // This is where it was failing. Tab got stuck moving into the plain 
text/combobox,
+        // so it could never loop around. At this point we are at the end of 
the loop,
+        // so the next tab should take us back to the beginning with the first 
legacy checkbox.
+
+        // Tab to the legacy checkbox, and toggle it off.
+        pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_TAB);
+        pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 32, KEY_SPACE);
+        Scheduler::ProcessEventsToIdle();
+        CPPUNIT_ASSERT(!pCheckBox->IsChecked());
+
+        // Tab to the next content control
+        pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_TAB);
+    }
+}
+
 CPPUNIT_TEST_FIXTURE(SwUiWriterTest4, testTdf151548_tabNavigation)
 {
     // given a form-protected doc with 4 unchecked legacy fieldmark checkboxes 
(and several modern
diff --git a/sw/source/core/crsr/crstrvl.cxx b/sw/source/core/crsr/crstrvl.cxx
index 9cf9f77a4d76..954e9ba6707c 100644
--- a/sw/source/core/crsr/crstrvl.cxx
+++ b/sw/source/core/crsr/crstrvl.cxx
@@ -829,12 +829,6 @@ bool SwCursorShell::GotoFootnoteAnchor(const 
SwTextFootnote& rTextFootnote)
 bool SwCursorShell::GotoFormatContentControl(const SwFormatContentControl& 
rContentControl)
 {
     std::shared_ptr<SwContentControl> pContentControl = 
rContentControl.GetContentControl();
-    if (!pContentControl->GetShowingPlaceHolder() && 
!pContentControl->GetCheckbox()
-        && !pContentControl->GetSelectedListItem() && 
!pContentControl->GetSelectedDate())
-    {
-        return false;
-    }
-
     const SwTextContentControl* pTextContentControl = 
pContentControl->GetTextAttr();
     if (!pTextContentControl)
         return false;
@@ -845,16 +839,25 @@ bool SwCursorShell::GotoFormatContentControl(const 
SwFormatContentControl& rCont
     SwCursor* pCursor = getShellCursor(true);
     SwCursorSaveState aSaveState(*pCursor);
 
-    pCursor->SetMark();
     SwTextNode* pTextNode = pContentControl->GetTextNode();
     // Don't select the text attribute itself at the start.
     sal_Int32 nStart = pTextContentControl->GetStart() + 1;
     pCursor->GetPoint()->Assign(*pTextNode, nStart);
-    // Don't select the CH_TXTATR_BREAKWORD itself at the end.
-    sal_Int32 nEnd = *pTextContentControl->End() - 1;
-    pCursor->GetMark()->Assign(*pTextNode, nEnd);
 
-    bool bRet = !pCursor->IsSelOvr();
+    bool bRet = true;
+    // select contents for certain controls or conditions
+    if (pContentControl->GetShowingPlaceHolder() || 
pContentControl->GetCheckbox()
+        || pContentControl->GetSelectedListItem() || 
pContentControl->GetSelectedDate())
+    {
+        pCursor->SetMark();
+        // Don't select the CH_TXTATR_BREAKWORD itself at the end.
+        sal_Int32 nEnd = *pTextContentControl->End() - 1;
+        pCursor->GetMark()->Assign(*pTextNode, nEnd);
+        bRet = !pCursor->IsSelOvr();
+    }
+    else
+        ClearMark();
+
     if (bRet)
     {
         UpdateCursor(SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE

Reply via email to