diff options
author | nona@chromium.org <nona@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-13 07:01:35 +0000 |
---|---|---|
committer | nona@chromium.org <nona@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-13 07:01:35 +0000 |
commit | 58b48a0df2a0c66c3833811aa3f381c99eb34cba (patch) | |
tree | 986a02167a5bcf493372bb5176d7f114b2c2ae1b /content/renderer | |
parent | b526ba45f02fddffa1aec3e8f1c81f1e9fe97176 (diff) | |
download | chromium_src-58b48a0df2a0c66c3833811aa3f381c99eb34cba.zip chromium_src-58b48a0df2a0c66c3833811aa3f381c99eb34cba.tar.gz chromium_src-58b48a0df2a0c66c3833811aa3f381c99eb34cba.tar.bz2 |
Add IPC argument to handle composition character bounds into ViewHostMsg_ImeCompositionRangeChanged.
What for:
Using this argument, browser can acquire the composition character boundary rectangles.
This information is used by Japanese input method editor to show its window at the correct position.
This is used not only for fixing http://crbug.com/120597 but also for supporting IMR_QUERYCHARPOSITION message in Windows Chrome.
To support IMR_QUERYCHARPOSITION(Ref 1) message, input method editor can also show its window at the correct position(Ref 2).
Performance:
There is no performance concern for latin language users because this IPC is only emitted if the user uses an input method editor.
Even if the user uses an input method editor, the cost of this IPC is small because this IPC will be emitted at the same frequency as key typing.
Data size:
The data size to be transferred is small: it is the composition string length (typically less than a few dozen characters) times sizeof(gfx::Rect).
Ref 1. http://msdn.microsoft.com/en-us/library/windows/desktop/dd318634(v=vs.85).aspx
Ref 2. https://bug-87911-attachments.webkit.org/attachment.cgi?id=144954
(In Ref2, Safari works well for all IMEs. Firefox works well except ATOK. Chrome doesn't work for all IMEs.)
BUG=120597
TEST=try bots.
Review URL: https://chromiumcodereview.appspot.com/10543024
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@141866 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/renderer')
-rw-r--r-- | content/renderer/render_view_browsertest.cc | 64 | ||||
-rw-r--r-- | content/renderer/render_view_impl.cc | 35 | ||||
-rw-r--r-- | content/renderer/render_view_impl.h | 4 | ||||
-rw-r--r-- | content/renderer/render_widget.cc | 57 | ||||
-rw-r--r-- | content/renderer/render_widget.h | 30 |
5 files changed, 177 insertions, 13 deletions
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc index 45dcff0..610f280 100644 --- a/content/renderer/render_view_browsertest.cc +++ b/content/renderer/render_view_browsertest.cc @@ -1514,3 +1514,67 @@ TEST_F(RenderViewImplTest, TestBackForward) { EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b)); EXPECT_EQ(1, was_page_b); } + +TEST_F(RenderViewImplTest, GetCompositionCharacterBoundsTest) { + LoadHTML("<textarea id=\"test\"></textarea>"); + ExecuteJavaScript("document.getElementById('test').focus();"); + + const string16 empty_string = UTF8ToUTF16(""); + const std::vector<WebKit::WebCompositionUnderline> empty_underline; + std::vector<gfx::Rect> bounds; + view()->OnSetFocus(true); + view()->OnSetInputMethodActive(true); + + // ASCII composition + const string16 ascii_composition = UTF8ToUTF16("aiueo"); + view()->OnImeSetComposition(ascii_composition, empty_underline, 0, 0); + view()->GetCompositionCharacterBounds(&bounds); + ASSERT_EQ(ascii_composition.size(), bounds.size()); + for (size_t i = 0; i < bounds.size(); ++i) + EXPECT_LT(0, bounds[i].width()); + view()->OnImeConfirmComposition(empty_string, ui::Range::InvalidRange()); + + // Non surrogate pair unicode character. + const string16 unicode_composition = UTF8ToUTF16( + "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A"); + view()->OnImeSetComposition(unicode_composition, empty_underline, 0, 0); + view()->GetCompositionCharacterBounds(&bounds); + ASSERT_EQ(unicode_composition.size(), bounds.size()); + for (size_t i = 0; i < bounds.size(); ++i) + EXPECT_LT(0, bounds[i].width()); + view()->OnImeConfirmComposition(empty_string, ui::Range::InvalidRange()); + + // Surrogate pair character. + const string16 surrogate_pair_char = UTF8ToUTF16("\xF0\xA0\xAE\x9F"); + view()->OnImeSetComposition(surrogate_pair_char, + empty_underline, + 0, + 0); + view()->GetCompositionCharacterBounds(&bounds); + ASSERT_EQ(surrogate_pair_char.size(), bounds.size()); + EXPECT_LT(0, bounds[0].width()); + EXPECT_EQ(0, bounds[1].width()); + view()->OnImeConfirmComposition(empty_string, ui::Range::InvalidRange()); + + // Mixed string. + const string16 surrogate_pair_mixed_composition = + surrogate_pair_char + UTF8ToUTF16("\xE3\x81\x82") + surrogate_pair_char + + UTF8ToUTF16("b") + surrogate_pair_char; + const size_t utf16_length = 8UL; + const bool is_surrogate_pair_empty_rect[8] = { + false, true, false, false, true, false, false, true }; + view()->OnImeSetComposition(surrogate_pair_mixed_composition, + empty_underline, + 0, + 0); + view()->GetCompositionCharacterBounds(&bounds); + ASSERT_EQ(utf16_length, bounds.size()); + for (size_t i = 0; i < utf16_length; ++i) { + if (is_surrogate_pair_empty_rect[i]) { + EXPECT_EQ(0, bounds[i].width()); + } else { + EXPECT_LT(0, bounds[i].width()); + } + } + view()->OnImeConfirmComposition(empty_string, ui::Range::InvalidRange()); +} diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc index 76e1772..2b6e5b1 100644 --- a/content/renderer/render_view_impl.cc +++ b/content/renderer/render_view_impl.cc @@ -5216,8 +5216,9 @@ void RenderViewImpl::SimulateImeConfirmComposition( void RenderViewImpl::PpapiPluginCancelComposition() { Send(new ViewHostMsg_ImeCancelComposition(routing_id())); - ui::Range range(ui::Range::InvalidRange()); - Send(new ViewHostMsg_ImeCompositionRangeChanged(routing_id(), range)); + const ui::Range range(ui::Range::InvalidRange()); + const std::vector<gfx::Rect> empty_bounds; + UpdateCompositionInfo(range, empty_bounds); } void RenderViewImpl::PpapiPluginSelectionChanged() { @@ -5322,6 +5323,36 @@ void RenderViewImpl::GetSelectionBounds(gfx::Rect* start, gfx::Rect* end) { RenderWidget::GetSelectionBounds(start, end); } +void RenderViewImpl::GetCompositionCharacterBounds( + std::vector<gfx::Rect>* bounds) { + DCHECK(bounds); + bounds->clear(); + + if (!webview()) + return; + size_t start_offset = 0; + size_t character_count = 0; + if (!webview()->compositionRange(&start_offset, &character_count)) + return; + if (character_count == 0) + return; + + WebKit::WebFrame* frame = webview()->focusedFrame(); + if (!frame) + return; + + bounds->reserve(character_count); + WebKit::WebRect webrect; + for (size_t i = 0; i < character_count; ++i) { + if (!frame->firstRectForCharacterRange(start_offset + i, 1, webrect)) { + DLOG(ERROR) << "Could not retrieve character rectangle at " << i; + bounds->clear(); + return; + } + bounds->push_back(webrect); + } +} + bool RenderViewImpl::CanComposeInline() { return pepper_delegate_.IsPluginFocused() ? pepper_delegate_.CanComposeInline() : true; diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h index 17b2149..d2ed3d5 100644 --- a/content/renderer/render_view_impl.h +++ b/content/renderer/render_view_impl.h @@ -748,6 +748,8 @@ class RenderViewImpl : public RenderWidget, const string16& text, const ui::Range& replacement_range) OVERRIDE; virtual ui::TextInputType GetTextInputType() OVERRIDE; virtual void GetSelectionBounds(gfx::Rect* start, gfx::Rect* end) OVERRIDE; + virtual void GetCompositionCharacterBounds( + std::vector<gfx::Rect>* character_bounds) OVERRIDE; virtual bool CanComposeInline() OVERRIDE; virtual bool WebWidgetHandlesCompositorScheduling() const OVERRIDE; @@ -776,6 +778,8 @@ class RenderViewImpl : public RenderWidget, FRIEND_TEST_ALL_PREFIXES(RenderViewImplTest, SendSwapOutACK); FRIEND_TEST_ALL_PREFIXES(RenderViewImplTest, StaleNavigationsIgnored); FRIEND_TEST_ALL_PREFIXES(RenderViewImplTest, UpdateTargetURLWithInvalidURL); + FRIEND_TEST_ALL_PREFIXES(RenderViewImplTest, + GetCompositionCharacterBoundsTest); #if defined(OS_MACOSX) FRIEND_TEST_ALL_PREFIXES(RenderViewTest, MacTestCmdUp); #endif diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc index 8514eed..f17b665 100644 --- a/content/renderer/render_widget.cc +++ b/content/renderer/render_widget.cc @@ -1323,6 +1323,17 @@ void RenderWidget::OnSetInputMethodActive(bool is_active) { input_method_is_active_ = is_active; } +void RenderWidget::UpdateCompositionInfo( + const ui::Range& range, + const std::vector<gfx::Rect>& character_bounds) { + if (!ShouldUpdateCompositionInfo(range, character_bounds)) + return; + composition_character_bounds_ = character_bounds; + composition_range_ = range; + Send(new ViewHostMsg_ImeCompositionRangeChanged( + routing_id(), composition_range_, composition_character_bounds_)); +} + void RenderWidget::OnImeSetComposition( const string16& text, const std::vector<WebCompositionUnderline>& underlines, @@ -1345,7 +1356,9 @@ void RenderWidget::OnImeSetComposition( range.set_start(location); range.set_end(location + length); } - Send(new ViewHostMsg_ImeCompositionRangeChanged(routing_id(), range)); + std::vector<gfx::Rect> character_bounds; + GetCompositionCharacterBounds(&character_bounds); + UpdateCompositionInfo(range, character_bounds); } else { // If we failed to set the composition text, then we need to let the browser // process to cancel the input method's ongoing composition session, to make @@ -1359,7 +1372,7 @@ void RenderWidget::OnImeSetComposition( range.set_start(location); range.set_end(location + length); } - Send(new ViewHostMsg_ImeCompositionRangeChanged(routing_id(), range)); + UpdateCompositionInfo(range, std::vector<gfx::Rect>()); } } @@ -1379,7 +1392,7 @@ void RenderWidget::OnImeConfirmComposition( range.set_start(location); range.set_end(location + length); } - Send(new ViewHostMsg_ImeCompositionRangeChanged(routing_id(), range)); + UpdateCompositionInfo(range, std::vector<gfx::Rect>()); } // This message causes the renderer to render an image of the @@ -1586,13 +1599,30 @@ void RenderWidget::UpdateSelectionBounds() { gfx::Rect start_rect; gfx::Rect end_rect; GetSelectionBounds(&start_rect, &end_rect); - if (selection_start_rect_ == start_rect && selection_end_rect_ == end_rect) - return; + if (selection_start_rect_ != start_rect || selection_end_rect_ != end_rect) { + selection_start_rect_ = start_rect; + selection_end_rect_ = end_rect; + Send(new ViewHostMsg_SelectionBoundsChanged( + routing_id_, selection_start_rect_, selection_end_rect_)); + } - selection_start_rect_ = start_rect; - selection_end_rect_ = end_rect; - Send(new ViewHostMsg_SelectionBoundsChanged( - routing_id_, selection_start_rect_, selection_end_rect_)); + std::vector<gfx::Rect> character_bounds; + GetCompositionCharacterBounds(&character_bounds); + UpdateCompositionInfo(composition_range_, character_bounds); +} + +bool RenderWidget::ShouldUpdateCompositionInfo( + const ui::Range& range, + const std::vector<gfx::Rect>& bounds) { + if (composition_range_ != range) + return true; + if (bounds.size() != composition_character_bounds_.size()) + return true; + for (size_t i = 0; i < bounds.size(); ++i) { + if (bounds[i] != composition_character_bounds_[i]) + return true; + } + return false; } // Check WebKit::WebTextInputType and ui::TextInputType is kept in sync. @@ -1636,6 +1666,12 @@ ui::TextInputType RenderWidget::GetTextInputType() { return ui::TEXT_INPUT_TYPE_NONE; } +void RenderWidget::GetCompositionCharacterBounds( + std::vector<gfx::Rect>* bounds) { + DCHECK(bounds); + bounds->clear(); +} + bool RenderWidget::CanComposeInline() { return true; } @@ -1668,7 +1704,8 @@ void RenderWidget::resetInputMethod() { range.set_start(location); range.set_end(location + length); } - Send(new ViewHostMsg_ImeCompositionRangeChanged(routing_id(), range)); + + UpdateCompositionInfo(range, std::vector<gfx::Rect>()); } void RenderWidget::SchedulePluginMove( diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h index 194d40c..2fcede8 100644 --- a/content/renderer/render_widget.h +++ b/content/renderer/render_widget.h @@ -25,6 +25,7 @@ #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebRect.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/ime/text_input_type.h" +#include "ui/base/range/range.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/rect.h" #include "ui/gfx/size.h" @@ -320,13 +321,34 @@ class CONTENT_EXPORT RenderWidget // Checks if the selection bounds have been changed. If they are changed, // the new value will be sent to the browser process. - void UpdateSelectionBounds(); + virtual void UpdateSelectionBounds(); + + // Checks if the composition range or composition character bounds have been + // changed. If they are changed, the new value will be sent to the browser + // process. + virtual void UpdateCompositionInfo( + const ui::Range& range, + const std::vector<gfx::Rect>& character_bounds); + // Override point to obtain that the current input method state and caret // position. virtual ui::TextInputType GetTextInputType(); virtual void GetSelectionBounds(gfx::Rect* start, gfx::Rect* end); + // Override point to obtain that the current composition character bounds. + // In the case of surrogate pairs, the character is treated as two characters: + // the bounds for first character is actual one, and the bounds for second + // character is zero width rectangle. + virtual void GetCompositionCharacterBounds( + std::vector<gfx::Rect>* character_bounds); + + // Returns true if the composition range or composition character bounds + // should be sent to the browser process. + bool ShouldUpdateCompositionInfo( + const ui::Range& range, + const std::vector<gfx::Rect>& bounds); + // Override point to obtain that the current input method state about // composition text. virtual bool CanComposeInline(); @@ -478,6 +500,12 @@ class CONTENT_EXPORT RenderWidget gfx::Rect selection_start_rect_; gfx::Rect selection_end_rect_; + // Stores the current composition character bounds. + std::vector<gfx::Rect> composition_character_bounds_; + + // Stores the current composition range. + ui::Range composition_range_; + // The kind of popup this widget represents, NONE if not a popup. WebKit::WebPopupType popup_type_; |