Add FORM_GetSelectedText() function.

This function copies the selected text from a form text field or
form combobox text field into the buffer parameter and returns the
length of the selected text string. When buffer is a nullptr or
buflen is less than the length of the selected text, this function
does not modify the buffer and only returns the selected text length.

BUG=chromium:59266

Change-Id: Ie77de38e45bbe6f9ea033826c961435304eedfc7
Reviewed-on: https://pdfium-review.googlesource.com/6413
Reviewed-by: dsinclair <dsinclair@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/fpdfsdk/cpdfsdk_annothandlermgr.cpp b/fpdfsdk/cpdfsdk_annothandlermgr.cpp
index 7ea301c..2b089e5 100644
--- a/fpdfsdk/cpdfsdk_annothandlermgr.cpp
+++ b/fpdfsdk/cpdfsdk_annothandlermgr.cpp
@@ -76,6 +76,11 @@
   GetAnnotHandler(pAnnot)->OnLoad(pAnnot);
 }
 
+CFX_WideString CPDFSDK_AnnotHandlerMgr::Annot_GetSelectedText(
+    CPDFSDK_Annot* pAnnot) {
+  return GetAnnotHandler(pAnnot)->GetSelectedText(pAnnot);
+}
+
 IPDFSDK_AnnotHandler* CPDFSDK_AnnotHandlerMgr::GetAnnotHandler(
     CPDFSDK_Annot* pAnnot) const {
   return GetAnnotHandler(pAnnot->GetAnnotSubtype());
diff --git a/fpdfsdk/cpdfsdk_annothandlermgr.h b/fpdfsdk/cpdfsdk_annothandlermgr.h
index f36c3fb..5f6ff94 100644
--- a/fpdfsdk/cpdfsdk_annothandlermgr.h
+++ b/fpdfsdk/cpdfsdk_annothandlermgr.h
@@ -43,6 +43,8 @@
   void Annot_OnCreate(CPDFSDK_Annot* pAnnot);
   void Annot_OnLoad(CPDFSDK_Annot* pAnnot);
 
+  CFX_WideString Annot_GetSelectedText(CPDFSDK_Annot* pAnnot);
+
   IPDFSDK_AnnotHandler* GetAnnotHandler(CPDFSDK_Annot* pAnnot) const;
   void Annot_OnDraw(CPDFSDK_PageView* pPageView,
                     CPDFSDK_Annot* pAnnot,
diff --git a/fpdfsdk/cpdfsdk_baannothandler.cpp b/fpdfsdk/cpdfsdk_baannothandler.cpp
index fa83932..4984ee9 100644
--- a/fpdfsdk/cpdfsdk_baannothandler.cpp
+++ b/fpdfsdk/cpdfsdk_baannothandler.cpp
@@ -193,6 +193,10 @@
   return pAnnot->GetRect();
 }
 
+CFX_WideString CPDFSDK_BAAnnotHandler::GetSelectedText(CPDFSDK_Annot* pAnnot) {
+  return CFX_WideString();
+}
+
 bool CPDFSDK_BAAnnotHandler::HitTest(CPDFSDK_PageView* pPageView,
                                      CPDFSDK_Annot* pAnnot,
                                      const CFX_PointF& point) {
diff --git a/fpdfsdk/cpdfsdk_baannothandler.h b/fpdfsdk/cpdfsdk_baannothandler.h
index d5f170f..2af2f3e 100644
--- a/fpdfsdk/cpdfsdk_baannothandler.h
+++ b/fpdfsdk/cpdfsdk_baannothandler.h
@@ -37,6 +37,7 @@
   void ReleaseAnnot(CPDFSDK_Annot* pAnnot) override;
   CFX_FloatRect GetViewBBox(CPDFSDK_PageView* pPageView,
                             CPDFSDK_Annot* pAnnot) override;
+  CFX_WideString GetSelectedText(CPDFSDK_Annot* pAnnot) override;
   bool HitTest(CPDFSDK_PageView* pPageView,
                CPDFSDK_Annot* pAnnot,
                const CFX_PointF& point) override;
diff --git a/fpdfsdk/cpdfsdk_pageview.cpp b/fpdfsdk/cpdfsdk_pageview.cpp
index 982eed1..aae245e 100644
--- a/fpdfsdk/cpdfsdk_pageview.cpp
+++ b/fpdfsdk/cpdfsdk_pageview.cpp
@@ -241,6 +241,16 @@
 }
 #endif  // PDF_ENABLE_XFA
 
+CFX_WideString CPDFSDK_PageView::GetSelectedText() {
+  if (CPDFSDK_Annot* pAnnot = GetFocusAnnot()) {
+    CPDFSDK_AnnotHandlerMgr* pAnnotHandlerMgr =
+        m_pFormFillEnv->GetAnnotHandlerMgr();
+    return pAnnotHandlerMgr->Annot_GetSelectedText(pAnnot);
+  }
+
+  return CFX_WideString();
+}
+
 bool CPDFSDK_PageView::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) {
   CPDFSDK_Annot::ObservedPtr pAnnot(GetFXWidgetAtPoint(point));
   if (!pAnnot) {
diff --git a/fpdfsdk/cpdfsdk_pageview.h b/fpdfsdk/cpdfsdk_pageview.h
index 8bede08..9eebb6e 100644
--- a/fpdfsdk/cpdfsdk_pageview.h
+++ b/fpdfsdk/cpdfsdk_pageview.h
@@ -60,6 +60,8 @@
     return m_pFormFillEnv.Get();
   }
 
+  CFX_WideString GetSelectedText();
+
   bool OnLButtonDown(const CFX_PointF& point, uint32_t nFlag);
   bool OnLButtonUp(const CFX_PointF& point, uint32_t nFlag);
 #ifdef PDF_ENABLE_XFA
diff --git a/fpdfsdk/cpdfsdk_widgethandler.cpp b/fpdfsdk/cpdfsdk_widgethandler.cpp
index c34f766..68d95ed 100644
--- a/fpdfsdk/cpdfsdk_widgethandler.cpp
+++ b/fpdfsdk/cpdfsdk_widgethandler.cpp
@@ -280,6 +280,13 @@
   return CFX_FloatRect(0, 0, 0, 0);
 }
 
