Add CFDE_TextEditEngine::CanGenerateCharacterInfo().

Extract the logic from CFDE_TextEditEngine::RebuildPieces(), so
CFWL_Edit can determine if CFDE_TextEditEngine::GetCharacterInfo()
should be called. This avoids the two classes being out of sync, which
can trigger a NOTREACHED() in GetCharacterInfo().

Bug: chromium:1230184
Change-Id: Ibe77d077f909542aa41a473a1fa166334fde3771
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/83195
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Hui Yingst <nigi@chromium.org>
diff --git a/xfa/fde/cfde_texteditengine.cpp b/xfa/fde/cfde_texteditengine.cpp
index c845ce8..1a37083 100644
--- a/xfa/fde/cfde_texteditengine.cpp
+++ b/xfa/fde/cfde_texteditengine.cpp
@@ -1041,7 +1041,7 @@
   text_piece_info_.clear();
 
   // Must have a font set in order to break the text.
-  if (text_length_ == 0 || !font_)
+  if (!CanGenerateCharacterInfo())
     return;
 
   bool initialized_bounding_box = false;
diff --git a/xfa/fde/cfde_texteditengine.h b/xfa/fde/cfde_texteditengine.h
index 396c0e9..86edc32 100644
--- a/xfa/fde/cfde_texteditengine.h
+++ b/xfa/fde/cfde_texteditengine.h
@@ -166,6 +166,10 @@
   // <start_idx, count>
   std::pair<size_t, size_t> BoundsForWordAt(size_t idx) const;
 
+  // Note that if CanGenerateCharacterInfo() returns false, then
+  // GetCharacterInfo() cannot be called.
+  bool CanGenerateCharacterInfo() const { return text_length_ > 0 && font_; }
+
   // Returns <bidi level, character rect>
   std::pair<int32_t, CFX_RectF> GetCharacterInfo(int32_t start_idx);
   std::vector<CFX_RectF> GetCharacterRectsInRange(int32_t start_idx,
diff --git a/xfa/fde/cfde_texteditengine_unittest.cpp b/xfa/fde/cfde_texteditengine_unittest.cpp
index 6d573dc..9dfd221 100644
--- a/xfa/fde/cfde_texteditengine_unittest.cpp
+++ b/xfa/fde/cfde_texteditengine_unittest.cpp
@@ -501,6 +501,26 @@
   EXPECT_EQ(11U, engine()->GetIndexForPoint({999999.0f, 9999999.0f}));
 }
 
+TEST_F(CFDE_TextEditEngineTest, CanGenerateCharacterInfo) {
+  RetainPtr<CFGAS_GEFont> font = engine()->GetFont();
+  ASSERT_TRUE(font);
+
+  // Has font but no text.
+  EXPECT_FALSE(engine()->CanGenerateCharacterInfo());
+
+  // Has font and text.
+  engine()->Insert(0, L"Hi!");
+  EXPECT_TRUE(engine()->CanGenerateCharacterInfo());
+
+  // Has text but no font.
+  engine()->SetFont(nullptr);
+  EXPECT_FALSE(engine()->CanGenerateCharacterInfo());
+
+  // Has no text and no font.
+  engine()->Clear();
+  EXPECT_FALSE(engine()->CanGenerateCharacterInfo());
+}
+
 TEST_F(CFDE_TextEditEngineTest, GetCharacterInfo) {
   std::pair<int32_t, CFX_RectF> char_info;
 
diff --git a/xfa/fwl/cfwl_edit.cpp b/xfa/fwl/cfwl_edit.cpp
index 7fb7c64..69d2bee 100644
--- a/xfa/fwl/cfwl_edit.cpp
+++ b/xfa/fwl/cfwl_edit.cpp
@@ -756,7 +756,7 @@
 
 void CFWL_Edit::UpdateCursorRect() {
   int32_t bidi_level;
-  if (m_pEditEngine->GetLength() > 0) {
+  if (m_pEditEngine->CanGenerateCharacterInfo()) {
     std::tie(bidi_level, m_CaretRect) =
         m_pEditEngine->GetCharacterInfo(m_CursorPosition);
   } else {