0

Restore focused annotation when focus comes back to the PDF

This CL adds restoring focus to the last focused element in PDF. Unit
tests have been added to validate the scenario.

Bug: 1057927
Change-Id: I760d2365671c610ce593016a0ac8fc439c7fbd77
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2061579
Commit-Queue: Ankit Kumar 🌪️ <ankk@microsoft.com>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Reviewed-by: Kevin Babbitt <kbabbitt@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#758774}
This commit is contained in:
Ankit Kumar
2020-04-14 09:32:32 +00:00
committed by Commit Bot
parent acb9858003
commit c5cbec6b4a
6 changed files with 170 additions and 4 deletions

@ -922,8 +922,7 @@ void OutOfProcessInstance::DidChangeView(const pp::View& view) {
}
void OutOfProcessInstance::DidChangeFocus(bool has_focus) {
if (!has_focus)
engine_->KillFormFocus();
engine_->UpdateFocus(has_focus);
}
void OutOfProcessInstance::GetPrintPresetOptionsFromDocument(

@ -452,6 +452,9 @@ class PDFEngine {
// Remove focus from form widgets, consolidating the user input.
virtual void KillFormFocus() = 0;
// Notify whether the PDF currently has the focus or not.
virtual void UpdateFocus(bool has_focus) = 0;
virtual uint32_t GetLoadedByteSize() = 0;
virtual bool ReadLoadedBytes(uint32_t length, void* buffer) = 0;
};

@ -347,6 +347,13 @@ wchar_t SimplifyForSearch(wchar_t c) {
}
}
PDFiumEngine::SetSelectedTextFunction g_set_selected_text_func_for_testing =
nullptr;
void SetSelectedText(pp::Instance* instance, const std::string& selected_text) {
pp::PDF::SetSelectedText(instance, selected_text.c_str());
}
} // namespace
void InitializeSDK(bool enable_v8) {
@ -426,6 +433,12 @@ void PDFiumEngine::SetDocumentLoaderForTesting(
doc_loader_set_for_testing_ = true;
}
// static
void PDFiumEngine::OverrideSetSelectedTextFunctionForTesting(
SetSelectedTextFunction function) {
g_set_selected_text_func_for_testing = function;
}
bool PDFiumEngine::New(const char* url, const char* headers) {
url_ = url;
if (headers)
@ -915,6 +928,40 @@ void PDFiumEngine::KillFormFocus() {
SetInFormTextArea(false);
}
void PDFiumEngine::UpdateFocus(bool has_focus) {
if (has_focus) {
focus_item_type_ = last_focused_item_type_;
if (focus_item_type_ == FocusElementType::kPage &&
PageIndexInBounds(last_focused_page_) &&
last_focused_annot_index_ != -1) {
ScopedFPDFAnnotation last_focused_annot(FPDFPage_GetAnnot(
pages_[last_focused_page_]->GetPage(), last_focused_annot_index_));
if (last_focused_annot) {
FPDF_BOOL ret = FORM_SetFocusedAnnot(form(), last_focused_annot.get());
DCHECK(ret);
}
}
} else {
last_focused_item_type_ = focus_item_type_;
if (focus_item_type_ == FocusElementType::kDocument) {
focus_item_type_ = FocusElementType::kNone;
} else if (focus_item_type_ == FocusElementType::kPage) {
FPDF_ANNOTATION last_focused_annot = nullptr;
FPDF_BOOL ret = FORM_GetFocusedAnnot(form(), &last_focused_page_,
&last_focused_annot);
DCHECK(ret);
if (PageIndexInBounds(last_focused_page_) && last_focused_annot) {
last_focused_annot_index_ = FPDFPage_GetAnnotIndex(
pages_[last_focused_page_]->GetPage(), last_focused_annot);
} else {
last_focused_annot_index_ = -1;
}
FPDFPage_CloseAnnot(last_focused_annot);
}
KillFormFocus();
}
}
uint32_t PDFiumEngine::GetLoadedByteSize() {
return doc_loader_->GetDocumentSize();
}
@ -3507,8 +3554,13 @@ void PDFiumEngine::SetInFormTextArea(bool in_form_text_area) {
// 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_)
pp::PDF::SetSelectedText(GetPluginInstance(), "");
if (in_form_text_area_) {
SetSelectedTextFunction set_selected_text_func =
g_set_selected_text_func_for_testing
? g_set_selected_text_func_for_testing
: &SetSelectedText;
set_selected_text_func(GetPluginInstance(), "");
}
client_->FormTextFieldFocusChange(in_form_text_area);
in_form_text_area_ = in_form_text_area;

@ -60,6 +60,11 @@ class PDFiumEngine : public PDFEngine,
// HandleDocumentLoad().
void SetDocumentLoaderForTesting(std::unique_ptr<DocumentLoader> loader);
using SetSelectedTextFunction = void (*)(pp::Instance* instance,
const std::string& selected_text);
static void OverrideSetSelectedTextFunctionForTesting(
SetSelectedTextFunction function);
// PDFEngine implementation.
bool New(const char* url, const char* headers) override;
void PageOffsetUpdated(const pp::Point& page_offset) override;
@ -150,6 +155,7 @@ class PDFiumEngine : public PDFEngine,
void OnDocumentComplete() override;
void OnDocumentCanceled() override;
void KillFormFocus() override;
void UpdateFocus(bool has_focus) override;
uint32_t GetLoadedByteSize() override;
bool ReadLoadedBytes(uint32_t length, void* buffer) override;
#if defined(PDF_ENABLE_XFA)
@ -699,6 +705,12 @@ class PDFiumEngine : public PDFEngine,
// The focus item type for the currently focused object.
FocusElementType focus_item_type_ = FocusElementType::kNone;
// Stores the last focused object's focus item type before PDF loses focus.
FocusElementType last_focused_item_type_ = FocusElementType::kNone;
// Stores the last focused annotation's index before PDF loses focus.
int last_focused_annot_index_ = -1;
// Holds the zero-based page index of the last page that had the focused
// object.
int last_focused_page_ = -1;

@ -244,6 +244,15 @@ class PDFiumEngineTabbingTest : public PDFiumTestBase {
return engine->last_focused_page_;
}
PDFiumEngine::FocusElementType GetLastFocusedElementType(
PDFiumEngine* engine) {
return engine->last_focused_item_type_;
}
int GetLastFocusedAnnotationIndex(PDFiumEngine* engine) {
return engine->last_focused_annot_index_;
}
protected:
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
@ -423,4 +432,88 @@ TEST_F(PDFiumEngineTabbingTest, NoFocusableItemTabbingTest) {
GetFocusedElementType(engine.get()));
}
TEST_F(PDFiumEngineTabbingTest, RestoringDocumentFocusTest) {
/*
* Document structure
* Document
* ++ Page 1
* ++++ Annotation
* ++++ Annotation
* ++ Page 2
* ++++ Annotation
*/
TestClient client;
std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
&client, FILE_PATH_LITERAL("annotation_form_fields.pdf"));
ASSERT_TRUE(engine);
ASSERT_EQ(2, engine->GetNumberOfPages());
EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
GetFocusedElementType(engine.get()));
EXPECT_EQ(-1, GetLastFocusedPage(engine.get()));
// Tabbing to bring the document into focus.
ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
GetFocusedElementType(engine.get()));
engine->UpdateFocus(/*has_focus=*/false);
EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
GetFocusedElementType(engine.get()));
EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
GetLastFocusedElementType(engine.get()));
EXPECT_EQ(-1, GetLastFocusedAnnotationIndex(engine.get()));
engine->UpdateFocus(/*has_focus=*/true);
EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument,
GetFocusedElementType(engine.get()));
}
TEST_F(PDFiumEngineTabbingTest, RestoringAnnotFocusTest) {
/*
* Document structure
* Document
* ++ Page 1
* ++++ Annotation
* ++++ Annotation
* ++ Page 2
* ++++ Annotation
*/
TestClient client;
std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
&client, FILE_PATH_LITERAL("annotation_form_fields.pdf"));
ASSERT_TRUE(engine);
ASSERT_EQ(2, engine->GetNumberOfPages());
EXPECT_EQ(PDFiumEngine::FocusElementType::kNone,
GetFocusedElementType(engine.get()));
EXPECT_EQ(-1, GetLastFocusedPage(engine.get()));
// Tabbing to bring last annotation of page 0 into focus.
ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
engine->UpdateFocus(/*has_focus=*/false);
EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
GetLastFocusedElementType(engine.get()));
EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
GetFocusedElementType(engine.get()));
EXPECT_EQ(0, GetLastFocusedAnnotationIndex(engine.get()));
engine->UpdateFocus(/*has_focus=*/true);
EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
GetFocusedElementType(engine.get()));
EXPECT_EQ(0, GetLastFocusedPage(engine.get()));
// Tabbing now should bring the second page's annotation to focus.
ASSERT_TRUE(HandleTabEvent(engine.get(), 0));
EXPECT_EQ(PDFiumEngine::FocusElementType::kPage,
GetFocusedElementType(engine.get()));
EXPECT_EQ(1, GetLastFocusedPage(engine.get()));
}
} // namespace chrome_pdf

@ -5,6 +5,7 @@
#include "pdf/pdfium/pdfium_test_base.h"
#include <memory>
#include <string>
#include <utility>
#include "build/build_config.h"
@ -24,6 +25,9 @@ bool IsValidLinkForTesting(const std::string& url) {
return !url.empty();
}
void SetSelectedTextForTesting(pp::Instance* instance,
const std::string& selected_text) {}
} // namespace
PDFiumTestBase::PDFiumTestBase() = default;
@ -41,11 +45,14 @@ bool PDFiumTestBase::IsRunningOnChromeOS() {
void PDFiumTestBase::SetUp() {
InitializePDFium();
PDFiumEngine::OverrideSetSelectedTextFunctionForTesting(
&SetSelectedTextForTesting);
PDFiumPage::SetIsValidLinkFunctionForTesting(&IsValidLinkForTesting);
}
void PDFiumTestBase::TearDown() {
PDFiumPage::SetIsValidLinkFunctionForTesting(nullptr);
PDFiumEngine::OverrideSetSelectedTextFunctionForTesting(nullptr);
FPDF_DestroyLibrary();
}