+CFX_WideString CPDFSDK_WidgetHandler::GetSelectedText(CPDFSDK_Annot* pAnnot) {
+  if (!pAnnot->IsSignatureWidget() && m_pFormFiller)
+    return m_pFormFiller->GetSelectedText(pAnnot);
+
+  return CFX_WideString();
+}
+
 bool CPDFSDK_WidgetHandler::HitTest(CPDFSDK_PageView* pPageView,
                                     CPDFSDK_Annot* pAnnot,
                                     const CFX_PointF& point) {
diff --git a/fpdfsdk/cpdfsdk_widgethandler.h b/fpdfsdk/cpdfsdk_widgethandler.h
index 4e8c9ef..9ef190f 100644
--- a/fpdfsdk/cpdfsdk_widgethandler.h
+++ b/fpdfsdk/cpdfsdk_widgethandler.h
@@ -38,6 +38,7 @@
   void ReleaseAnnot(CPDFSDK_Annot* pAnnot) override;
   CFX_FloatRect GetViewBBox(CPDFSDK_PageView* pPageView,
                             CPDFSDK_Annot* pAnnot) override;
+  CFX_WideString GetSelectedText(CPDFSDK_Annot* pAnnot) override;
   bool HitTest(CPDFSDK_PageView* pPageView,
                CPDFSDK_Annot* pAnnot,
                const CFX_PointF& point) override;
diff --git a/fpdfsdk/cpdfsdk_xfawidgethandler.cpp b/fpdfsdk/cpdfsdk_xfawidgethandler.cpp
index 5e258bc..53a7740 100644
--- a/fpdfsdk/cpdfsdk_xfawidgethandler.cpp
+++ b/fpdfsdk/cpdfsdk_xfawidgethandler.cpp
@@ -97,6 +97,11 @@
   return rcWidget;
 }
 
+CFX_WideString CPDFSDK_XFAWidgetHandler::GetSelectedText(
+    CPDFSDK_Annot* pAnnot) {
+  return CFX_WideString();
+}
+
 bool CPDFSDK_XFAWidgetHandler::HitTest(CPDFSDK_PageView* pPageView,
                                        CPDFSDK_Annot* pAnnot,
                                        const CFX_PointF& point) {
diff --git a/fpdfsdk/cpdfsdk_xfawidgethandler.h b/fpdfsdk/cpdfsdk_xfawidgethandler.h
index 2aea49b..65e5e45 100644
--- a/fpdfsdk/cpdfsdk_xfawidgethandler.h
+++ b/fpdfsdk/cpdfsdk_xfawidgethandler.h
@@ -33,6 +33,7 @@
   void ReleaseAnnot(CPDFSDK_Annot* pAnnot) override;
   CFX_FloatRect GetViewBBox(CPDFSDK_PageView* pPageView,
                             CPDFSDK_Annot* pAnnot) override;
+  CFX_WideString GetSelectedText(CPDFSDK_Annot* pAnnot) override;
   bool HitTest(CPDFSDK_PageView* pPageView,
                CPDFSDK_Annot* pAnnot,
                const CFX_PointF& point) override;
diff --git a/fpdfsdk/formfiller/cffl_formfiller.cpp b/fpdfsdk/formfiller/cffl_formfiller.cpp
index 297d5b7..526f4b9 100644
--- a/fpdfsdk/formfiller/cffl_formfiller.cpp
+++ b/fpdfsdk/formfiller/cffl_formfiller.cpp
@@ -239,6 +239,17 @@
   return false;
 }
 
