Improve focused form field tracking in the PDF Viewer.
Instead of just determining if a text form field area has focus or not, distinguish between text form field, non-text form field, and no form field. With this information, the PDF plugin can send "formFocusChange" messages to the PDF UI with a focused value of true for non-text form fields. The PDF UI can then avoid scrolling to correctly handle keyboard shortcuts when non-text form fields are in focus. Bug: 1266637 Change-Id: I39910d38dff16d3f4dea727899991ee91eba6f31 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3261665 Commit-Queue: Lei Zhang <thestig@chromium.org> Reviewed-by: Daniel Hosseinian <dhoss@chromium.org> Cr-Commit-Position: refs/heads/main@{#938357}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
135c5a69b2
commit
f2f4bef3d7
@ -102,6 +102,15 @@ class PDFEngine {
|
||||
// Maximum number of parameters a nameddest view can contain.
|
||||
static constexpr size_t kMaxViewParams = 4;
|
||||
|
||||
enum class FocusFieldType {
|
||||
// Focus is not on any form field.
|
||||
kNoFocus,
|
||||
// Focus is on a form text field or form combobox text field.
|
||||
kText,
|
||||
// Focus is on a non-text field.
|
||||
kNonText,
|
||||
};
|
||||
|
||||
// Named destination in a document.
|
||||
struct NamedDestination {
|
||||
// 0-based page number.
|
||||
@ -254,8 +263,8 @@ class PDFEngine {
|
||||
// Notifies the client about document load progress.
|
||||
virtual void DocumentLoadProgress(uint32_t available, uint32_t doc_size) {}
|
||||
|
||||
// Notifies the client about focus changes for form text fields.
|
||||
virtual void FormTextFieldFocusChange(bool in_focus) {}
|
||||
// Notifies the client about focus changes for form fields.
|
||||
virtual void FormFieldFocusChange(FocusFieldType type) {}
|
||||
|
||||
// Returns true if the plugin has been opened within print preview.
|
||||
virtual bool IsPrintPreview() const = 0;
|
||||
|
@ -423,7 +423,7 @@ void PdfViewPluginBase::DocumentLoadComplete() {
|
||||
RecordDocumentMetrics();
|
||||
|
||||
// Clear the focus state for on-screen keyboards.
|
||||
FormTextFieldFocusChange(false);
|
||||
FormFieldFocusChange(PDFEngine::FocusFieldType::kNoFocus);
|
||||
|
||||
if (IsPrintPreview())
|
||||
OnPrintPreviewLoaded();
|
||||
@ -496,13 +496,13 @@ void PdfViewPluginBase::DocumentLoadProgress(uint32_t available,
|
||||
SendLoadingProgress(progress);
|
||||
}
|
||||
|
||||
void PdfViewPluginBase::FormTextFieldFocusChange(bool in_focus) {
|
||||
void PdfViewPluginBase::FormFieldFocusChange(PDFEngine::FocusFieldType type) {
|
||||
base::Value message(base::Value::Type::DICTIONARY);
|
||||
message.SetStringKey("type", "formFocusChange");
|
||||
message.SetBoolKey("focused", in_focus);
|
||||
message.SetBoolKey("focused", type != PDFEngine::FocusFieldType::kNoFocus);
|
||||
SendMessage(std::move(message));
|
||||
|
||||
SetFormTextFieldInFocus(in_focus);
|
||||
SetFormTextFieldInFocus(type == PDFEngine::FocusFieldType::kText);
|
||||
}
|
||||
|
||||
bool PdfViewPluginBase::IsPrintPreview() const {
|
||||
|
@ -131,7 +131,7 @@ class PdfViewPluginBase : public PDFEngine::Client,
|
||||
void DocumentLoadFailed() override;
|
||||
void DocumentHasUnsupportedFeature(const std::string& feature) override;
|
||||
void DocumentLoadProgress(uint32_t available, uint32_t doc_size) override;
|
||||
void FormTextFieldFocusChange(bool in_focus) override;
|
||||
void FormFieldFocusChange(PDFEngine::FocusFieldType type) override;
|
||||
bool IsPrintPreview() const override;
|
||||
SkColor GetBackgroundColor() override;
|
||||
void SetIsSelecting(bool is_selecting) override;
|
||||
|
@ -718,15 +718,31 @@ TEST_F(PdfViewWebPluginTest, FormTextFieldFocusChangeUpdatesTextInputType) {
|
||||
EXPECT_CALL(*wrapper_ptr_, UpdateTextInputState);
|
||||
EXPECT_CALL(checkpoint, Call);
|
||||
EXPECT_CALL(*wrapper_ptr_, UpdateTextInputState);
|
||||
EXPECT_CALL(checkpoint, Call);
|
||||
EXPECT_CALL(*wrapper_ptr_, UpdateTextInputState);
|
||||
EXPECT_CALL(checkpoint, Call);
|
||||
EXPECT_CALL(*wrapper_ptr_, UpdateTextInputState);
|
||||
}
|
||||
|
||||
plugin_->FormTextFieldFocusChange(true);
|
||||
plugin_->FormFieldFocusChange(PDFEngine::FocusFieldType::kText);
|
||||
EXPECT_EQ(blink::WebTextInputType::kWebTextInputTypeText,
|
||||
wrapper_ptr_->widget_text_input_type());
|
||||
|
||||
checkpoint.Call();
|
||||
|
||||
plugin_->FormTextFieldFocusChange(false);
|
||||
plugin_->FormFieldFocusChange(PDFEngine::FocusFieldType::kNoFocus);
|
||||
EXPECT_EQ(blink::WebTextInputType::kWebTextInputTypeNone,
|
||||
wrapper_ptr_->widget_text_input_type());
|
||||
|
||||
checkpoint.Call();
|
||||
|
||||
plugin_->FormFieldFocusChange(PDFEngine::FocusFieldType::kText);
|
||||
EXPECT_EQ(blink::WebTextInputType::kWebTextInputTypeText,
|
||||
wrapper_ptr_->widget_text_input_type());
|
||||
|
||||
checkpoint.Call();
|
||||
|
||||
plugin_->FormFieldFocusChange(PDFEngine::FocusFieldType::kNonText);
|
||||
EXPECT_EQ(blink::WebTextInputType::kWebTextInputTypeNone,
|
||||
wrapper_ptr_->widget_text_input_type());
|
||||
}
|
||||
|
@ -1040,7 +1040,7 @@ std::vector<uint8_t> PDFiumEngine::PrintPagesAsPdf(
|
||||
|
||||
void PDFiumEngine::KillFormFocus() {
|
||||
FORM_ForceToKillFocus(form());
|
||||
SetInFormTextArea(false);
|
||||
SetFieldFocus(FocusFieldType::kNoFocus);
|
||||
}
|
||||
|
||||
void PDFiumEngine::UpdateFocus(bool has_focus) {
|
||||
@ -1137,7 +1137,7 @@ void PDFiumEngine::SetFormSelectedText(FPDF_FORMHANDLE form_handle,
|
||||
std::string selected_form_text = selected_form_text_;
|
||||
selected_form_text_ = base::UTF16ToUTF8(selected_form_text16);
|
||||
if (selected_form_text != selected_form_text_) {
|
||||
DCHECK(in_form_text_area_);
|
||||
DCHECK_EQ(focus_field_type_, FocusFieldType::kText);
|
||||
client_->SetSelectedText(selected_form_text_);
|
||||
}
|
||||
}
|
||||
@ -1263,10 +1263,10 @@ bool PDFiumEngine::OnLeftMouseDown(const blink::WebMouseEvent& event) {
|
||||
|
||||
if (form_type != FPDF_FORMFIELD_UNKNOWN) {
|
||||
// FORM_OnLButton*() will trigger a callback to
|
||||
// OnFocusedAnnotationUpdated() which will call SetInFormTextArea().
|
||||
// Destroy SelectionChangeInvalidator object before SetInFormTextArea()
|
||||
// changes plugin's focus to be in form text area. This way, regular text
|
||||
// selection can be cleared when a user clicks into a form text area
|
||||
// OnFocusedAnnotationUpdated() which will call SetFieldFocus().
|
||||
// Destroy SelectionChangeInvalidator object before SetFieldFocus()
|
||||
// changes plugin's focus to be `FocusFieldType::kText`. This way, regular
|
||||
// text selection can be cleared when a user clicks into a form text area
|
||||
// because the pp::PDF::SetSelectedText() call in
|
||||
// ~SelectionChangeInvalidator() still goes to the Mimehandler
|
||||
// (not the Renderer).
|
||||
@ -1284,7 +1284,7 @@ bool PDFiumEngine::OnLeftMouseDown(const blink::WebMouseEvent& event) {
|
||||
if (form_type != FPDF_FORMFIELD_UNKNOWN)
|
||||
return true; // Return now before we get into the selection code.
|
||||
}
|
||||
SetInFormTextArea(false);
|
||||
SetFieldFocus(FocusFieldType::kNoFocus);
|
||||
|
||||
if (area != PDFiumPage::TEXT_AREA)
|
||||
return true; // Return true so WebKit doesn't do its own highlighting.
|
||||
@ -1356,13 +1356,12 @@ bool PDFiumEngine::OnRightMouseDown(const blink::WebMouseEvent& event) {
|
||||
}
|
||||
|
||||
// Handle the case when focus starts inside a form text area.
|
||||
if (in_form_text_area_) {
|
||||
if (focus_field_type_ == FocusFieldType::kText) {
|
||||
if (is_form_text_area) {
|
||||
FORM_OnFocus(form(), page, 0, page_x, page_y);
|
||||
} else {
|
||||
// Transition out of a form text area.
|
||||
FORM_ForceToKillFocus(form());
|
||||
SetInFormTextArea(false);
|
||||
KillFormFocus();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1396,7 +1395,7 @@ bool PDFiumEngine::NavigateToLinkDestination(
|
||||
WindowOpenDisposition disposition) {
|
||||
if (area == PDFiumPage::WEBLINK_AREA) {
|
||||
client_->NavigateTo(target.url, disposition);
|
||||
SetInFormTextArea(false);
|
||||
SetFieldFocus(FocusFieldType::kNoFocus);
|
||||
return true;
|
||||
}
|
||||
if (area == PDFiumPage::DOCLINK_AREA) {
|
||||
@ -1417,7 +1416,7 @@ bool PDFiumEngine::NavigateToLinkDestination(
|
||||
|
||||
client_->NavigateTo(parameters, disposition);
|
||||
}
|
||||
SetInFormTextArea(false);
|
||||
SetFieldFocus(FocusFieldType::kNoFocus);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -1711,7 +1710,7 @@ bool PDFiumEngine::OnKeyUp(const blink::WebKeyboardEvent& event) {
|
||||
|
||||
// Check if form text selection needs to be updated.
|
||||
FPDF_PAGE page = pages_[last_focused_page_]->GetPage();
|
||||
if (in_form_text_area_)
|
||||
if (focus_field_type_ == FocusFieldType::kText)
|
||||
SetFormSelectedText(form(), page);
|
||||
|
||||
return !!FORM_OnKeyUp(form(), page, event.windows_key_code,
|
||||
@ -2316,7 +2315,7 @@ void PDFiumEngine::SelectAll() {
|
||||
if (IsReadOnly())
|
||||
return;
|
||||
|
||||
if (in_form_text_area_) {
|
||||
if (focus_field_type_ == FocusFieldType::kText) {
|
||||
if (PageIndexInBounds(last_focused_page_))
|
||||
FORM_SelectAllText(form(), pages_[last_focused_page_]->GetPage());
|
||||
return;
|
||||
@ -3709,7 +3708,7 @@ void PDFiumEngine::GetRegion(const gfx::Point& location,
|
||||
}
|
||||
|
||||
void PDFiumEngine::OnSelectionTextChanged() {
|
||||
DCHECK(!in_form_text_area_);
|
||||
DCHECK_NE(focus_field_type_, FocusFieldType::kText);
|
||||
client_->SetSelectedText(GetSelectedText());
|
||||
}
|
||||
|
||||
@ -3798,20 +3797,21 @@ void PDFiumEngine::EnteredEditMode() {
|
||||
client_->EnteredEditMode();
|
||||
}
|
||||
|
||||
void PDFiumEngine::SetInFormTextArea(bool in_form_text_area) {
|
||||
void PDFiumEngine::SetFieldFocus(PDFEngine::FocusFieldType type) {
|
||||
// If focus was previously in form text area, clear form text selection.
|
||||
// Clearing needs to be done before changing focus to ensure the correct
|
||||
// observer is notified of the change in selection. When `in_form_text_area_`
|
||||
// is true, this is the Renderer. After it flips, the MimeHandler is notified.
|
||||
if (in_form_text_area_) {
|
||||
// observer is notified of the change in selection. When `focus_field_type_`
|
||||
// is set to `FocusFieldType::kText`, this is the Renderer. After it flips,
|
||||
// the MimeHandler is notified.
|
||||
if (focus_field_type_ == FocusFieldType::kText) {
|
||||
client_->SetSelectedText("");
|
||||
}
|
||||
|
||||
client_->FormTextFieldFocusChange(in_form_text_area);
|
||||
in_form_text_area_ = in_form_text_area;
|
||||
client_->FormFieldFocusChange(type);
|
||||
focus_field_type_ = type;
|
||||
|
||||
// Clear `editable_form_text_area_` when focus no longer in form text area.
|
||||
if (!in_form_text_area_)
|
||||
if (focus_field_type_ != FocusFieldType::kText)
|
||||
editable_form_text_area_ = false;
|
||||
}
|
||||
|
||||
@ -3940,7 +3940,7 @@ void PDFiumEngine::OnFocusedAnnotationUpdated(FPDF_ANNOTATION annot,
|
||||
SetLinkUnderCursorForAnnotation(annot, page_index);
|
||||
int form_type = FPDFAnnot_GetFormFieldType(form(), annot);
|
||||
if (form_type <= FPDF_FORMFIELD_UNKNOWN) {
|
||||
SetInFormTextArea(false);
|
||||
SetFieldFocus(FocusFieldType::kNoFocus);
|
||||
return;
|
||||
}
|
||||
bool is_form_text_area =
|
||||
@ -3949,7 +3949,8 @@ void PDFiumEngine::OnFocusedAnnotationUpdated(FPDF_ANNOTATION annot,
|
||||
SelectionChangeInvalidator selection_invalidator(this);
|
||||
selection_.clear();
|
||||
}
|
||||
SetInFormTextArea(is_form_text_area);
|
||||
SetFieldFocus(is_form_text_area ? FocusFieldType::kText
|
||||
: FocusFieldType::kNonText);
|
||||
editable_form_text_area_ =
|
||||
is_form_text_area && IsAnnotationAnEditableFormTextArea(annot, form_type);
|
||||
|
||||
|
@ -555,9 +555,8 @@ class PDFiumEngine : public PDFEngine,
|
||||
// within form text fields.
|
||||
void SetSelecting(bool selecting);
|
||||
|
||||
// Sets whether or not focus is in form text field or form combobox text
|
||||
// field.
|
||||
void SetInFormTextArea(bool in_form_text_area);
|
||||
// Sets what type of field has focus.
|
||||
void SetFieldFocus(PDFEngine::FocusFieldType type);
|
||||
|
||||
// Sets whether or not left mouse button is currently being held down.
|
||||
void SetMouseLeftButtonDown(bool is_mouse_left_button_down);
|
||||
@ -715,13 +714,6 @@ class PDFiumEngine : public PDFEngine,
|
||||
// Text selection within form text fields and form combobox text fields.
|
||||
std::string selected_form_text_;
|
||||
|
||||
// True if focus is in form text field or form combobox text field.
|
||||
bool in_form_text_area_ = false;
|
||||
|
||||
// True if the form text area currently in focus is not read only, and is a
|
||||
// form text field or user-editable form combobox text field.
|
||||
bool editable_form_text_area_ = false;
|
||||
|
||||
// True if left mouse button is currently being held down.
|
||||
bool mouse_left_button_down_ = false;
|
||||
|
||||
@ -760,6 +752,13 @@ class PDFiumEngine : public PDFEngine,
|
||||
// Set to true when updating plugin focus.
|
||||
bool updating_focus_ = false;
|
||||
|
||||
// True if `focus_field_type_` is currently set to `FocusFieldType::kText` and
|
||||
// the focused form text area is not read-only.
|
||||
bool editable_form_text_area_ = false;
|
||||
|
||||
// The type of the currently focused form field.
|
||||
FocusFieldType focus_field_type_ = FocusFieldType::kNoFocus;
|
||||
|
||||
// The focus element type for the currently focused object.
|
||||
FocusElementType focus_element_type_ = FocusElementType::kNone;
|
||||
|
||||
|
@ -705,8 +705,8 @@ class PDFiumEngineTabbingTest : public PDFiumTestBase {
|
||||
return engine->last_focused_annot_index_;
|
||||
}
|
||||
|
||||
bool IsInFormTextArea(PDFiumEngine* engine) {
|
||||
return engine->in_form_text_area_;
|
||||
PDFEngine::FocusFieldType FormFocusFieldType(PDFiumEngine* engine) {
|
||||
return engine->focus_field_type_;
|
||||
}
|
||||
|
||||
size_t GetSelectionSize(PDFiumEngine* engine) {
|
||||
@ -1139,24 +1139,29 @@ TEST_F(PDFiumEngineTabbingTest, VerifyFormFieldStatesOnTabbing) {
|
||||
ASSERT_TRUE(engine);
|
||||
ASSERT_EQ(1, engine->GetNumberOfPages());
|
||||
|
||||
// Bring focus to the document.
|
||||
ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
|
||||
EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
|
||||
GetFocusedElementType(engine.get()));
|
||||
EXPECT_EQ(PDFEngine::FocusFieldType::kNoFocus,
|
||||
FormFocusFieldType(engine.get()));
|
||||
EXPECT_FALSE(engine->CanEditText());
|
||||
|
||||
// Bring focus to the text field.
|
||||
// Bring focus to the text field on the page.
|
||||
ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
|
||||
EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
|
||||
GetFocusedElementType(engine.get()));
|
||||
EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
|
||||
EXPECT_TRUE(IsInFormTextArea(engine.get()));
|
||||
EXPECT_EQ(PDFEngine::FocusFieldType::kText, FormFocusFieldType(engine.get()));
|
||||
EXPECT_TRUE(engine->CanEditText());
|
||||
|
||||
// Bring focus to the button.
|
||||
// Bring focus to the button on the page.
|
||||
ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
|
||||
EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
|
||||
GetFocusedElementType(engine.get()));
|
||||
EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
|
||||
EXPECT_FALSE(IsInFormTextArea(engine.get()));
|
||||
EXPECT_EQ(PDFEngine::FocusFieldType::kNonText,
|
||||
FormFocusFieldType(engine.get()));
|
||||
EXPECT_FALSE(engine->CanEditText());
|
||||
}
|
||||
|
||||
@ -1328,7 +1333,10 @@ class ReadOnlyTestClient : public TestClient {
|
||||
ReadOnlyTestClient& operator=(const ReadOnlyTestClient&) = delete;
|
||||
|
||||
// Mock PDFEngine::Client methods.
|
||||
MOCK_METHOD(void, FormTextFieldFocusChange, (bool), (override));
|
||||
MOCK_METHOD(void,
|
||||
FormFieldFocusChange,
|
||||
(PDFEngine::FocusFieldType),
|
||||
(override));
|
||||
MOCK_METHOD(void, SetSelectedText, (const std::string&), (override));
|
||||
};
|
||||
|
||||
@ -1342,13 +1350,15 @@ TEST_F(PDFiumEngineReadOnlyTest, KillFormFocus) {
|
||||
|
||||
// Setting read-only mode should kill form focus.
|
||||
EXPECT_FALSE(engine->IsReadOnly());
|
||||
EXPECT_CALL(client, FormTextFieldFocusChange(false));
|
||||
EXPECT_CALL(client,
|
||||
FormFieldFocusChange(PDFEngine::FocusFieldType::kNoFocus));
|
||||
engine->SetReadOnly(true);
|
||||
|
||||
// Attempting to focus during read-only mode should once more trigger a
|
||||
// killing of form focus.
|
||||
EXPECT_TRUE(engine->IsReadOnly());
|
||||
EXPECT_CALL(client, FormTextFieldFocusChange(false));
|
||||
EXPECT_CALL(client,
|
||||
FormFieldFocusChange(PDFEngine::FocusFieldType::kNoFocus));
|
||||
engine->UpdateFocus(true);
|
||||
}
|
||||
|
||||
|
@ -146,7 +146,7 @@ void PreviewModeClient::DocumentHasUnsupportedFeature(
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
void PreviewModeClient::FormTextFieldFocusChange(bool in_focus) {
|
||||
void PreviewModeClient::FormFieldFocusChange(PDFEngine::FocusFieldType type) {
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ class PreviewModeClient : public PDFEngine::Client {
|
||||
void DocumentLoadComplete() override;
|
||||
void DocumentLoadFailed() override;
|
||||
void DocumentHasUnsupportedFeature(const std::string& feature) override;
|
||||
void FormTextFieldFocusChange(bool in_focus) override;
|
||||
void FormFieldFocusChange(PDFEngine::FocusFieldType type) override;
|
||||
bool IsPrintPreview() const override;
|
||||
SkColor GetBackgroundColor() override;
|
||||
void SetSelectedText(const std::string& selected_text) override;
|
||||
|
Reference in New Issue
Block a user