tdf#152012 Fix assert fail on opening date picker
This patch fixes tdf#152012 which caused an assertion failure on opening
date picker field in a DOCX file
The assertion was:
include/o3tl/span.hxx:83: constexpr o3tl::span<T>::value_type& o3tl::
span<T>::operator[](o3tl::span<T>::size_type) const [with T = const
int; o3tl::span<T>::reference = const int&; o3tl::span<T>::size_type
= long unsigned int]: Assertion `pos < size()' failed.
And the backtrace was:
1 __pthread_kill_implementation pthread_kill.c:44
2 __pthread_kill_internal pthread_kill.c:78
3 __GI___pthread_kill pthread_kill.c:89
4 __GI_raise raise.c:26
5 __GI_abort abort.c:79
6 __assert_fail_base assert.c:92
7 __GI___assert_fail assert.c:101
8 o3tl::span<int const>::operator[] span.hxx:83
9 OutputDevice::ImplLayout text.cxx:1396
10 OutputDevice::DrawTextArray text.cxx:948
11 Calendar::ImplDraw calendar.cxx:71
12 Calendar::Paint calendar.cxx:1133
The problem was caused by an out of bound access to a vector of integers
which was created for rendering calendar header consisting of the first
letters of 7 days of week, when you clicked on the down arrow on the
date field.
The function OutputDevice::DrawTextArray() takes an 'rStr' string to
draw, and 'pDXAry' array for the exact position of the the individual
characters. It also takes 'nIndex' as the first index, and 'nLen' as the
length of the array. 'nLen' has the default value of -1. In this case,
the length is calculated from the size of the string passed to the
function. This works well if the one who uses the function makes sure
that the size of the array and the length of string are equal.
Previously, for the 7 days of the week, a 7 letter string "smtwtfs"
(depending on the week start day this can be different, but length is
always 7) was sent to this method without providing the length, thus
the string length: 7 was used. In this case, positions of the letters
were calculated and used from other array named mnDayOfWeekAry[7].
mnDayOfWeekAry[k+1] was used as the position of letter k (k=0..5).
In this case, there was 7 letters for 7 days, and only 6 positions
provided by the array. This caused assertion failure in span.hxx:83
when trying to accesss mnDayOfWeekAry[7] via o3tl::span<T>::operator[].
Value of mnDayOfWeekAry[0] was used in other calculations, therefore to
fix this problem, mnDayOfWeekAry was extended from 7 to 8, and the last
position was set to the end of drawing rectangle.
The other thing that is done in this patch to avoid this problem in the
future is removing the default value from the function prototype, so
that the use should always be done by providing the length of array and
starting index. After removing these defaults, it became necessary to
provide empty arrays for 'pKashidaAry' which provides the kashida
positions, if needed.
With this fix in place, the assertion failure no longer happens.
A UI test is added to make sure the crash will not happen again. The
test can be run by invoking:
cd sw && make -srj1 UITest_writer_tests5 \
UITEST_TEST_NAME="DateFormFieldPropertiesDialog.dateFormFieldDialog.test_date_picker_drop_down" \
SAL_USE_VCLPLUGIN=gen
Change-Id: I347afb358fbc4956524f7f0a0abc3a221bf42992
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/142642
Tested-by: Jenkins
Reviewed-by: خالد حسني <khaled@aliftype.com>
diff --git a/include/vcl/outdev.hxx b/include/vcl/outdev.hxx
index b1b47d60..14c1675 100644
--- a/include/vcl/outdev.hxx
+++ b/include/vcl/outdev.hxx
@@ -1038,9 +1038,9 @@ public:
void DrawTextArray( const Point& rStartPt, const OUString& rStr,
o3tl::span<const sal_Int32> pDXAry,
o3tl::span<const sal_Bool> pKashidaAry={},
sal_Int32 nIndex = 0,
sal_Int32 nLen = -1,
o3tl::span<const sal_Bool> pKashidaAry,
sal_Int32 nIndex,
sal_Int32 nLen,
SalLayoutFlags flags = SalLayoutFlags::NONE,
const SalLayoutGlyphs* pLayoutCache = nullptr);
tools::Long GetTextArray( const OUString& rStr, std::vector<sal_Int32>* pDXAry,
diff --git a/sc/source/ui/view/output2.cxx b/sc/source/ui/view/output2.cxx
index 07f9f75..28d448f 100644
--- a/sc/source/ui/view/output2.cxx
+++ b/sc/source/ui/view/output2.cxx
@@ -2111,7 +2111,7 @@ tools::Rectangle ScOutputData::LayoutStrings(bool bPixelToLogic, bool bPaint, co
}
if (bPaint)
mpDev->DrawTextArray(aDrawTextPos, aShort, aDX);
mpDev->DrawTextArray(aDrawTextPos, aShort, aDX, {}, 0, nLen);
}
else
{
diff --git a/sw/qa/uitest/data/date_picker.docx b/sw/qa/uitest/data/date_picker.docx
new file mode 100644
index 0000000..3100c8d
--- /dev/null
+++ b/sw/qa/uitest/data/date_picker.docx
Binary files differ
diff --git a/sw/qa/uitest/writer_tests5/DateFormFieldPropertiesDialog.py b/sw/qa/uitest/writer_tests5/DateFormFieldPropertiesDialog.py
index 7eaa7b2..eb43912 100644
--- a/sw/qa/uitest/writer_tests5/DateFormFieldPropertiesDialog.py
+++ b/sw/qa/uitest/writer_tests5/DateFormFieldPropertiesDialog.py
@@ -8,6 +8,7 @@
#
from uitest.framework import UITestCase
from uitest.uihelper.common import get_state_as_dict, get_url_for_data_file
from libreoffice.uno.propertyvalue import mkPropertyValues
class dateFormFieldDialog(UITestCase):
@@ -125,4 +126,18 @@ class dateFormFieldDialog(UITestCase):
# a placeholder text is not changed by format change
self.assertEqual(writer_doc.getText().getString(), "Jul 17, 2019")
def test_date_picker_drop_down(self):
with self.ui_test.load_file(get_url_for_data_file("date_picker.docx")) as writer_doc:
xWriterDoc = self.xUITest.getTopFocusWindow()
xWriterEdit = xWriterDoc.getChild("writer_edit")
xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE": "RIGHT"}))
xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE": "RIGHT"}))
# open the dialog (cursor is at the field)
xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE": "ALT+DOWN"}))
xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE": "ESC"}))
xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE": "UP"}))
self.assertEqual(writer_doc.getText().getString(), "\nClick to choose a date")
# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/toolkit/source/awt/vclxgraphics.cxx b/toolkit/source/awt/vclxgraphics.cxx
index a8409a5..673c44e 100644
--- a/toolkit/source/awt/vclxgraphics.cxx
+++ b/toolkit/source/awt/vclxgraphics.cxx
@@ -468,7 +468,7 @@ void VCLXGraphics::drawTextArray( sal_Int32 x, sal_Int32 y, const OUString& rTex
{
aDXA[i] = rLongs[i];
}
mpOutputDevice->DrawTextArray( Point( x, y ), rText, aDXA );
mpOutputDevice->DrawTextArray( Point( x, y ), rText, aDXA , {}, 0, rText.getLength());
}
}
diff --git a/vcl/inc/calendar.hxx b/vcl/inc/calendar.hxx
index 500b97f..a40f1ff 100644
--- a/vcl/inc/calendar.hxx
+++ b/vcl/inc/calendar.hxx
@@ -126,7 +126,7 @@ class Calendar final : public Control
tools::Rectangle maPrevRect;
tools::Rectangle maNextRect;
OUString maDayOfWeekText;
sal_Int32 mnDayOfWeekAry[7];
sal_Int32 mnDayOfWeekAry[8];
Date maOldFormatFirstDate;
Date maOldFormatLastDate;
Date maFirstDate;
diff --git a/vcl/source/control/calendar.cxx b/vcl/source/control/calendar.cxx
index 0227db0..3995aa1 100644
--- a/vcl/source/control/calendar.cxx
+++ b/vcl/source/control/calendar.cxx
@@ -260,6 +260,9 @@ void Calendar::ImplFormat()
nDay %= 7;
}
// header position for the last day of week
mnDayOfWeekAry[7] = mnMonthWidth;
mbCalc = false;
}
@@ -705,9 +708,9 @@ void Calendar::ImplDraw(vcl::RenderContext& rRenderContext)
Point aStartPos(nDayX, nDeltaY);
rRenderContext.DrawLine(aStartPos, Point(nDayX + (7 * mnDayWidth), nDeltaY));
std::vector<sal_Int32> aTmp;
for (int k=0; k<6; ++k)
for (int k=0; k<7; ++k)
aTmp.push_back(mnDayOfWeekAry[k+1]);
rRenderContext.DrawTextArray(Point(nDayX + mnDayOfWeekAry[0], nDayY), maDayOfWeekText, aTmp);
rRenderContext.DrawTextArray(Point(nDayX + mnDayOfWeekAry[0], nDayY), maDayOfWeekText, aTmp, {}, 0, aTmp.size());
// display days
sal_uInt16 nDaysInMonth = aDate.GetDaysInMonth();