+CFX_WideString CFFL_FormFiller::GetSelectedText(CPDFSDK_Annot* pAnnot) {
+  if (!IsValid())
+    return CFX_WideString();
+
+  CPDFSDK_PageView* pPageView = GetCurPageView(true);
+  ASSERT(pPageView);
+
+  CPWL_Wnd* pWnd = GetPDFWindow(pPageView, false);
+  return pWnd ? pWnd->GetSelectedText() : CFX_WideString();
+}
+
 void CFFL_FormFiller::SetFocusForAnnot(CPDFSDK_Annot* pAnnot, uint32_t nFlag) {
   CPDFSDK_Widget* pWidget = (CPDFSDK_Widget*)pAnnot;
   UnderlyingPageType* pPage = pWidget->GetUnderlyingPage();
diff --git a/fpdfsdk/formfiller/cffl_formfiller.h b/fpdfsdk/formfiller/cffl_formfiller.h
index 445e86f..2ddbdf2 100644
--- a/fpdfsdk/formfiller/cffl_formfiller.h
+++ b/fpdfsdk/formfiller/cffl_formfiller.h
@@ -74,6 +74,8 @@
                          uint32_t nFlags);
   virtual bool OnChar(CPDFSDK_Annot* pAnnot, uint32_t nChar, uint32_t nFlags);
 
+  CFX_WideString GetSelectedText(CPDFSDK_Annot* pAnnot);
+
   void SetFocusForAnnot(CPDFSDK_Annot* pAnnot, uint32_t nFlag);
   void KillFocusForAnnot(CPDFSDK_Annot* pAnnot, uint32_t nFlag);
 
diff --git a/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp b/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp
index 7647fb1..69c74bc 100644
--- a/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp
+++ b/fpdfsdk/formfiller/cffl_interactiveformfiller.cpp
@@ -522,6 +522,13 @@
     UnRegisterFormFiller(pAnnot);
 }
 
+CFX_WideString CFFL_InteractiveFormFiller::GetSelectedText(
+    CPDFSDK_Annot* pAnnot) {
+  ASSERT(pAnnot->GetPDFAnnot()->GetSubtype() == CPDF_Annot::Subtype::WIDGET);
+  CFFL_FormFiller* pFormFiller = GetFormFiller(pAnnot, false);
+  return pFormFiller ? pFormFiller->GetSelectedText(pAnnot) : CFX_WideString();
+}
+
 void CFFL_InteractiveFormFiller::UnRegisterFormFiller(CPDFSDK_Annot* pAnnot) {
   auto it = m_Maps.find(pAnnot);
   if (it == m_Maps.end())
diff --git a/fpdfsdk/formfiller/cffl_interactiveformfiller.h b/fpdfsdk/formfiller/cffl_interactiveformfiller.h
index f72dfbc..b7724a3 100644
--- a/fpdfsdk/formfiller/cffl_interactiveformfiller.h
+++ b/fpdfsdk/formfiller/cffl_interactiveformfiller.h
@@ -82,6 +82,8 @@
   CFFL_FormFiller* GetFormFiller(CPDFSDK_Annot* pAnnot, bool bRegister);
   void RemoveFormFiller(CPDFSDK_Annot* pAnnot);
 
+  CFX_WideString GetSelectedText(CPDFSDK_Annot* pAnnot);
+
   static bool IsVisible(CPDFSDK_Widget* pWidget);
   static bool IsReadOnly(CPDFSDK_Widget* pWidget);
   static bool IsFillingAllowed(CPDFSDK_Widget* pWidget);
diff --git a/fpdfsdk/fpdfformfill.cpp b/fpdfsdk/fpdfformfill.cpp
index 717e972..08d3d30 100644
--- a/fpdfsdk/fpdfformfill.cpp
+++ b/fpdfsdk/fpdfformfill.cpp
@@ -369,6 +369,24 @@
   return pPageView->OnChar(nChar, modifier);
 }
 
