Fix undo counting in CPWL_EditImpl::ReplaceSelection for cut operations

When cutting all text and undoing, only partial text was restored due
to incorrect undo counting logic. Add special case handling for cut
operations (selection cleared + empty text inserted) to ensure proper
undo behavior.

Bug: 413695642
Change-Id: I67bff754183f118537aca0ab3ba4726fe63de689
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/132350
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/fpdfsdk/fpdf_formfill_embeddertest.cpp b/fpdfsdk/fpdf_formfill_embeddertest.cpp
index 2b6eda7..386109f 100644
--- a/fpdfsdk/fpdf_formfill_embeddertest.cpp
+++ b/fpdfsdk/fpdf_formfill_embeddertest.cpp
@@ -3591,3 +3591,32 @@
   ASSERT_FALSE(
       FORM_OnKeyDown(form_handle(), page(), FWL_VKEY_Control, modifier));
 }
+
+TEST_F(FPDFFormFillTextFormEmbedderTest, CutAllTextUndoRestoresAllCharacters) {
+  ClickOnFormFieldAtPoint(RegularFormBegin());
+  CheckCanUndo(false);
+  CheckCanRedo(false);
+
+  // Type "ABC"
+  TypeTextIntoTextField(3, RegularFormBegin());
+  CheckFocusedFieldText("ABC");
+  CheckSelection("");
+  CheckCanUndo(true);
+
+  // Select all text
+  SelectAllRegularFormTextWithMouse();
+  CheckSelection("ABC");
+
+  // Cut all text (equivalent to ctrl+x) by replacing selection with empty
+  // string
+  FORM_ReplaceSelection(form_handle(), page(), nullptr);
+  CheckFocusedFieldText("");
+  CheckCanUndo(true);
+  CheckCanRedo(false);
+
+  // Undo the cut operation - should restore all 3 characters "ABC"
+  PerformUndo();
+  CheckFocusedFieldText("ABC");
+  CheckCanUndo(true);
+  CheckCanRedo(true);
+}
diff --git a/fpdfsdk/pwl/cpwl_edit_impl.cpp b/fpdfsdk/pwl/cpwl_edit_impl.cpp
index 18aa66d..1a56f9e 100644
--- a/fpdfsdk/pwl/cpwl_edit_impl.cpp
+++ b/fpdfsdk/pwl/cpwl_edit_impl.cpp
@@ -1855,7 +1855,9 @@
   bool is_insert_undo_clear = ClearSelection();
   // It is necessary to determine whether the value of `undo_remaining_` is 2 or
   // 3 based on ClearSelection().
-  if (!is_insert_undo_clear) {
+  // Special case: when cutting (clearing selection and inserting empty text),
+  // we need to ensure proper undo counting to restore all characters.
+  if (!is_insert_undo_clear || text.IsEmpty()) {
     undo_.GetLastAddItem()->set_undo_remaining(2);
   }
   // Select the inserted text.
@@ -1865,7 +1867,7 @@
   sel_state_.Set(caret_before_insert, caret_after_insert);
 
   AddEditUndoItem(std::make_unique<UndoReplaceSelection>(this, true));
-  if (!is_insert_undo_clear) {
+  if (!is_insert_undo_clear || text.IsEmpty()) {
     undo_.GetLastAddItem()->set_undo_remaining(2);
   }
 }
@@ -1875,12 +1877,14 @@
   bool is_insert_undo_clear = ClearSelection();
   // It is necessary to determine whether the value of `undo_remaining_` is 2 or
   // 3 based on ClearSelection().
-  if (!is_insert_undo_clear) {
+  // Special case: when cutting (clearing selection and inserting empty text),
+  // we need to ensure proper undo counting to restore all characters.
+  if (!is_insert_undo_clear || text.IsEmpty()) {
     undo_.GetLastAddItem()->set_undo_remaining(2);
   }
   InsertText(text, FX_Charset::kDefault);
   AddEditUndoItem(std::make_unique<UndoReplaceSelection>(this, true));
-  if (!is_insert_undo_clear) {
+  if (!is_insert_undo_clear || text.IsEmpty()) {
     undo_.GetLastAddItem()->set_undo_remaining(2);
   }
 }