[unseasoned-pdf] Handle IME input.
Port applicable PepperWebPluginImpl and PepperPluginInstanceImpl logic to PdfViewWebPlugin. This provides the same basic level of IME support as the PPAPI-based PDF Viewer. Bug: 1230168 Change-Id: I9611c48eb61e527421ee8cf9d0c073de48acabab Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3184085 Commit-Queue: Lei Zhang <thestig@chromium.org> Reviewed-by: K. Moon <kmoon@chromium.org> Cr-Commit-Position: refs/heads/main@{#925914}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
0d20cc78bc
commit
a5482d7083
@ -13,6 +13,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/i18n/char_iterator.h"
|
||||
#include "base/i18n/string_search.h"
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/notreached.h"
|
||||
@ -69,6 +70,7 @@
|
||||
#include "ui/base/cursor/cursor.h"
|
||||
#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
|
||||
#include "ui/display/screen_info.h"
|
||||
#include "ui/events/base_event_utils.h"
|
||||
#include "ui/events/blink/blink_event_util.h"
|
||||
#include "ui/events/keycodes/keyboard_codes.h"
|
||||
#include "ui/gfx/geometry/point.h"
|
||||
@ -595,10 +597,40 @@ void PdfViewWebPlugin::RotateView(blink::WebPlugin::RotationType type) {
|
||||
}
|
||||
}
|
||||
|
||||
bool PdfViewWebPlugin::ShouldDispatchImeEventsToPlugin() {
|
||||
return true;
|
||||
}
|
||||
|
||||
blink::WebTextInputType PdfViewWebPlugin::GetPluginTextInputType() {
|
||||
return text_input_type_;
|
||||
}
|
||||
|
||||
gfx::Rect PdfViewWebPlugin::GetPluginCaretBounds() {
|
||||
return caret_rect_;
|
||||
}
|
||||
|
||||
void PdfViewWebPlugin::ImeSetCompositionForPlugin(
|
||||
const blink::WebString& text,
|
||||
const std::vector<ui::ImeTextSpan>& /*ime_text_spans*/,
|
||||
const gfx::Range& /*replacement_range*/,
|
||||
int /*selection_start*/,
|
||||
int /*selection_end*/) {
|
||||
composition_text_ = text;
|
||||
}
|
||||
|
||||
void PdfViewWebPlugin::ImeCommitTextForPlugin(
|
||||
const blink::WebString& text,
|
||||
const std::vector<ui::ImeTextSpan>& /*ime_text_spans*/,
|
||||
const gfx::Range& /*replacement_range*/,
|
||||
int /*relative_cursor_pos*/) {
|
||||
HandleImeCommit(text);
|
||||
}
|
||||
|
||||
void PdfViewWebPlugin::ImeFinishComposingTextForPlugin(
|
||||
bool /*keep_selection*/) {
|
||||
HandleImeCommit(composition_text_);
|
||||
}
|
||||
|
||||
void PdfViewWebPlugin::UpdateCursor(ui::mojom::CursorType new_cursor_type) {
|
||||
set_cursor_type(new_cursor_type);
|
||||
}
|
||||
@ -612,6 +644,11 @@ void PdfViewWebPlugin::NotifySelectedFindResultChanged(int current_find_index) {
|
||||
current_find_index + 1);
|
||||
}
|
||||
|
||||
void PdfViewWebPlugin::CaretChanged(const gfx::Rect& caret_rect) {
|
||||
caret_rect_ = gfx::ScaleToEnclosingRectSafe(
|
||||
caret_rect + available_area().OffsetFromOrigin(), device_to_css_scale_);
|
||||
}
|
||||
|
||||
void PdfViewWebPlugin::Alert(const std::string& message) {
|
||||
container_wrapper_->Alert(blink::WebString::FromUTF8(message));
|
||||
}
|
||||
@ -957,6 +994,32 @@ bool PdfViewWebPlugin::Redo() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void PdfViewWebPlugin::HandleImeCommit(const blink::WebString& text) {
|
||||
if (text.IsEmpty())
|
||||
return;
|
||||
|
||||
std::u16string text16 = text.Utf16();
|
||||
composition_text_.Reset();
|
||||
|
||||
size_t i = 0;
|
||||
for (base::i18n::UTF16CharIterator iterator(text16); iterator.Advance();) {
|
||||
blink::WebKeyboardEvent char_event(blink::WebInputEvent::Type::kChar,
|
||||
blink::WebInputEvent::kNoModifiers,
|
||||
ui::EventTimeForNow());
|
||||
char_event.windows_key_code = text16[i];
|
||||
char_event.native_key_code = text16[i];
|
||||
|
||||
for (const size_t char_start = i; i < iterator.array_pos(); ++i) {
|
||||
char_event.text[i - char_start] = text16[i];
|
||||
char_event.unmodified_text[i - char_start] = text16[i];
|
||||
}
|
||||
|
||||
blink::WebCoalescedInputEvent input_event(char_event, ui::LatencyInfo());
|
||||
ui::Cursor dummy_cursor_info;
|
||||
HandleInputEvent(input_event, &dummy_cursor_info);
|
||||
}
|
||||
}
|
||||
|
||||
void PdfViewWebPlugin::OnInvokePrintDialog(int32_t /*result*/) {
|
||||
client_->Print(Container()->GetElement());
|
||||
}
|
||||
|
@ -198,11 +198,27 @@ class PdfViewWebPlugin final : public PdfViewPluginBase,
|
||||
void StopFind() override;
|
||||
bool CanRotateView() override;
|
||||
void RotateView(blink::WebPlugin::RotationType type) override;
|
||||
|
||||
bool ShouldDispatchImeEventsToPlugin() override;
|
||||
blink::WebTextInputType GetPluginTextInputType() override;
|
||||
gfx::Rect GetPluginCaretBounds() override;
|
||||
void ImeSetCompositionForPlugin(
|
||||
const blink::WebString& text,
|
||||
const std::vector<ui::ImeTextSpan>& ime_text_spans,
|
||||
const gfx::Range& replacement_range,
|
||||
int selection_start,
|
||||
int selection_end) override;
|
||||
void ImeCommitTextForPlugin(
|
||||
const blink::WebString& text,
|
||||
const std::vector<ui::ImeTextSpan>& ime_text_spans,
|
||||
const gfx::Range& replacement_range,
|
||||
int relative_cursor_pos) override;
|
||||
void ImeFinishComposingTextForPlugin(bool keep_selection) override;
|
||||
|
||||
// PdfViewPluginBase:
|
||||
void UpdateCursor(ui::mojom::CursorType new_cursor_type) override;
|
||||
void NotifySelectedFindResultChanged(int current_find_index) override;
|
||||
void CaretChanged(const gfx::Rect& caret_rect) override;
|
||||
void Alert(const std::string& message) override;
|
||||
bool Confirm(const std::string& message) override;
|
||||
std::string Prompt(const std::string& question,
|
||||
@ -302,6 +318,10 @@ class PdfViewWebPlugin final : public PdfViewPluginBase,
|
||||
bool Undo();
|
||||
bool Redo();
|
||||
|
||||
// Helper method for converting IME text to input events.
|
||||
// TODO(crbug.com/1253665): Consider handling composition events.
|
||||
void HandleImeCommit(const blink::WebString& text);
|
||||
|
||||
// Callback to print without re-entrancy issues. The callback prevents the
|
||||
// invocation of printing in the middle of an event handler, which is risky;
|
||||
// see crbug.com/66334.
|
||||
@ -331,6 +351,10 @@ class PdfViewWebPlugin final : public PdfViewPluginBase,
|
||||
blink::WebTextInputType text_input_type_ =
|
||||
blink::WebTextInputType::kWebTextInputTypeNone;
|
||||
|
||||
gfx::Rect caret_rect_;
|
||||
|
||||
blink::WebString composition_text_;
|
||||
|
||||
// Whether the plugin element currently has focus.
|
||||
bool has_focus_ = false;
|
||||
|
||||
|
@ -5,8 +5,10 @@
|
||||
#include "pdf/pdf_view_web_plugin.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "base/strings/string_piece.h"
|
||||
#include "cc/paint/paint_canvas.h"
|
||||
#include "cc/test/pixel_comparator.h"
|
||||
#include "cc/test/pixel_test_utils.h"
|
||||
@ -18,6 +20,7 @@
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "third_party/blink/public/common/input/web_coalesced_input_event.h"
|
||||
#include "third_party/blink/public/common/input/web_input_event.h"
|
||||
#include "third_party/blink/public/common/input/web_keyboard_event.h"
|
||||
#include "third_party/blink/public/common/input/web_mouse_event.h"
|
||||
#include "third_party/blink/public/common/input/web_pointer_properties.h"
|
||||
#include "third_party/blink/public/platform/web_string.h"
|
||||
@ -30,6 +33,8 @@
|
||||
#include "third_party/skia/include/core/SkColor.h"
|
||||
#include "ui/base/cursor/cursor.h"
|
||||
#include "ui/events/blink/blink_event_util.h"
|
||||
#include "ui/events/keycodes/dom/dom_code.h"
|
||||
#include "ui/events/keycodes/dom/dom_key.h"
|
||||
#include "ui/gfx/canvas.h"
|
||||
#include "ui/gfx/geometry/point_f.h"
|
||||
#include "ui/gfx/geometry/rect.h"
|
||||
@ -83,6 +88,20 @@ MATCHER(SearchStringResultEq, "") {
|
||||
return l.start_index == r.start_index && l.length == r.length;
|
||||
}
|
||||
|
||||
MATCHER_P(IsExpectedImeKeyEvent, expected_text, "") {
|
||||
if (arg.GetType() != blink::WebInputEvent::Type::kChar)
|
||||
return false;
|
||||
|
||||
const auto& event = static_cast<const blink::WebKeyboardEvent&>(arg);
|
||||
return event.GetModifiers() == blink::WebInputEvent::kNoModifiers &&
|
||||
event.windows_key_code == expected_text[0] &&
|
||||
event.native_key_code == expected_text[0] &&
|
||||
event.dom_code == static_cast<int>(ui::DomCode::NONE) &&
|
||||
event.dom_key == ui::DomKey::NONE && !event.is_system_key &&
|
||||
!event.is_browser_shortcut && event.text == expected_text &&
|
||||
event.unmodified_text == expected_text;
|
||||
}
|
||||
|
||||
// Generates the expected `SkBitmap` with `paint_color` filled in the expected
|
||||
// clipped area and `kDefaultColor` as the background color.
|
||||
SkBitmap GenerateExpectedBitmapForPaint(const gfx::Rect& expected_clipped_rect,
|
||||
@ -560,6 +579,106 @@ TEST_F(PdfViewWebPluginMouseEventsTest,
|
||||
EXPECT_EQ(gfx::PointF(-20.0f, 0.0f), event->PositionInWidget());
|
||||
}
|
||||
|
||||
class PdfViewWebPluginImeTest : public PdfViewWebPluginTest {
|
||||
public:
|
||||
class TestPDFiumEngineForIme : public TestPDFiumEngine {
|
||||
public:
|
||||
explicit TestPDFiumEngineForIme(PDFEngine::Client* client)
|
||||
: TestPDFiumEngine(client) {}
|
||||
|
||||
// TestPDFiumEngine:
|
||||
MOCK_METHOD(bool,
|
||||
HandleInputEvent,
|
||||
(const blink::WebInputEvent&),
|
||||
(override));
|
||||
};
|
||||
|
||||
std::unique_ptr<TestPDFiumEngine> CreateEngine() override {
|
||||
return std::make_unique<TestPDFiumEngineForIme>(plugin_.get());
|
||||
}
|
||||
|
||||
TestPDFiumEngineForIme* engine() {
|
||||
return static_cast<TestPDFiumEngineForIme*>(engine_ptr_);
|
||||
}
|
||||
|
||||
void TestImeSetCompositionForPlugin(const blink::WebString& text) {
|
||||
EXPECT_CALL(*engine(), HandleInputEvent).Times(0);
|
||||
plugin_->ImeSetCompositionForPlugin(text, std::vector<ui::ImeTextSpan>(),
|
||||
gfx::Range(),
|
||||
/*selection_start=*/0,
|
||||
/*selection_end=*/0);
|
||||
}
|
||||
|
||||
void TestImeFinishComposingTextForPlugin(
|
||||
const blink::WebString& expected_text) {
|
||||
InSequence sequence;
|
||||
std::u16string expected_text16 = expected_text.Utf16();
|
||||
if (expected_text16.size()) {
|
||||
for (const auto& c : expected_text16) {
|
||||
base::StringPiece16 expected_key(&c, 1);
|
||||
EXPECT_CALL(*engine(),
|
||||
HandleInputEvent(IsExpectedImeKeyEvent(expected_key)))
|
||||
.WillOnce(Return(true));
|
||||
}
|
||||
} else {
|
||||
EXPECT_CALL(*engine(), HandleInputEvent).Times(0);
|
||||
}
|
||||
plugin_->ImeFinishComposingTextForPlugin(false);
|
||||
}
|
||||
|
||||
void TestImeCommitTextForPlugin(const blink::WebString& text) {
|
||||
InSequence sequence;
|
||||
std::u16string expected_text16 = text.Utf16();
|
||||
if (expected_text16.size()) {
|
||||
for (const auto& c : expected_text16) {
|
||||
base::StringPiece16 event(&c, 1);
|
||||
EXPECT_CALL(*engine(), HandleInputEvent(IsExpectedImeKeyEvent(event)))
|
||||
.WillOnce(Return(true));
|
||||
}
|
||||
} else {
|
||||
EXPECT_CALL(*engine(), HandleInputEvent).Times(0);
|
||||
}
|
||||
plugin_->ImeCommitTextForPlugin(text, std::vector<ui::ImeTextSpan>(),
|
||||
gfx::Range(),
|
||||
/*relative_cursor_pos=*/0);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(PdfViewWebPluginImeTest, ImeSetCompositionAndFinishAscii) {
|
||||
const blink::WebString text = blink::WebString::FromASCII("input");
|
||||
TestImeSetCompositionForPlugin(text);
|
||||
TestImeFinishComposingTextForPlugin(text);
|
||||
}
|
||||
|
||||
TEST_F(PdfViewWebPluginImeTest, ImeSetCompositionAndFinishUnicode) {
|
||||
const blink::WebString text = blink::WebString::FromUTF16(u"你好");
|
||||
TestImeSetCompositionForPlugin(text);
|
||||
TestImeFinishComposingTextForPlugin(text);
|
||||
// Calling ImeFinishComposingTextForPlugin() again is a no-op.
|
||||
TestImeFinishComposingTextForPlugin("");
|
||||
}
|
||||
|
||||
TEST_F(PdfViewWebPluginImeTest, ImeSetCompositionAndFinishEmpty) {
|
||||
const blink::WebString text;
|
||||
TestImeSetCompositionForPlugin(text);
|
||||
TestImeFinishComposingTextForPlugin(text);
|
||||
}
|
||||
|
||||
TEST_F(PdfViewWebPluginImeTest, ImeCommitTextForPluginAscii) {
|
||||
const blink::WebString text = blink::WebString::FromASCII("a b");
|
||||
TestImeCommitTextForPlugin(text);
|
||||
}
|
||||
|
||||
TEST_F(PdfViewWebPluginImeTest, ImeCommitTextForPluginUnicode) {
|
||||
const blink::WebString text = blink::WebString::FromUTF16(u"さようなら");
|
||||
TestImeCommitTextForPlugin(text);
|
||||
}
|
||||
|
||||
TEST_F(PdfViewWebPluginImeTest, ImeCommitTextForPluginEmpty) {
|
||||
const blink::WebString text;
|
||||
TestImeCommitTextForPlugin(text);
|
||||
}
|
||||
|
||||
TEST_F(PdfViewWebPluginTest, ChangeTextSelection) {
|
||||
ASSERT_FALSE(plugin_->HasSelection());
|
||||
ASSERT_TRUE(plugin_->SelectionAsText().IsEmpty());
|
||||
@ -670,4 +789,28 @@ TEST_F(PdfViewWebPluginTest, UpdateFocus) {
|
||||
plugin_->UpdateFocus(/*focused=*/true, blink::mojom::FocusType::kNone);
|
||||
}
|
||||
|
||||
TEST_F(PdfViewWebPluginTest, ShouldDispatchImeEventsToPlugin) {
|
||||
ASSERT_TRUE(plugin_->ShouldDispatchImeEventsToPlugin());
|
||||
}
|
||||
|
||||
TEST_F(PdfViewWebPluginTest, CaretChangeUseZoomForDSFEnabled) {
|
||||
EXPECT_CALL(*client_ptr_, IsUseZoomForDSFEnabled)
|
||||
.WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(*engine_ptr_, ZoomUpdated(2.0f));
|
||||
UpdatePluginGeometry(
|
||||
/*device_scale=*/2.0f, /*window_rect=*/gfx::Rect(12, 24, 36, 48));
|
||||
plugin_->CaretChanged(gfx::Rect(10, 20, 30, 40));
|
||||
EXPECT_EQ(gfx::Rect(28, 20, 30, 40), plugin_->GetPluginCaretBounds());
|
||||
}
|
||||
|
||||
TEST_F(PdfViewWebPluginTest, CaretChangeUseZoomForDSFDisabled) {
|
||||
EXPECT_CALL(*client_ptr_, IsUseZoomForDSFEnabled)
|
||||
.WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(*engine_ptr_, ZoomUpdated(2.0f));
|
||||
UpdatePluginGeometry(
|
||||
/*device_scale=*/2.0f, /*window_rect=*/gfx::Rect(12, 24, 36, 48));
|
||||
plugin_->CaretChanged(gfx::Rect(10, 20, 30, 40));
|
||||
EXPECT_EQ(gfx::Rect(23, 10, 15, 20), plugin_->GetPluginCaretBounds());
|
||||
}
|
||||
|
||||
} // namespace chrome_pdf
|
||||
|
Reference in New Issue
Block a user