+DLLEXPORT unsigned long STDCALL FORM_GetSelectedText(FPDF_FORMHANDLE hHandle,
+                                                     FPDF_PAGE page,
+                                                     void* buffer,
+                                                     unsigned long buflen) {
+  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
+  if (!pPageView)
+    return 0;
+
+  CFX_WideString wide_str_form_text = pPageView->GetSelectedText();
+  CFX_ByteString encoded_form_text = wide_str_form_text.UTF16LE_Encode();
+  unsigned long form_text_len = encoded_form_text.GetLength();
+
+  if (buffer && buflen >= form_text_len)
+    memcpy(buffer, encoded_form_text.c_str(), form_text_len);
+
+  return form_text_len;
+}
+
 DLLEXPORT FPDF_BOOL STDCALL FORM_ForceToKillFocus(FPDF_FORMHANDLE hHandle) {
   CPDFSDK_FormFillEnvironment* pFormFillEnv =
       HandleToCPDFSDKEnvironment(hHandle);
diff --git a/fpdfsdk/fpdfformfill_embeddertest.cpp b/fpdfsdk/fpdfformfill_embeddertest.cpp
index 8718a43..f304398 100644
--- a/fpdfsdk/fpdfformfill_embeddertest.cpp
+++ b/fpdfsdk/fpdfformfill_embeddertest.cpp
@@ -4,10 +4,13 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
+#include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/fx_system.h"
 #include "public/cpp/fpdf_deleters.h"
 #include "public/fpdf_formfill.h"
+#include "public/fpdf_fwlevent.h"
 #include "testing/embedder_test.h"
 #include "testing/embedder_test_mock_delegate.h"
 #include "testing/embedder_test_timer_handling_delegate.h"
@@ -17,7 +20,78 @@
 using testing::_;
 using testing::Return;
 
-class FPDFFormFillEmbeddertest : public EmbedderTest, public TestSaver {};
+class FPDFFormFillEmbeddertest : public EmbedderTest, public TestSaver {
+ protected:
+  void TypeTextIntoTextfield(FPDF_PAGE page, int num_chars) {
+    // Click on the textfield in text_form.pdf.
+    EXPECT_EQ(FPDF_FORMFIELD_TEXTFIELD,
+              FPDFPage_HasFormFieldAtPoint(form_handle(), page, 120.0, 120.0));
+    FORM_OnMouseMove(form_handle(), page, 0, 120.0, 120.0);
+    FORM_OnLButtonDown(form_handle(), page, 0, 120.0, 120.0);
+    FORM_OnLButtonUp(form_handle(), page, 0, 120.0, 120.0);
+
+    // Type text starting with 'A' to as many chars as specified by |num_chars|.
+    for (int i = 0; i < num_chars; ++i) {
+      FORM_OnChar(form_handle(), page, 'A' + i, 0);
+    }
+  }
+
+  // Navigates to form text field using the mouse and then selects text via the
+  // shift and specfied left or right arrow key.
+  void SelectTextWithKeyboard(FPDF_PAGE page,
+                              int num_chars,
+                              int arrow_key,
+                              double x,
+                              double y) {
+    // Navigate to starting position for selection.
+    FORM_OnMouseMove(form_handle(), page, 0, x, y);
+    FORM_OnLButtonDown(form_handle(), page, 0, x, y);
+    FORM_OnLButtonUp(form_handle(), page, 0, x, y);
+
+    // Hold down shift (and don't release until entire text is selected).
+    FORM_OnKeyDown(form_handle(), page, FWL_VKEY_Shift, 0);
+
+    // Select text char by char via left or right arrow key.
+    for (int i = 0; i < num_chars; ++i) {
+      FORM_OnKeyDown(form_handle(), page, arrow_key, FWL_EVENTFLAG_ShiftKey);
+      FORM_OnKeyUp(form_handle(), page, arrow_key, FWL_EVENTFLAG_ShiftKey);
+    }
+    FORM_OnKeyUp(form_handle(), page, FWL_VKEY_Shift, 0);
+  }
+
+  // Uses the mouse to navigate to form text field and select text.
+  void SelectTextWithMouse(FPDF_PAGE page,
+                           double start_x,
+                           double end_x,
+                           double y) {
+    // Navigate to starting position and click mouse.
+    FORM_OnMouseMove(form_handle(), page, 0, start_x, y);
+    FORM_OnLButtonDown(form_handle(), page, 0, start_x, y);
+
+    // Hold down mouse until reach end of desired selection.
+    FORM_OnMouseMove(form_handle(), page, 0, end_x, y);
+    FORM_OnLButtonUp(form_handle(), page, 0, end_x, y);
+  }
+
+  void CheckSelection(FPDF_PAGE page, const CFX_WideString& expected_string) {
+    // Calculate expected length for selected text.
+    int num_chars = expected_string.GetLength();
+
+    // Check actual selection against expected selection.
+    const unsigned long expected_length =
+        sizeof(unsigned short) * (num_chars + 1);
+    unsigned long sel_text_len =
+        FORM_GetSelectedText(form_handle(), page, nullptr, 0);
+    ASSERT_EQ(expected_length, sel_text_len);
+
+    std::vector<unsigned short> buf(sel_text_len);
+    EXPECT_EQ(expected_length, FORM_GetSelectedText(form_handle(), page,
+                                                    buf.data(), sel_text_len));
+
+    EXPECT_EQ(expected_string,
+              CFX_WideString::FromUTF16LE(buf.data(), num_chars));
+  }
+};
 
 TEST_F(FPDFFormFillEmbeddertest, FirstTest) {
   EmbedderTestMockDelegate mock;
@@ -282,3 +356,100 @@
       RenderPage(new_page.get()));
   CompareBitmap(new_bitmap.get(), 300, 300, md5_3);
 }
