Handle Enter/Space key events for read-only check box and radio button.

Currently if the user tabs onto a read-only check box or radio button,
pressing any character or just space/enter checks/unchecks the controls.

This CL adds a check for read-only flag in the PWL control (Check Box/
Radio Button), before processing the OnChar event.

If the window created for the control is read-only in the CFFL_Checkbox
and CFFL_RadioButton, altering the state of the control is not allowed.


Bug: pdfium:1431
Change-Id: I6fcf8de0c01d973157641fde9bc2579483878484
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/66832
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Himadri Kakar <hikakar@microsoft.com>
diff --git a/fpdfsdk/formfiller/cffl_checkbox.cpp b/fpdfsdk/formfiller/cffl_checkbox.cpp
index 47e158f..4cb4a02 100644
--- a/fpdfsdk/formfiller/cffl_checkbox.cpp
+++ b/fpdfsdk/formfiller/cffl_checkbox.cpp
@@ -63,7 +63,7 @@
       CFFL_FormFiller::OnChar(pAnnot, nChar, nFlags);
 
       CPWL_CheckBox* pWnd = GetCheckBox(pPageView, true);
-      if (pWnd) {
+      if (pWnd && !pWnd->IsReadOnly()) {
         CPDFSDK_Widget* pWidget = ToCPDFSDKWidget(pAnnot);
         pWnd->SetCheck(!pWidget->IsChecked());
       }
diff --git a/fpdfsdk/formfiller/cffl_radiobutton.cpp b/fpdfsdk/formfiller/cffl_radiobutton.cpp
index 90a4fd9..670197e 100644
--- a/fpdfsdk/formfiller/cffl_radiobutton.cpp
+++ b/fpdfsdk/formfiller/cffl_radiobutton.cpp
@@ -58,7 +58,7 @@
 
       CFFL_FormFiller::OnChar(pAnnot, nChar, nFlags);
       CPWL_RadioButton* pWnd = GetRadioButton(pPageView, true);
-      if (pWnd)
+      if (pWnd && !pWnd->IsReadOnly())
         pWnd->SetCheck(true);
       return CommitData(pPageView, nFlags);
     }
diff --git a/fpdfsdk/fpdf_formfill_embeddertest.cpp b/fpdfsdk/fpdf_formfill_embeddertest.cpp
index eee623b..56917e2 100644
--- a/fpdfsdk/fpdf_formfill_embeddertest.cpp
+++ b/fpdfsdk/fpdf_formfill_embeddertest.cpp
@@ -1021,6 +1021,75 @@
   UnloadPage(page);
 }
 
