Avoid underflow before calling CFDE_TextEditEngine::AdjustGap().
The text by itself may already exceed the limit if it was previously
granted an exemption. Increasing the limit maintains the invariant
that |text_length_| <= |character_limit_|.
-- Add the link to the explainer dsinclair suggested
Bug: chromium:1029437
Change-Id: I7962522dc8253f6070524c3342b65bd3aefca5bf
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/65190
Reviewed-by: Lei Zhang <thestig@chromium.org>
Reviewed-by: dsinclair <dsinclair@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/xfa/fde/cfde_texteditengine.cpp b/xfa/fde/cfde_texteditengine.cpp
index 21b365b..0df28b4 100644
--- a/xfa/fde/cfde_texteditengine.cpp
+++ b/xfa/fde/cfde_texteditengine.cpp
@@ -308,12 +308,17 @@
// engine. Otherwise, if you enter 123456789 for an SSN into a field
// with a 9 character limit and we reformat to 123-45-6789 we'll truncate
// the 89 when inserting into the text edit. See https://crbug.com/pdfium/1089
- if (has_character_limit_ && add_operation != RecordOperation::kSkipNotify &&
- text_length_ + length > character_limit_) {
- exceeded_limit = true;
- length = character_limit_ - text_length_;
+ if (has_character_limit_ && text_length_ + length > character_limit_) {
+ if (add_operation == RecordOperation::kSkipNotify) {
+ // Raise the limit to allow subsequent changes to expanded text.
+ character_limit_ = text_length_ + length;
+ } else {
+ // Trucate the text to comply with the limit.
+ CHECK(text_length_ <= character_limit_);
+ length = character_limit_ - text_length_;
+ exceeded_limit = true;
+ }
}
-
AdjustGap(idx, length);
if (validation_enabled_ || limit_horizontal_area_ || limit_vertical_area_) {
diff --git a/xfa/fde/cfde_texteditengine.h b/xfa/fde/cfde_texteditengine.h
index 7c78c6c..d95994d 100644
--- a/xfa/fde/cfde_texteditengine.h
+++ b/xfa/fde/cfde_texteditengine.h
@@ -214,8 +214,11 @@
float line_spacing_;
std::vector<WideString::CharType> content_;
size_t text_length_;
+
+ // See e.g. https://en.wikipedia.org/wiki/Gap_buffer
size_t gap_position_;
size_t gap_size_;
+
size_t available_width_;
size_t character_limit_;
size_t visible_line_count_;
diff --git a/xfa/fde/cfde_texteditengine_unittest.cpp b/xfa/fde/cfde_texteditengine_unittest.cpp
index 38c0532..f1fc182 100644
--- a/xfa/fde/cfde_texteditengine_unittest.cpp
+++ b/xfa/fde/cfde_texteditengine_unittest.cpp
@@ -161,6 +161,24 @@
engine()->SetDelegate(nullptr);
}
+TEST_F(CFDE_TextEditEngineTest, InsertSkipNotify) {
+ engine()->SetHasCharacterLimit(true);
+ engine()->SetCharacterLimit(8);
+ engine()->Insert(0, L"Hello");
+ engine()->Insert(5, L" World",
+ CFDE_TextEditEngine::RecordOperation::kSkipNotify);
+ EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
+
+ engine()->Insert(0, L"Not inserted");
+ EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
+
+ engine()->Delete(5, 1);
+ EXPECT_STREQ(L"HelloWorld", engine()->GetText().c_str());
+
+ engine()->Insert(0, L"****");
+ EXPECT_STREQ(L"*HelloWorld", engine()->GetText().c_str());
+}
+
TEST_F(CFDE_TextEditEngineTest, Delete) {
EXPECT_STREQ(L"", engine()->Delete(0, 50).c_str());
EXPECT_STREQ(L"", engine()->GetText().c_str());