+
+TEST_F(FPDFFormFillEmbeddertest, GetSelectedTextEmptyAndBasicKeyboard) {
+  // Open file with form text field.
+  EXPECT_TRUE(OpenDocument("text_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Test empty selection.
+  CheckSelection(page, CFX_WideString(L""));
+
+  // Test basic selection.
+  TypeTextIntoTextfield(page, 3);
+  SelectTextWithKeyboard(page, 3, FWL_VKEY_Left, 123.0, 115.5);
+  CheckSelection(page, CFX_WideString(L"ABC"));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbeddertest, GetSelectedTextEmptyAndBasicMouse) {
+  // Open file with form text field.
+  EXPECT_TRUE(OpenDocument("text_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  // Test empty selection.
+  CheckSelection(page, CFX_WideString(L""));
+
+  // Test basic selection.
+  TypeTextIntoTextfield(page, 3);
+  SelectTextWithMouse(page, 125.0, 102.0, 115.5);
+  CheckSelection(page, CFX_WideString(L"ABC"));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbeddertest, GetSelectedTextFragmentsKeyBoard) {
+  // Open file with form text field.
+  EXPECT_TRUE(OpenDocument("text_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  TypeTextIntoTextfield(page, 12);
+
+  // Test selecting first character in forward direction.
+  // Navigate to starting position and click mouse.
+  SelectTextWithKeyboard(page, 1, FWL_VKEY_Right, 102.0, 115.5);
+  CheckSelection(page, CFX_WideString(L"A"));
+
+  // Test selecting entire long string in backwards direction.
+  SelectTextWithKeyboard(page, 12, FWL_VKEY_Left, 191.0, 115.5);
+  CheckSelection(page, CFX_WideString(L"ABCDEFGHIJKL"));
+
+  // Test selecting middle section in backwards direction.
+  SelectTextWithKeyboard(page, 6, FWL_VKEY_Left, 170.0, 115.5);
+  CheckSelection(page, CFX_WideString(L"DEFGHI"));
+
+  // Test selecting middle selection in forward direction.
+  SelectTextWithKeyboard(page, 6, FWL_VKEY_Right, 125.0, 115.5);
+  CheckSelection(page, CFX_WideString(L"DEFGHI"));
+
+  // Test selecting last character in backwards direction.
+  SelectTextWithKeyboard(page, 1, FWL_VKEY_Left, 191.0, 115.5);
+  CheckSelection(page, CFX_WideString(L"L"));
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFFormFillEmbeddertest, GetSelectedTextFragmentsMouse) {
+  // Open file with form text field.
+  EXPECT_TRUE(OpenDocument("text_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  TypeTextIntoTextfield(page, 12);
+
+  // Test selecting first character in forward direction.
+  SelectTextWithMouse(page, 102.0, 106.0, 115.5);
+  CheckSelection(page, CFX_WideString(L"A"));
+
+  // Test selecting entire long string in backwards direction.
+  SelectTextWithMouse(page, 191.0, 102.0, 115.5);
+  CheckSelection(page, CFX_WideString(L"ABCDEFGHIJKL"));
+
+  // Test selecting middle section in backwards direction.
+  SelectTextWithMouse(page, 170.0, 125.0, 115.5);
+  CheckSelection(page, CFX_WideString(L"DEFGHI"));
+
+  // Test selecting middle selection in forward direction.
+  SelectTextWithMouse(page, 125.0, 170.0, 115.5);
+  CheckSelection(page, CFX_WideString(L"DEFGHI"));
+
+  // Test selecting last character in backwards direction.
+  SelectTextWithMouse(page, 191.0, 186.0, 115.5);
+  CheckSelection(page, CFX_WideString(L"L"));
+
+  UnloadPage(page);
+}
diff --git a/fpdfsdk/fpdfview_c_api_test.c b/fpdfsdk/fpdfview_c_api_test.c
index 0deaf9d..05242c6 100644
--- a/fpdfsdk/fpdfview_c_api_test.c
+++ b/fpdfsdk/fpdfview_c_api_test.c
@@ -156,6 +156,7 @@
     CHK(FORM_OnKeyDown);
     CHK(FORM_OnKeyUp);
     CHK(FORM_OnChar);
+    CHK(FORM_GetSelectedText);
     CHK(FORM_ForceToKillFocus);
     CHK(FPDFPage_HasFormFieldAtPoint);
     CHK(FPDFPage_FormFieldZOrderAtPoint);
diff --git a/fpdfsdk/ipdfsdk_annothandler.h b/fpdfsdk/ipdfsdk_annothandler.h
index 636d161..08008eb 100644
--- a/fpdfsdk/ipdfsdk_annothandler.h
+++ b/fpdfsdk/ipdfsdk_annothandler.h
@@ -36,6 +36,7 @@
   virtual void ReleaseAnnot(CPDFSDK_Annot* pAnnot) = 0;
   virtual CFX_FloatRect GetViewBBox(CPDFSDK_PageView* pPageView,
                                     CPDFSDK_Annot* pAnnot) = 0;
+  virtual CFX_WideString GetSelectedText(CPDFSDK_Annot* pAnnot) = 0;
   virtual bool HitTest(CPDFSDK_PageView* pPageView,
                        CPDFSDK_Annot* pAnnot,
                        const CFX_PointF& point) = 0;
diff --git a/fpdfsdk/pdfwindow/cpwl_combo_box.cpp b/fpdfsdk/pdfwindow/cpwl_combo_box.cpp
index b69f989..2929aac 100644
--- a/fpdfsdk/pdfwindow/cpwl_combo_box.cpp
+++ b/fpdfsdk/pdfwindow/cpwl_combo_box.cpp
@@ -216,6 +216,13 @@
   CPWL_Wnd::KillFocus();
 }
 
+CFX_WideString CPWL_ComboBox::GetSelectedText() {
+  if (m_pEdit)
+    return m_pEdit->GetSelectedText();
+
+  return CFX_WideString();
+}
+
 CFX_WideString CPWL_ComboBox::GetText() const {
   if (m_pEdit) {
     return m_pEdit->GetText();
diff --git a/fpdfsdk/pdfwindow/cpwl_combo_box.h b/fpdfsdk/pdfwindow/cpwl_combo_box.h
index e814ca5..549374f 100644
--- a/fpdfsdk/pdfwindow/cpwl_combo_box.h
+++ b/fpdfsdk/pdfwindow/cpwl_combo_box.h
@@ -69,6 +69,7 @@
   CFX_FloatRect GetFocusRect() const override;
   void SetFocus() override;
   void KillFocus() override;
+  CFX_WideString GetSelectedText() override;
 
   void SetFillerNotify(IPWL_Filler_Notify* pNotify);
 
diff --git a/fpdfsdk/pdfwindow/cpwl_edit_ctrl.cpp b/fpdfsdk/pdfwindow/cpwl_edit_ctrl.cpp
index 9c22cea..3390696 100644
--- a/fpdfsdk/pdfwindow/cpwl_edit_ctrl.cpp
+++ b/fpdfsdk/pdfwindow/cpwl_edit_ctrl.cpp
@@ -53,6 +53,13 @@
   }
 }
 
+CFX_WideString CPWL_EditCtrl::GetSelectedText() {
+  if (m_pEdit)
+    return m_pEdit->GetSelText();
+
+  return CFX_WideString();
+}
+
 void CPWL_EditCtrl::RePosChildWnd() {
   m_pEdit->SetPlateRect(GetClientRect());
 }
diff --git a/fpdfsdk/pdfwindow/cpwl_edit_ctrl.h b/fpdfsdk/pdfwindow/cpwl_edit_ctrl.h
index 59bb623..1c4c925 100644
--- a/fpdfsdk/pdfwindow/cpwl_edit_ctrl.h
+++ b/fpdfsdk/pdfwindow/cpwl_edit_ctrl.h
@@ -61,6 +61,7 @@
   void SetFontSize(float fFontSize) override;
   float GetFontSize() const override;
   void SetCursor() override;
+  CFX_WideString GetSelectedText() override;
 
   void IOnSetScrollInfoY(float fPlateMin,
                          float fPlateMax,
diff --git a/fpdfsdk/pdfwindow/cpwl_edit_embeddertest.cpp b/fpdfsdk/pdfwindow/cpwl_edit_embeddertest.cpp
index 4a14b65..e206cd1 100644
--- a/fpdfsdk/pdfwindow/cpwl_edit_embeddertest.cpp
+++ b/fpdfsdk/pdfwindow/cpwl_edit_embeddertest.cpp
@@ -11,51 +11,115 @@
 #include "testing/embedder_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-class CPWLEditEmbeddertest : public EmbedderTest {};
+class CPWLEditEmbeddertest : public EmbedderTest {
+ protected:
+  void SetUp() override {
+    EmbedderTest::SetUp();
+    CreateAndInitializeFormPDF();
+  }
+
+  void TearDown() override {
+    UnloadPage(GetPage());
+    EmbedderTest::TearDown();
+  }
+
+  void CreateAndInitializeFormPDF() {
+    EXPECT_TRUE(OpenDocument("text_form.pdf"));
+    m_page = LoadPage(0);
+    ASSERT_TRUE(m_page);
+
+    CPDFSDK_FormFillEnvironment* pFormFillEnv =
+        static_cast<CPDFSDK_FormFillEnvironment*>(form_handle());
+
+    {
+      CBA_AnnotIterator iter(pFormFillEnv->GetPageView(0),
+                             CPDF_Annot::Subtype::WIDGET);
+      m_pAnnot = iter.GetFirstAnnot();
+      CPDFSDK_Annot* pLastAnnot = iter.GetLastAnnot();
+      ASSERT_EQ(m_pAnnot, pLastAnnot);
+      ASSERT_TRUE(m_pAnnot);
+      ASSERT_EQ(CPDF_Annot::Subtype::WIDGET, m_pAnnot->GetAnnotSubtype());
+    }
+
+    CFFL_InteractiveFormFiller* pInteractiveFormFiller =
+        pFormFillEnv->GetInteractiveFormFiller();
+    {
+      CPDFSDK_Annot::ObservedPtr pObserved(m_pAnnot);
+      EXPECT_TRUE(pInteractiveFormFiller->OnSetFocus(&pObserved, 0));
+    }
+
+    m_pFormFiller = pInteractiveFormFiller->GetFormFiller(m_pAnnot, false);
+    ASSERT_TRUE(m_pFormFiller);
+
+    CPWL_Wnd* pWindow =
+        m_pFormFiller->GetPDFWindow(pFormFillEnv->GetPageView(0), false);
+    ASSERT_TRUE(pWindow);
+    ASSERT_EQ(PWL_CLASSNAME_EDIT, pWindow->GetClassName());
+
+    m_pEdit = static_cast<CPWL_Edit*>(pWindow);
+  }
+
+  FPDF_PAGE GetPage() { return m_page; }
+  CPWL_Edit* GetCPWLEdit() { return m_pEdit; }
+  CFFL_FormFiller* GetCFFLFormFiller() { return m_pFormFiller; }
+  CPDFSDK_Annot* GetCPDFSDKAnnot() { return m_pAnnot; }
+
+ private:
+  FPDF_PAGE m_page;
+  CPWL_Edit* m_pEdit;
+  CFFL_FormFiller* m_pFormFiller;
+  CPDFSDK_Annot* m_pAnnot;
+};
 
 TEST_F(CPWLEditEmbeddertest, TypeText) {
-  EXPECT_TRUE(OpenDocument("text_form.pdf"));
-  FPDF_PAGE page = LoadPage(0);
-  ASSERT_TRUE(page);
+  EXPECT_TRUE(GetCPWLEdit()->GetText().IsEmpty());
+  EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'a', 0));
+  EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'b', 0));
+  EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'c', 0));
 
-  CPDFSDK_FormFillEnvironment* pFormFillEnv =
-      static_cast<CPDFSDK_FormFillEnvironment*>(form_handle());
+  EXPECT_STREQ(L"abc", GetCPWLEdit()->GetText().c_str());
+}
 
-  CPDFSDK_Annot* pAnnot;
-  {
-    CBA_AnnotIterator iter(pFormFillEnv->GetPageView(0),
-                           CPDF_Annot::Subtype::WIDGET);
-    pAnnot = iter.GetFirstAnnot();
-    CPDFSDK_Annot* pLastAnnot = iter.GetLastAnnot();
-    ASSERT_EQ(pAnnot, pLastAnnot);
-    ASSERT_TRUE(pAnnot);
-    ASSERT_EQ(CPDF_Annot::Subtype::WIDGET, pAnnot->GetAnnotSubtype());
+TEST_F(CPWLEditEmbeddertest, GetSelectedTextEmptyAndBasic) {
+  // Attempt to set selection before text has been typed to test that
+  // selection is identified as empty.
+  //
+  // Select from character index [0, 3) within form text field.
+  GetCPWLEdit()->SetSel(0, 3);
+  EXPECT_TRUE(GetCPWLEdit()->GetSelectedText().IsEmpty());
+
+  EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'a', 0));
+  EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'b', 0));
+  EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), 'c', 0));
+  GetCPWLEdit()->SetSel(0, 2);
+
+  EXPECT_STREQ(L"ab", GetCPWLEdit()->GetSelectedText().c_str());
+}
+
+TEST_F(CPWLEditEmbeddertest, GetSelectedTextFragments) {
+  for (int i = 0; i < 50; ++i) {
+    EXPECT_TRUE(GetCFFLFormFiller()->OnChar(GetCPDFSDKAnnot(), i + 'A', 0));
   }
 
-  CFFL_InteractiveFormFiller* pInteractiveFormFiller =
-      pFormFillEnv->GetInteractiveFormFiller();
-  {
-    CPDFSDK_Annot::ObservedPtr pObserved(pAnnot);
-    EXPECT_TRUE(pInteractiveFormFiller->OnSetFocus(&pObserved, 0));
-  }
+  GetCPWLEdit()->SetSel(0, 0);
+  EXPECT_TRUE(GetCPWLEdit()->GetSelectedText().IsEmpty());
 
-  CFFL_FormFiller* pFormFiller =
-      pInteractiveFormFiller->GetFormFiller(pAnnot, false);
-  ASSERT_TRUE(pFormFiller);
+  GetCPWLEdit()->SetSel(0, 1);
+  EXPECT_STREQ(L"A", GetCPWLEdit()->GetSelectedText().c_str());
 
-  CPWL_Wnd* pWindow =
-      pFormFiller->GetPDFWindow(pFormFillEnv->GetPageView(0), false);
-  ASSERT_TRUE(pWindow);
-  ASSERT_EQ(PWL_CLASSNAME_EDIT, pWindow->GetClassName());
+  GetCPWLEdit()->SetSel(0, -1);
+  EXPECT_STREQ(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqr",
+               GetCPWLEdit()->GetSelectedText().c_str());
 
-  CPWL_Edit* pEdit = static_cast<CPWL_Edit*>(pWindow);
-  EXPECT_TRUE(pEdit->GetText().IsEmpty());
+  GetCPWLEdit()->SetSel(-8, -1);
+  EXPECT_TRUE(GetCPWLEdit()->GetSelectedText().IsEmpty());
 
-  EXPECT_TRUE(pFormFiller->OnChar(pAnnot, 'a', 0));
-  EXPECT_TRUE(pFormFiller->OnChar(pAnnot, 'b', 0));
-  EXPECT_TRUE(pFormFiller->OnChar(pAnnot, 'c', 0));
+  GetCPWLEdit()->SetSel(23, 12);
+  EXPECT_STREQ(L"MNOPQRSTUVW", GetCPWLEdit()->GetSelectedText().c_str());
 
-  EXPECT_STREQ(L"abc", pEdit->GetText().c_str());
+  GetCPWLEdit()->SetSel(12, 23);
+  EXPECT_STREQ(L"MNOPQRSTUVW", GetCPWLEdit()->GetSelectedText().c_str());
 
-  UnloadPage(page);
+  GetCPWLEdit()->SetSel(49, 50);
+  EXPECT_STREQ(L"r", GetCPWLEdit()->GetSelectedText().c_str());
 }
