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