+TEST_F(FPDFFormFillEmbedderTest, CheckReadOnlyInCheckbox) {
+  EmbedderTestTimerHandlingDelegate delegate;
+  SetDelegate(&delegate);
+
+  ASSERT_TRUE(OpenDocument("click_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // Check for read-only checkbox.
+    ScopedFPDFAnnotation focused_annot(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(FORM_SetFocusedAnnot(form_handle(), focused_annot.get()));
+
+    // Shift-tab to the previous control.
+    ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab,
+                               FWL_EVENTFLAG_ShiftKey));
+    FPDF_ANNOTATION annot = nullptr;
+    int page_index = -1;
+    ASSERT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot));
+    EXPECT_EQ(0, FPDFPage_GetAnnotIndex(page, annot));
+
+    // The read-only checkbox is initially in checked state.
+    EXPECT_TRUE(FPDFAnnot_IsChecked(form_handle(), annot));
+
+    EXPECT_TRUE(FORM_OnChar(form_handle(), page, FWL_VKEY_Return, 0));
+    EXPECT_TRUE(FPDFAnnot_IsChecked(form_handle(), annot));
+
+    EXPECT_TRUE(FORM_OnChar(form_handle(), page, FWL_VKEY_Space, 0));
+    EXPECT_TRUE(FPDFAnnot_IsChecked(form_handle(), annot));
+
+    FPDFPage_CloseAnnot(annot);
+  }
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbedderTest, CheckReadOnlyInRadiobutton) {
+  EmbedderTestTimerHandlingDelegate delegate;
+  SetDelegate(&delegate);
+
+  ASSERT_TRUE(OpenDocument("click_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    // Check for read-only radio button.
+    ScopedFPDFAnnotation focused_annot(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(FORM_SetFocusedAnnot(form_handle(), focused_annot.get()));
+
+    // Tab to the next control.
+    ASSERT_TRUE(FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Tab, 0));
+
+    FPDF_ANNOTATION annot = nullptr;
+    int page_index = -1;
+    ASSERT_TRUE(FORM_GetFocusedAnnot(form_handle(), &page_index, &annot));
+    EXPECT_EQ(2, FPDFPage_GetAnnotIndex(page, annot));
+    // The read-only radio button is initially in checked state.
+    EXPECT_FALSE(FPDFAnnot_IsChecked(form_handle(), annot));
+
+    EXPECT_TRUE(FORM_OnChar(form_handle(), page, FWL_VKEY_Return, 0));
+    EXPECT_FALSE(FPDFAnnot_IsChecked(form_handle(), annot));
+
+    EXPECT_TRUE(FORM_OnChar(form_handle(), page, FWL_VKEY_Space, 0));
+    EXPECT_FALSE(FPDFAnnot_IsChecked(form_handle(), annot));
+
+    FPDFPage_CloseAnnot(annot);
+  }
+  UnloadPage(page);
+}
+
 #ifdef PDF_ENABLE_V8
 TEST_F(FPDFFormFillEmbedderTest, DisableJavaScript) {
   // Test that timers and intervals can't fire without JS.
diff --git a/fpdfsdk/pwl/cpwl_special_button.cpp b/fpdfsdk/pwl/cpwl_special_button.cpp
index e653ae1..215df40 100644
--- a/fpdfsdk/pwl/cpwl_special_button.cpp
+++ b/fpdfsdk/pwl/cpwl_special_button.cpp
@@ -39,6 +39,9 @@
 }
 
 bool CPWL_CheckBox::OnChar(uint16_t nChar, uint32_t nFlag) {
+  if (IsReadOnly())
+    return false;
+
   SetCheck(!IsChecked());
   return true;
 }
@@ -59,6 +62,9 @@
 }
 
 bool CPWL_RadioButton::OnChar(uint16_t nChar, uint32_t nFlag) {
+  if (IsReadOnly())
+    return false;
+
   SetCheck(true);
   return true;
 }
diff --git a/fpdfsdk/pwl/cpwl_special_button_embeddertest.cpp b/fpdfsdk/pwl/cpwl_special_button_embeddertest.cpp
index c99e488..ef50ef4 100644
--- a/fpdfsdk/pwl/cpwl_special_button_embeddertest.cpp
+++ b/fpdfsdk/pwl/cpwl_special_button_embeddertest.cpp
@@ -108,13 +108,10 @@
 TEST_F(CPWLSpecialButtonEmbedderTest, EnterOnReadOnlyCheckBox) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotReadOnlyCheckBox());
   CPWL_CheckBox* check_box = static_cast<CPWL_CheckBox*>(GetWindow());
+  EXPECT_TRUE(check_box->IsChecked());
   EXPECT_TRUE(GetCPDFSDKFormFillEnv()->GetInteractiveFormFiller()->OnChar(
       GetCPDFSDKAnnotReadOnlyCheckBox(), '\r', 0));
-  // The check box is checked by default. Since it is a read only checkbox,
-  // clicking Enter shouldn't change its state.
-  // TODO(http://crbug.com/pdfium/1431) : Change this to EXPECT_TRUE
-  // as part of the fix.
-  EXPECT_FALSE(check_box->IsChecked());
+  EXPECT_TRUE(check_box->IsChecked());
 }
 
 TEST_F(CPWLSpecialButtonEmbedderTest, EnterOnCheckBox) {
@@ -132,11 +129,10 @@
 TEST_F(CPWLSpecialButtonEmbedderTest, EnterOnReadOnlyRadioButton) {
   FormFillerAndWindowSetup(GetCPDFSDKAnnotReadOnlyRadioButton());
   CPWL_RadioButton* radio_button = static_cast<CPWL_RadioButton*>(GetWindow());
+  EXPECT_FALSE(radio_button->IsChecked());
   EXPECT_TRUE(GetCPDFSDKFormFillEnv()->GetInteractiveFormFiller()->OnChar(
       GetCPDFSDKAnnotReadOnlyRadioButton(), '\r', 0));
-  // TODO(http://crbug.com/pdfium/1431) : Change this to EXPECT_FALSE
-  // as part of the fix.
-  EXPECT_TRUE(radio_button->IsChecked());
+  EXPECT_FALSE(radio_button->IsChecked());
 }
 
 TEST_F(CPWLSpecialButtonEmbedderTest, EnterOnRadioButton) {