diff --git a/fpdfsdk/pdfwindow/cpwl_wnd.cpp b/fpdfsdk/pdfwindow/cpwl_wnd.cpp
index 90a7930..d44b219 100644
--- a/fpdfsdk/pdfwindow/cpwl_wnd.cpp
+++ b/fpdfsdk/pdfwindow/cpwl_wnd.cpp
@@ -390,6 +390,10 @@
 PWL_IMPLEMENT_MOUSE_METHOD(OnMouseMove)
 #undef PWL_IMPLEMENT_MOUSE_METHOD
 
+CFX_WideString CPWL_Wnd::GetSelectedText() {
+  return CFX_WideString();
+}
+
 bool CPWL_Wnd::OnMouseWheel(short zDelta,
                             const CFX_PointF& point,
                             uint32_t nFlag) {
diff --git a/fpdfsdk/pdfwindow/cpwl_wnd.h b/fpdfsdk/pdfwindow/cpwl_wnd.h
index e7a4e23..7049849 100644
--- a/fpdfsdk/pdfwindow/cpwl_wnd.h
+++ b/fpdfsdk/pdfwindow/cpwl_wnd.h
@@ -229,6 +229,7 @@
   virtual void SetFontSize(float fFontSize);
   virtual float GetFontSize() const;
 
+  virtual CFX_WideString GetSelectedText();
   virtual CFX_FloatRect GetFocusRect() const;
   virtual CFX_FloatRect GetClientRect() const;