summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsuzhe@chromium.org <suzhe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-23 17:53:04 +0000
committersuzhe@chromium.org <suzhe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-23 17:53:04 +0000
commitfa7b1dc866e2a5af457602e015ebc84ab05c75b9 (patch)
treee0226343303b9925c10deba260ed00e7f91aa8dd
parent31825d4399cf8c44f86f828f5662c885c85e8b49 (diff)
downloadchromium_src-fa7b1dc866e2a5af457602e015ebc84ab05c75b9.zip
chromium_src-fa7b1dc866e2a5af457602e015ebc84ab05c75b9.tar.gz
chromium_src-fa7b1dc866e2a5af457602e015ebc84ab05c75b9.tar.bz2
Improve input method support.
This CL fixes following issues: BUG=23219 IME should be disabled in password box. BUG=41876 Chinese IME is still active when current focus is not a text input control BUG=44529 Clause segmentation information of composition text is not honored when using CJK input methods. BUG=46326 Clicking during a composition cancels it TEST=See individual bug report. This CL is blocked on webkit bug: https://bugs.webkit.org/show_bug.cgi?id=40608 Review URL: http://codereview.chromium.org/2824015 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@50622 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/ime_input.cc244
-rw-r--r--chrome/browser/ime_input.h79
-rw-r--r--chrome/browser/renderer_host/gtk_im_context_wrapper.cc266
-rw-r--r--chrome/browser/renderer_host/gtk_im_context_wrapper.h52
-rw-r--r--chrome/browser/renderer_host/gtk_im_context_wrapper_unittest.cc154
-rw-r--r--chrome/browser/renderer_host/render_widget_host.cc54
-rw-r--r--chrome/browser/renderer_host/render_widget_host.h45
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view.h9
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_gtk.cc15
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_gtk.h4
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_mac.h26
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_mac.mm288
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_win.cc72
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_win.h8
-rw-r--r--chrome/browser/renderer_host/test/test_render_view_host.h4
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/common/render_messages.h42
-rw-r--r--chrome/common/render_messages_internal.h90
-rw-r--r--chrome/common/webkit_param_traits.h86
-rw-r--r--chrome/renderer/render_view.cc10
-rw-r--r--chrome/renderer/render_view.h1
-rw-r--r--chrome/renderer/render_view_unittest.cc140
-rw-r--r--chrome/renderer/render_widget.cc158
-rw-r--r--chrome/renderer/render_widget.h51
24 files changed, 1163 insertions, 736 deletions
diff --git a/chrome/browser/ime_input.cc b/chrome/browser/ime_input.cc
index 7897a66..edb49f8 100644
--- a/chrome/browser/ime_input.cc
+++ b/chrome/browser/ime_input.cc
@@ -4,8 +4,10 @@
#include "chrome/browser/ime_input.h"
+#include "base/basictypes.h"
#include "base/scoped_ptr.h"
#include "base/string_util.h"
+#include "third_party/skia/include/core/SkColor.h"
// "imm32.lib" is required by IMM32 APIs used in this file.
// NOTE(hbono): To comply with a comment from Darin, I have added
@@ -15,6 +17,84 @@
///////////////////////////////////////////////////////////////////////////////
// ImeInput
+namespace {
+
+// Determines whether or not the given attribute represents a target
+// (a.k.a. a selection).
+bool IsTargetAttribute(char attribute) {
+ return (attribute == ATTR_TARGET_CONVERTED ||
+ attribute == ATTR_TARGET_NOTCONVERTED);
+}
+
+// Helper function for ImeInput::GetCompositionInfo() method, to get the target
+// range that's selected by the user in the current composition string.
+void GetCompositionTargetRange(HIMC imm_context, int* target_start,
+ int* target_end) {
+ int attribute_size = ::ImmGetCompositionString(imm_context, GCS_COMPATTR,
+ NULL, 0);
+ if (attribute_size > 0) {
+ int start = 0;
+ int end = 0;
+ scoped_array<char> attribute_data(new char[attribute_size]);
+ if (attribute_data.get()) {
+ ::ImmGetCompositionString(imm_context, GCS_COMPATTR,
+ attribute_data.get(), attribute_size);
+ for (start = 0; start < attribute_size; ++start) {
+ if (IsTargetAttribute(attribute_data[start]))
+ break;
+ }
+ for (end = start; end < attribute_size; ++end) {
+ if (!IsTargetAttribute(attribute_data[end]))
+ break;
+ }
+ if (start == attribute_size) {
+ // This composition clause does not contain any target clauses,
+ // i.e. this clauses is an input clause.
+ // We treat the whole composition as a target clause.
+ start = 0;
+ end = attribute_size;
+ }
+ }
+ *target_start = start;
+ *target_end = end;
+ }
+}
+
+// Helper function for ImeInput::GetCompositionInfo() method, to get underlines
+// information of the current composition string.
+void GetCompositionUnderlines(
+ HIMC imm_context,
+ int target_start,
+ int target_end,
+ std::vector<WebKit::WebCompositionUnderline>* underlines) {
+ int clause_size = ::ImmGetCompositionString(imm_context, GCS_COMPCLAUSE,
+ NULL, 0);
+ int clause_length = clause_size / sizeof(uint32);
+ if (clause_length) {
+ scoped_array<uint32> clause_data(new uint32[clause_length]);
+ if (clause_data.get()) {
+ ::ImmGetCompositionString(imm_context, GCS_COMPCLAUSE,
+ clause_data.get(), clause_size);
+ for (int i = 0; i < clause_length - 1; ++i) {
+ WebKit::WebCompositionUnderline underline;
+ underline.startOffset = clause_data[i];
+ underline.endOffset = clause_data[i+1];
+ underline.color = SK_ColorBLACK;
+ underline.thick = false;
+
+ // Use thick underline for the target clause.
+ if (underline.startOffset >= static_cast<unsigned>(target_start) &&
+ underline.endOffset <= static_cast<unsigned>(target_end)) {
+ underline.thick = true;
+ }
+ underlines->push_back(underline);
+ }
+ }
+ }
+}
+
+} // namespace
+
ImeInput::ImeInput()
: ime_status_(false),
input_language_id_(LANG_USER_DEFAULT),
@@ -168,65 +248,64 @@ void ImeInput::CompleteComposition(HWND window_handle, HIMC imm_context) {
}
}
-void ImeInput::GetCaret(HIMC imm_context, LPARAM lparam,
- ImeComposition* composition) {
- // This operation is optional and language-dependent because the caret
- // style is depended on the language, e.g.:
- // * Korean IMEs: the caret is a blinking block,
- // (It contains only one hangul character);
- // * Chinese IMEs: the caret is a blinking line,
- // (i.e. they do not need to retrieve the target selection);
- // * Japanese IMEs: the caret is a selection (or underlined) block,
- // (which can contain one or more Japanese characters).
- int target_start = -1;
- int target_end = -1;
- switch (PRIMARYLANGID(input_language_id_)) {
- case LANG_KOREAN:
- if (lparam & CS_NOMOVECARET) {
- target_start = 0;
- target_end = 1;
+void ImeInput::GetCompositionInfo(HIMC imm_context, LPARAM lparam,
+ ImeComposition* composition) {
+ // We only care about GCS_COMPATTR, GCS_COMPCLAUSE and GCS_CURSORPOS, and
+ // convert them into underlines and selection range respectively.
+ composition->underlines.clear();
+
+ int length = static_cast<int>(composition->ime_string.length());
+
+ // Retrieve the selection range information. If CS_NOMOVECARET is specified,
+ // that means the cursor should not be moved, then we just place the caret at
+ // the beginning of the composition string. Otherwise we should honour the
+ // GCS_CURSORPOS value if it's available.
+ // TODO(suzhe): due to a bug of webkit, we currently can't use selection range
+ // with composition string. See: https://bugs.webkit.org/show_bug.cgi?id=40805
+ if (lparam & CS_NOMOVECARET) {
+ composition->selection_start = composition->selection_end = 0;
+ } else if (lparam & GCS_CURSORPOS) {
+ composition->selection_start = composition->selection_end =
+ ::ImmGetCompositionString(imm_context, GCS_CURSORPOS, NULL, 0);
+ } else {
+ composition->selection_start = composition->selection_end = length;
+ }
+
+ // Find out the range selected by the user.
+ int target_start = 0;
+ int target_end = 0;
+ if (lparam & GCS_COMPATTR)
+ GetCompositionTargetRange(imm_context, &target_start, &target_end);
+
+ // Retrieve the clause segmentations and convert them to underlines.
+ if (lparam & GCS_COMPCLAUSE) {
+ GetCompositionUnderlines(imm_context, target_start, target_end,
+ &composition->underlines);
+ }
+
+ // Set default underlines in case there is no clause information.
+ if (!composition->underlines.size()) {
+ WebKit::WebCompositionUnderline underline;
+ underline.color = SK_ColorBLACK;
+ if (target_start > 0) {
+ underline.startOffset = 0;
+ underline.endOffset = target_start;
+ underline.thick = false;
+ composition->underlines.push_back(underline);
}
- break;
- case LANG_CHINESE:
- break;
- case LANG_JAPANESE:
- // For Japanese IMEs, the robustest way to retrieve the caret
- // is scanning the attribute of the latest composition string and
- // retrieving the begining and the end of the target clause, i.e.
- // a clause being converted.
- if (lparam & GCS_COMPATTR) {
- int attribute_size = ::ImmGetCompositionString(imm_context,
- GCS_COMPATTR,
- NULL, 0);
- if (attribute_size > 0) {
- scoped_array<char> attribute_data(new char[attribute_size]);
- if (attribute_data.get()) {
- ::ImmGetCompositionString(imm_context, GCS_COMPATTR,
- attribute_data.get(), attribute_size);
- for (target_start = 0; target_start < attribute_size;
- ++target_start) {
- if (IsTargetAttribute(attribute_data[target_start]))
- break;
- }
- for (target_end = target_start; target_end < attribute_size;
- ++target_end) {
- if (!IsTargetAttribute(attribute_data[target_end]))
- break;
- }
- if (target_start == attribute_size) {
- // This composition clause does not contain any target clauses,
- // i.e. this clauses is an input clause.
- // We treat whole this clause as a target clause.
- target_end = target_start;
- target_start = 0;
- }
- }
- }
+ if (target_end > target_start) {
+ underline.startOffset = target_start;
+ underline.endOffset = target_end;
+ underline.thick = true;
+ composition->underlines.push_back(underline);
+ }
+ if (target_end < length) {
+ underline.startOffset = target_end;
+ underline.endOffset = length;
+ underline.thick = false;
+ composition->underlines.push_back(underline);
}
- break;
}
- composition->target_start = target_start;
- composition->target_end = target_end;
}
bool ImeInput::GetString(HIMC imm_context, WPARAM lparam, int type,
@@ -259,9 +338,8 @@ bool ImeInput::GetResult(HWND window_handle, LPARAM lparam,
result = GetString(imm_context, lparam, GCS_RESULTSTR, composition);
// Reset all the other parameters because a result string does not
// have composition attributes.
- composition->cursor_position = -1;
- composition->target_start = -1;
- composition->target_end = -1;
+ composition->selection_start = 0;
+ composition->selection_end = 0;
::ImmReleaseContext(window_handle, imm_context);
}
return result;
@@ -288,16 +366,8 @@ bool ImeInput::GetComposition(HWND window_handle, LPARAM lparam,
composition->ime_string[0] = 0xFF3F;
}
- // Retrieve the cursor position in the IME composition.
- int cursor_position = ::ImmGetCompositionString(imm_context,
- GCS_CURSORPOS, NULL, 0);
- composition->cursor_position = cursor_position;
- composition->target_start = -1;
- composition->target_end = -1;
-
- // Retrieve the target selection and Update the ImeComposition
- // object.
- GetCaret(imm_context, lparam, composition);
+ // Retrieve the composition underlines and selection range information.
+ GetCompositionInfo(imm_context, lparam, composition);
// Mark that there is an ongoing composition.
is_composing_ = true;
@@ -328,34 +398,26 @@ void ImeInput::CancelIME(HWND window_handle) {
}
}
-void ImeInput::EnableIME(HWND window_handle,
- const gfx::Rect& caret_rect,
- bool complete) {
+void ImeInput::EnableIME(HWND window_handle) {
// Load the default IME context.
// NOTE(hbono)
// IMM ignores this call if the IME context is loaded. Therefore, we do
// not have to check whether or not the IME context is loaded.
::ImmAssociateContextEx(window_handle, NULL, IACE_DEFAULT);
- // Complete the ongoing composition and move the IME windows.
- HIMC imm_context = ::ImmGetContext(window_handle);
- if (imm_context) {
- if (complete) {
- // A renderer process have moved its input focus to another edit
- // control when there is an ongoing composition, e.g. a user has
- // clicked a mouse button and selected another edit control while
- // composing a text.
- // For this case, we have to complete the ongoing composition and
- // hide the IME windows BEFORE MOVING THEM.
- CompleteComposition(window_handle, imm_context);
- }
- // Save the caret position, and Update the position of the IME window.
- // This update is used for moving an IME window when a renderer process
- // resize/moves the input caret.
- if (caret_rect.x() >= 0 && caret_rect.y() >= 0) {
- caret_rect_.SetRect(caret_rect.x(), caret_rect.y(), caret_rect.width(),
- caret_rect.height());
+}
+
+void ImeInput::UpdateCaretRect(HWND window_handle,
+ const gfx::Rect& caret_rect) {
+ // Save the caret position, and Update the position of the IME window.
+ // This update is used for moving an IME window when a renderer process
+ // resize/moves the input caret.
+ if (caret_rect_ != caret_rect) {
+ caret_rect_ = caret_rect;
+ // Move the IME windows.
+ HIMC imm_context = ::ImmGetContext(window_handle);
+ if (imm_context) {
MoveImeWindow(window_handle, imm_context);
+ ::ImmReleaseContext(window_handle, imm_context);
}
- ::ImmReleaseContext(window_handle, imm_context);
}
}
diff --git a/chrome/browser/ime_input.h b/chrome/browser/ime_input.h
index 45fe21f..184343b 100644
--- a/chrome/browser/ime_input.h
+++ b/chrome/browser/ime_input.h
@@ -8,9 +8,11 @@
#include <windows.h>
#include <string>
+#include <vector>
#include "base/basictypes.h"
#include "gfx/rect.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCompositionUnderline.h"
// This header file defines a struct and a class used for encapsulating IMM32
// APIs, controls IMEs attached to a window, and enables the 'on-the-spot'
@@ -53,14 +55,14 @@
// This struct represents the status of an ongoing composition.
struct ImeComposition {
- // Represents the cursor position in the IME composition.
- int cursor_position;
+ // Represents the start position of the selection range in the IME
+ // composition.
+ int selection_start;
- // Represents the position of the beginning of the selection
- int target_start;
-
- // Represents the position of the end of the selection
- int target_end;
+ // Represents the end position of the selection range in the IME composition.
+ // If |selection_start| and |selection_end| are equal, then it represents the
+ // cursor position.
+ int selection_end;
// Represents the type of the string in the 'ime_string' parameter.
// Its possible values and description are listed bwlow:
@@ -72,6 +74,9 @@ struct ImeComposition {
// Represents the string retrieved from IME (Input Method Editor)
std::wstring ime_string;
+
+ // Contains the underline information of the composition string.
+ std::vector<WebKit::WebCompositionUnderline> underlines;
};
// This class controls the IMM (Input Method Manager) through IMM32 APIs and
@@ -104,13 +109,13 @@ class ImeInput {
// The given input language does not have IMEs.
bool SetInputLanguage();
- // Create the IME windows, and allocate required resources for them.
+ // Creates the IME windows, and allocate required resources for them.
// Parameters
// * window_handle [in] (HWND)
// Represents the window handle of the caller.
void CreateImeWindow(HWND window_handle);
- // Update the style of the IME windows.
+ // Updates the style of the IME windows.
// Parameters
// * window_handle [in] (HWND)
// Represents the window handle of the caller.
@@ -131,26 +136,26 @@ class ImeInput {
void SetImeWindowStyle(HWND window_handle, UINT message,
WPARAM wparam, LPARAM lparam, BOOL* handled);
- // Destroy the IME windows and all the resources attached to them.
+ // Destroys the IME windows and all the resources attached to them.
// Parameters
// * window_handle [in] (HWND)
// Represents the window handle of the caller.
void DestroyImeWindow(HWND window_handle);
- // Update the position of the IME windows.
+ // Updates the position of the IME windows.
// Parameters
// * window_handle [in] (HWND)
// Represents the window handle of the caller.
void UpdateImeWindow(HWND window_handle);
- // Clean up the all resources attached to the given ImeInput object, and
+ // Cleans up the all resources attached to the given ImeInput object, and
// reset its composition status.
// Parameters
// * window_handle [in] (HWND)
// Represents the window handle of the caller.
void CleanupComposition(HWND window_handle);
- // Reset the composition status.
+ // Resets the composition status.
// Cancel the ongoing composition if it exists.
// NOTE(hbono): This method does not release the allocated resources.
// Parameters
@@ -158,7 +163,7 @@ class ImeInput {
// Represents the window handle of the caller.
void ResetComposition(HWND window_handle);
- // Retrieve a composition result of the ongoing composition if it exists.
+ // Retrieves a composition result of the ongoing composition if it exists.
// Parameters
// * window_handle [in] (HWND)
// Represents the window handle of the caller.
@@ -180,7 +185,7 @@ class ImeInput {
bool GetResult(HWND window_handle, LPARAM lparam,
ImeComposition* composition);
- // Retrieve the current composition status of the ongoing composition.
+ // Retrieves the current composition status of the ongoing composition.
// Parameters
// * window_handle [in] (HWND)
// Represents the window handle of the caller.
@@ -202,17 +207,11 @@ class ImeInput {
bool GetComposition(HWND window_handle, LPARAM lparam,
ImeComposition* composition);
- // Enable the IME attached to the given window, i.e. allows user-input
+ // Enables the IME attached to the given window, i.e. allows user-input
// events to be dispatched to the IME.
- // In Chrome, this function is used when:
- // * a renderer process moves its input focus to another edit control, or;
- // * a renrerer process moves the position of the focused edit control.
// Parameters
// * window_handle [in] (HWND)
// Represents the window handle of the caller.
- // * caret_rect [in] (const gfx::Rect&)
- // Represent the rectangle of the input caret.
- // This rectangle is used for controlling the positions of IME windows.
// * complete [in] (bool)
// Represents whether or not to complete the ongoing composition.
// + true
@@ -222,11 +221,9 @@ class ImeInput {
// + false
// Just move the IME windows of the ongoing composition to the given
// position without finishing it.
- void EnableIME(HWND window_handle,
- const gfx::Rect& caret_rect,
- bool complete);
+ void EnableIME(HWND window_handle);
- // Disable the IME attached to the given window, i.e. prohibits any user-input
+ // Disables the IME attached to the given window, i.e. prohibits any user-input
// events from being dispatched to the IME.
// In Chrome, this function is used when:
// * a renreder process sets its input focus to a password input.
@@ -241,25 +238,27 @@ class ImeInput {
// Represents the window handle of the caller.
void CancelIME(HWND window_handle);
+ // Updates the caret position of the given window.
+ // Parameters
+ // * window_handle [in] (HWND)
+ // Represents the window handle of the caller.
+ // * caret_rect [in] (const gfx::Rect&)
+ // Represent the rectangle of the input caret.
+ // This rectangle is used for controlling the positions of IME windows.
+ void UpdateCaretRect(HWND window_handle, const gfx::Rect& caret_rect);
+
protected:
- // Determines whether or not the given attribute represents a target
- // (a.k.a. a selection).
- bool IsTargetAttribute(char attribute) const {
- return (attribute == ATTR_TARGET_CONVERTED ||
- attribute == ATTR_TARGET_NOTCONVERTED);
- }
-
- // Retrieve the target area.
- void GetCaret(HIMC imm_context, LPARAM lparam,
- ImeComposition* composition);
-
- // Update the position of the IME windows.
+ // Retrieves the composition information.
+ void GetCompositionInfo(HIMC imm_context, LPARAM lparam,
+ ImeComposition* composition);
+
+ // Updates the position of the IME windows.
void MoveImeWindow(HWND window_handle, HIMC imm_context);
- // Complete the ongoing composition if it exists.
+ // Completes the ongoing composition if it exists.
void CompleteComposition(HWND window_handle, HIMC imm_context);
- // Retrieve a string from the IMM.
+ // Retrieves a string from the IMM.
bool GetString(HIMC imm_context, WPARAM lparam, int type,
ImeComposition* composition);
diff --git a/chrome/browser/renderer_host/gtk_im_context_wrapper.cc b/chrome/browser/renderer_host/gtk_im_context_wrapper.cc
index 3badcae..94173e2 100644
--- a/chrome/browser/renderer_host/gtk_im_context_wrapper.cc
+++ b/chrome/browser/renderer_host/gtk_im_context_wrapper.cc
@@ -7,10 +7,12 @@
#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
+#include <algorithm>
#include "app/l10n_util.h"
#include "base/logging.h"
#include "base/string_util.h"
+#include "base/third_party/icu/icu_utf.h"
#include "chrome/app/chrome_dll_resource.h"
#include "chrome/browser/gtk/gtk_util.h"
#include "chrome/browser/gtk/menu_gtk.h"
@@ -20,6 +22,7 @@
#include "chrome/common/render_messages.h"
#include "gfx/rect.h"
#include "grit/generated_resources.h"
+#include "third_party/skia/include/core/SkColor.h"
GtkIMContextWrapper::GtkIMContextWrapper(RenderWidgetHostViewGtk* host_view)
: host_view_(host_view),
@@ -29,7 +32,8 @@ GtkIMContextWrapper::GtkIMContextWrapper(RenderWidgetHostViewGtk* host_view)
is_composing_text_(false),
is_enabled_(false),
is_in_key_event_handler_(false),
- preedit_cursor_position_(0),
+ preedit_selection_start_(0),
+ preedit_selection_end_(0),
is_preedit_changed_(false) {
DCHECK(context_);
DCHECK(context_simple_);
@@ -122,6 +126,9 @@ void GtkIMContextWrapper::ProcessKeyEvent(GdkEventKey* event) {
filtered = gtk_im_context_filter_keypress(context_simple_, event);
}
+ // Reset this flag here, as it's only used in input method callbacks.
+ is_in_key_event_handler_ = false;
+
NativeWebKeyboardEvent wke(event);
// Send filtered keydown event before sending IME result.
@@ -152,13 +159,10 @@ void GtkIMContextWrapper::ProcessKeyEvent(GdkEventKey* event) {
ProcessUnfilteredKeyPressEvent(&wke);
else if (event->type == GDK_KEY_RELEASE)
host_view_->ForwardKeyboardEvent(wke);
-
- // End of key event processing.
- is_in_key_event_handler_ = false;
}
-void GtkIMContextWrapper::UpdateStatus(int control,
- const gfx::Rect& caret_rect) {
+void GtkIMContextWrapper::UpdateInputMethodState(WebKit::WebTextInputType type,
+ const gfx::Rect& caret_rect) {
// The renderer has updated its IME status.
// Control the GtkIMContext object according to this status.
if (!context_ || !is_focused_)
@@ -166,37 +170,16 @@ void GtkIMContextWrapper::UpdateStatus(int control,
DCHECK(!is_in_key_event_handler_);
- // TODO(james.su@gmail.com): Following code causes a side effect:
- // When trying to move cursor from one text input box to another while
- // composition text is still not confirmed, following CompleteComposition()
- // calls will prevent the cursor from moving outside the first input box.
- if (control == IME_DISABLE) {
- if (is_enabled_) {
- CompleteComposition();
- gtk_im_context_reset(context_simple_);
- gtk_im_context_focus_out(context_);
- is_enabled_ = false;
- }
- } else if (control == IME_CANCEL_COMPOSITION) {
- preedit_text_.clear();
- preedit_cursor_position_ = 0;
- gtk_im_context_reset(context_simple_);
- gtk_im_context_reset(context_);
- } else {
- // Enable the GtkIMContext object if it's not enabled yet.
- if (!is_enabled_) {
- // Reset context_simple_ to its initial state, in case it's currently
- // in middle of a composition session inside a password box.
- gtk_im_context_reset(context_simple_);
+ bool is_enabled = (type == WebKit::WebTextInputTypeText);
+ if (is_enabled_ != is_enabled) {
+ is_enabled_ = is_enabled;
+ if (is_enabled)
gtk_im_context_focus_in(context_);
- // It might be true when switching from a password box in middle of a
- // composition session.
- is_composing_text_ = false;
- is_enabled_ = true;
- } else if (control == IME_COMPLETE_COMPOSITION) {
- CompleteComposition();
- }
+ else
+ gtk_im_context_focus_out(context_);
+ }
+ if (is_enabled) {
// Updates the position of the IME candidate window.
// The position sent from the renderer is a relative one, so we need to
// attach the GtkIMContext object to this window before changing the
@@ -225,7 +208,7 @@ void GtkIMContextWrapper::OnFocusIn() {
// Enables RenderWidget's IME related events, so that we can be notified
// when WebKit wants to enable or disable IME.
- host_view_->GetRenderWidgetHost()->ImeSetInputMode(true);
+ host_view_->GetRenderWidgetHost()->SetInputMethodActive(true);
}
void GtkIMContextWrapper::OnFocusOut() {
@@ -240,7 +223,7 @@ void GtkIMContextWrapper::OnFocusOut() {
// enabled by WebKit.
if (is_enabled_) {
// To reset the GtkIMContext object and prevent data loss.
- CompleteComposition();
+ ConfirmComposition();
gtk_im_context_focus_out(context_);
}
@@ -248,13 +231,10 @@ void GtkIMContextWrapper::OnFocusOut() {
gtk_im_context_reset(context_simple_);
gtk_im_context_focus_out(context_simple_);
- // Reset stored IME status.
is_composing_text_ = false;
- preedit_text_.clear();
- preedit_cursor_position_ = 0;
// Disable RenderWidget's IME related events to save bandwidth.
- host_view_->GetRenderWidgetHost()->ImeSetInputMode(false);
+ host_view_->GetRenderWidgetHost()->SetInputMethodActive(false);
}
void GtkIMContextWrapper::AppendInputMethodsContextMenu(MenuGtk* menu) {
@@ -269,6 +249,31 @@ void GtkIMContextWrapper::AppendInputMethodsContextMenu(MenuGtk* menu) {
menu->AppendMenuItem(IDC_INPUT_METHODS_MENU, menuitem);
}
+void GtkIMContextWrapper::CancelComposition() {
+ if (!is_enabled_)
+ return;
+
+ DCHECK(!is_in_key_event_handler_);
+
+ // To prevent any text from being committed when resetting the |context_|;
+ is_in_key_event_handler_ = true;
+
+ gtk_im_context_reset(context_);
+ gtk_im_context_reset(context_simple_);
+
+ // Some input methods may not honour the reset call. Focusing out/in the
+ // |context_| to make sure it gets reset correctly.
+ gtk_im_context_focus_out(context_);
+ gtk_im_context_focus_in(context_);
+
+ is_composing_text_ = false;
+ preedit_text_.clear();
+ preedit_underlines_.clear();
+ commit_text_.clear();
+
+ is_in_key_event_handler_ = false;
+}
+
bool GtkIMContextWrapper::NeedCommitByForwardingCharEvent() {
// If there is no composition text and has only one character to be
// committed, then the character will be send to webkit as a Char event
@@ -374,43 +379,28 @@ void GtkIMContextWrapper::ProcessInputMethodResult(const GdkEventKey* event,
// preedit text again.
if (is_preedit_changed_) {
if (preedit_text_.length()) {
- host->ImeSetComposition(preedit_text_, preedit_cursor_position_,
- -1, -1);
+ // Another composition session has been started.
+ is_composing_text_ = true;
+ host->ImeSetComposition(preedit_text_, preedit_underlines_,
+ preedit_selection_start_, preedit_selection_end_);
} else if (!committed) {
host->ImeCancelComposition();
}
}
}
-void GtkIMContextWrapper::CompleteComposition() {
+void GtkIMContextWrapper::ConfirmComposition() {
if (!is_enabled_)
return;
- // If WebKit requires to complete current composition, then we need commit
- // existing preedit text and reset the GtkIMContext object.
-
- // Backup existing preedit text to avoid it's being cleared when resetting
- // the GtkIMContext object.
- string16 old_preedit_text = preedit_text_;
-
- // Clear it so that we can know if anything is committed by following
- // line.
- commit_text_.clear();
+ DCHECK(!is_in_key_event_handler_);
- // Resetting the GtkIMContext. Input method may commit something at this
- // point. In this case, we shall not commit the preedit text again.
- gtk_im_context_reset(context_);
+ if (is_composing_text_) {
+ host_view_->GetRenderWidgetHost()->ImeConfirmComposition();
- // If nothing was committed by above line, then commit stored preedit text
- // to prevent data loss.
- if (old_preedit_text.length() && commit_text_.length() == 0) {
- host_view_->GetRenderWidgetHost()->ImeConfirmComposition(
- old_preedit_text);
+ // Reset the input method.
+ CancelComposition();
}
-
- is_composing_text_ = false;
- preedit_text_.clear();
- preedit_cursor_position_ = 0;
}
void GtkIMContextWrapper::HandleCommit(const string16& text) {
@@ -423,46 +413,41 @@ void GtkIMContextWrapper::HandleCommit(const string16& text) {
// It's possible that commit signal is fired without a key event, for
// example when user input via a voice or handwriting recognition software.
// In this case, the text must be committed directly.
- if (!is_in_key_event_handler_) {
+ if (!is_in_key_event_handler_)
host_view_->GetRenderWidgetHost()->ImeConfirmComposition(text);
- }
}
void GtkIMContextWrapper::HandlePreeditStart() {
is_composing_text_ = true;
}
-void GtkIMContextWrapper::HandlePreeditChanged(const string16& text,
+void GtkIMContextWrapper::HandlePreeditChanged(const gchar* text,
+ PangoAttrList* attrs,
int cursor_position) {
- bool changed = false;
- // If preedit text or cursor position is not changed since last time,
- // then it's not necessary to update it again.
- // Preedit text is always stored, so that we can commit it when webkit
- // requires.
// Don't set is_preedit_changed_ to false if there is no change, because
// this handler might be called multiple times with the same data.
- if (cursor_position != preedit_cursor_position_ || text != preedit_text_) {
- preedit_text_ = text;
- preedit_cursor_position_ = cursor_position;
- is_preedit_changed_ = true;
- changed = true;
- }
+ is_preedit_changed_ = true;
+ preedit_text_.clear();
+ preedit_underlines_.clear();
+ preedit_selection_start_ = 0;
+ preedit_selection_end_ = 0;
+
+ ExtractCompositionInfo(text, attrs, cursor_position, &preedit_text_,
+ &preedit_underlines_, &preedit_selection_start_,
+ &preedit_selection_end_);
// In case we are using a buggy input method which doesn't fire
// "preedit_start" signal.
- if (text.length())
+ if (preedit_text_.length())
is_composing_text_ = true;
// Nothing needs to do, if it's currently in ProcessKeyEvent()
// handler, which will send preedit text to webkit later.
// Otherwise, we need send it here if it's been changed.
- if (!is_in_key_event_handler_ && changed) {
- if (text.length()) {
- host_view_->GetRenderWidgetHost()->ImeSetComposition(
- text, cursor_position, -1, -1);
- } else {
- host_view_->GetRenderWidgetHost()->ImeCancelComposition();
- }
+ if (!is_in_key_event_handler_) {
+ host_view_->GetRenderWidgetHost()->ImeSetComposition(
+ preedit_text_, preedit_underlines_, preedit_selection_start_,
+ preedit_selection_end_);
}
}
@@ -471,7 +456,7 @@ void GtkIMContextWrapper::HandlePreeditEnd() {
if (preedit_text_.length()) {
// The composition session has been finished.
preedit_text_.clear();
- preedit_cursor_position_ = 0;
+ preedit_underlines_.clear();
is_preedit_changed_ = true;
changed = true;
}
@@ -479,9 +464,8 @@ void GtkIMContextWrapper::HandlePreeditEnd() {
// If there is still a preedit text when firing "preedit-end" signal,
// we need inform webkit to clear it.
// It's only necessary when it's not in ProcessKeyEvent ().
- if (!is_in_key_event_handler_ && changed) {
+ if (!is_in_key_event_handler_ && changed)
host_view_->GetRenderWidgetHost()->ImeCancelComposition();
- }
// Don't set is_composing_text_ to false here, because "preedit_end"
// signal may be fired before "commit" signal.
@@ -515,10 +499,12 @@ void GtkIMContextWrapper::HandlePreeditStartThunk(
void GtkIMContextWrapper::HandlePreeditChangedThunk(
GtkIMContext* context, GtkIMContextWrapper* self) {
gchar* text = NULL;
+ PangoAttrList* attrs = NULL;
gint cursor_position = 0;
- gtk_im_context_get_preedit_string(context, &text, NULL, &cursor_position);
- self->HandlePreeditChanged(UTF8ToUTF16(text), cursor_position);
+ gtk_im_context_get_preedit_string(context, &text, &attrs, &cursor_position);
+ self->HandlePreeditChanged(text, attrs, cursor_position);
g_free(text);
+ pango_attr_list_unref(attrs);
}
void GtkIMContextWrapper::HandlePreeditEndThunk(
@@ -535,3 +521,97 @@ void GtkIMContextWrapper::HandleHostViewUnrealizeThunk(
GtkWidget* widget, GtkIMContextWrapper* self) {
self->HandleHostViewUnrealize();
}
+
+void GtkIMContextWrapper::ExtractCompositionInfo(
+ const gchar* utf8_text,
+ PangoAttrList* attrs,
+ int cursor_position,
+ string16* utf16_text,
+ std::vector<WebKit::WebCompositionUnderline>* underlines,
+ int* selection_start,
+ int* selection_end) {
+ *utf16_text = UTF8ToUTF16(utf8_text);
+
+ if (utf16_text->empty())
+ return;
+
+ // Gtk/Pango uses character index for cursor position and byte index for
+ // attribute range, but we use char16 offset for them. So we need to do
+ // conversion here.
+ std::vector<int> char16_offsets;
+ int length = static_cast<int>(utf16_text->length());
+ for (int offset = 0; offset < length; ++offset) {
+ char16_offsets.push_back(offset);
+ if (CBU16_IS_SURROGATE((*utf16_text)[offset]))
+ ++offset;
+ }
+
+ // The text length in Unicode characters.
+ int char_length = static_cast<int>(char16_offsets.size());
+ // Make sure we can convert the value of |char_length| as well.
+ char16_offsets.push_back(length);
+
+ int cursor_offset =
+ char16_offsets[std::max(0, std::min(char_length, cursor_position))];
+
+ // TODO(suzhe): due to a bug of webkit, we currently can't use selection range
+ // with composition string. See: https://bugs.webkit.org/show_bug.cgi?id=40805
+ *selection_start = *selection_end = cursor_offset;
+
+ if (attrs) {
+ int utf8_length = strlen(utf8_text);
+ PangoAttrIterator* iter = pango_attr_list_get_iterator(attrs);
+
+ // We only care about underline and background attributes and convert
+ // background attribute into selection if possible.
+ do {
+ gint start, end;
+ pango_attr_iterator_range(iter, &start, &end);
+
+ start = std::min(start, utf8_length);
+ end = std::min(end, utf8_length);
+ if (start >= end)
+ continue;
+
+ start = g_utf8_pointer_to_offset(utf8_text, utf8_text + start);
+ end = g_utf8_pointer_to_offset(utf8_text, utf8_text + end);
+
+ // Double check, in case |utf8_text| is not a valid utf-8 string.
+ start = std::min(start, char_length);
+ end = std::min(end, char_length);
+ if (start >= end)
+ continue;
+
+ PangoAttribute* background_attr =
+ pango_attr_iterator_get(iter, PANGO_ATTR_BACKGROUND);
+ PangoAttribute* underline_attr =
+ pango_attr_iterator_get(iter, PANGO_ATTR_UNDERLINE);
+
+ if (background_attr || underline_attr) {
+ // Use a black thin underline by default.
+ WebKit::WebCompositionUnderline underline(
+ char16_offsets[start], char16_offsets[end], SK_ColorBLACK, false);
+
+ // Always use thick underline for a range with background color, which
+ // is usually the selection range.
+ if (background_attr)
+ underline.thick = true;
+ if (underline_attr) {
+ int type = reinterpret_cast<PangoAttrInt*>(underline_attr)->value;
+ if (type == PANGO_UNDERLINE_DOUBLE)
+ underline.thick = true;
+ else if (type == PANGO_UNDERLINE_ERROR)
+ underline.color = SK_ColorRED;
+ }
+ underlines->push_back(underline);
+ }
+ } while (pango_attr_iterator_next(iter));
+ pango_attr_iterator_destroy(iter);
+ }
+
+ // Use a black thin underline by default.
+ if (underlines->empty()) {
+ underlines->push_back(
+ WebKit::WebCompositionUnderline(0, length, SK_ColorBLACK, false));
+ }
+}
diff --git a/chrome/browser/renderer_host/gtk_im_context_wrapper.h b/chrome/browser/renderer_host/gtk_im_context_wrapper.h
index ef0ac18..c33c290 100644
--- a/chrome/browser/renderer_host/gtk_im_context_wrapper.h
+++ b/chrome/browser/renderer_host/gtk_im_context_wrapper.h
@@ -6,9 +6,14 @@
#define CHROME_BROWSER_RENDERER_HOST_GTK_IM_CONTEXT_WRAPPER_H_
#include <gdk/gdk.h>
+#include <pango/pango-attributes.h>
+#include <vector>
#include "base/basictypes.h"
#include "base/string16.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCompositionUnderline.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebTextInputType.h"
namespace gfx {
class Rect;
@@ -39,14 +44,22 @@ class GtkIMContextWrapper {
// Processes a gdk key event received by |host_view|.
void ProcessKeyEvent(GdkEventKey* event);
- // Updates IME status and caret position.
- void UpdateStatus(int control, const gfx::Rect& caret_rect);
+ void UpdateInputMethodState(WebKit::WebTextInputType type,
+ const gfx::Rect& caret_rect);
void OnFocusIn();
void OnFocusOut();
void AppendInputMethodsContextMenu(MenuGtk* menu);
+ void CancelComposition();
+
+ void ConfirmComposition();
+
private:
+ // For unit tests.
+ class GtkIMContextWrapperTest;
+ FRIEND_TEST(GtkIMContextWrapperTest, ExtractCompositionInfo);
+
// Check if a text needs commit by forwarding a char event instead of
// by confirming as a composition text.
bool NeedCommitByForwardingCharEvent();
@@ -58,8 +71,6 @@ class GtkIMContextWrapper {
// |filtered| indicates if the key event was filtered by the input method.
void ProcessInputMethodResult(const GdkEventKey* event, bool filtered);
- void CompleteComposition();
-
// Real code of "commit" signal handler.
void HandleCommit(const string16& text);
@@ -67,7 +78,9 @@ class GtkIMContextWrapper {
void HandlePreeditStart();
// Real code of "preedit-changed" signal handler.
- void HandlePreeditChanged(const string16& text, int cursor_position);
+ void HandlePreeditChanged(const gchar* text,
+ PangoAttrList* attrs,
+ int cursor_position);
// Real code of "preedit-end" signal handler.
void HandlePreeditEnd();
@@ -96,6 +109,17 @@ class GtkIMContextWrapper {
static void HandleHostViewUnrealizeThunk(GtkWidget* widget,
GtkIMContextWrapper* self);
+ // Extracts composition underlines, selection range and utf-16 text from given
+ // utf-8 text, pango attributes and cursor position.
+ static void ExtractCompositionInfo(
+ const gchar* utf8_text,
+ PangoAttrList* attrs,
+ int cursor_position,
+ string16* utf16_text,
+ std::vector<WebKit::WebCompositionUnderline>* underlines,
+ int* selection_start,
+ int* selection_end);
+
// The parent object.
RenderWidgetHostViewGtk* host_view_;
@@ -139,12 +163,6 @@ class GtkIMContextWrapper {
bool is_composing_text_;
// Whether or not the IME is enabled.
- // This flag is actually controlled by RenderWidget.
- // It shall be set to false when an ImeUpdateStatus message with control ==
- // IME_DISABLE is received, and shall be set to true if control ==
- // IME_COMPLETE_COMPOSITION or IME_MOVE_WINDOWS.
- // When this flag is false, keyboard events shall be dispatched directly
- // instead of sending to context_.
bool is_enabled_;
// Whether or not it's currently running inside key event handler.
@@ -154,13 +172,15 @@ class GtkIMContextWrapper {
bool is_in_key_event_handler_;
// Stores a copy of the most recent preedit text retrieved from context_.
- // When an ImeUpdateStatus message with control == IME_COMPLETE_COMPOSITION
- // is received, this stored preedit text (if not empty) shall be committed,
- // and context_ shall be reset.
string16 preedit_text_;
- // Stores the cursor position in the stored preedit text.
- int preedit_cursor_position_;
+ // Stores the selection range in the stored preedit text.
+ int preedit_selection_start_;
+ int preedit_selection_end_;
+
+ // Stores composition underlines computed from the pango attributes of the
+ // most recent preedit text.
+ std::vector<WebKit::WebCompositionUnderline> preedit_underlines_;
// Whether or not the preedit has been changed since last key event.
bool is_preedit_changed_;
diff --git a/chrome/browser/renderer_host/gtk_im_context_wrapper_unittest.cc b/chrome/browser/renderer_host/gtk_im_context_wrapper_unittest.cc
new file mode 100644
index 0000000..5b8fcb5
--- /dev/null
+++ b/chrome/browser/renderer_host/gtk_im_context_wrapper_unittest.cc
@@ -0,0 +1,154 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#include "chrome/browser/renderer_host/gtk_im_context_wrapper.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+namespace {
+
+struct AttributeInfo {
+ int type;
+ int value;
+ int start_offset;
+ int end_offset;
+};
+
+struct Underline {
+ unsigned startOffset;
+ unsigned endOffset;
+ uint32 color;
+ bool thick;
+};
+
+struct TestData {
+ const char* text;
+ const AttributeInfo attrs[10];
+ const Underline underlines[10];
+};
+
+const TestData kTestData[] = {
+ // Normal case
+ { "One Two Three",
+ { { PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_SINGLE, 0, 3 },
+ { PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_DOUBLE, 4, 7 },
+ { PANGO_ATTR_BACKGROUND, 0, 4, 7 },
+ { PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_SINGLE, 8, 13 },
+ { 0, 0, 0, 0 } },
+ { { 0, 3, SK_ColorBLACK, false },
+ { 4, 7, SK_ColorBLACK, true },
+ { 8, 13, SK_ColorBLACK, false },
+ { 0, 0, 0, false } }
+ },
+
+ // Offset overflow.
+ { "One Two Three",
+ { { PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_SINGLE, 0, 3 },
+ { PANGO_ATTR_BACKGROUND, 0, 4, 7 },
+ { PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_SINGLE, 8, 20 },
+ { 0, 0, 0, 0 } },
+ { { 0, 3, SK_ColorBLACK, false },
+ { 4, 7, SK_ColorBLACK, true },
+ { 8, 13, SK_ColorBLACK, false },
+ { 0, 0, 0, false} }
+ },
+
+ // Error underline.
+ { "One Two Three",
+ { { PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_SINGLE, 0, 3 },
+ { PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_ERROR, 4, 7 },
+ { PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_SINGLE, 8, 13 },
+ { 0, 0, 0, 0 } },
+ { { 0, 3, SK_ColorBLACK, false },
+ { 4, 7, SK_ColorRED, false },
+ { 8, 13, SK_ColorBLACK, false },
+ { 0, 0, 0, false} }
+ },
+
+ // Default underline.
+ { "One Two Three",
+ { { 0, 0, 0, 0 } },
+ { { 0, 13, SK_ColorBLACK, false },
+ { 0, 0, 0, false } }
+ },
+
+ // Unicode, including non-BMP characters: "123你好𠀀𠀁一丁 456"
+ { "123\xE4\xBD\xA0\xE5\xA5\xBD\xF0\xA0\x80\x80\xF0\xA0\x80\x81\xE4\xB8\x80\xE4\xB8\x81 456",
+ { { PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_SINGLE, 0, 3 },
+ { PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_SINGLE, 3, 5 },
+ { PANGO_ATTR_BACKGROUND, 0, 5, 7 },
+ { PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_SINGLE, 7, 13 },
+ { 0, 0, 0, 0 } },
+ { { 0, 3, SK_ColorBLACK, false },
+ { 3, 5, SK_ColorBLACK, false },
+ { 5, 9, SK_ColorBLACK, true },
+ { 9, 15, SK_ColorBLACK, false },
+ { 0, 0, 0, false } }
+ },
+};
+
+void CompareUnderline(const Underline& a,
+ const WebKit::WebCompositionUnderline& b) {
+ EXPECT_EQ(a.startOffset, b.startOffset);
+ EXPECT_EQ(a.endOffset, b.endOffset);
+ EXPECT_EQ(a.color, b.color);
+ EXPECT_EQ(a.thick, b.thick);
+}
+
+class GtkIMContextWrapperTest : public testing::Test {
+};
+
+} // namespace
+
+TEST(GtkIMContextWrapperTest, ExtractCompositionInfo) {
+ for (size_t i = 0; i < arraysize(kTestData); ++i) {
+ const char* text = kTestData[i].text;
+ const AttributeInfo* attrs = kTestData[i].attrs;
+ SCOPED_TRACE(testing::Message() << "Testing:" << i
+ << " text:" << text);
+
+ PangoAttrList* pango_attrs = pango_attr_list_new();
+ for (size_t a = 0; attrs[a].type; ++a) {
+ PangoAttribute* pango_attr = NULL;
+ switch (attrs[a].type) {
+ case PANGO_ATTR_UNDERLINE:
+ pango_attr = pango_attr_underline_new(
+ static_cast<PangoUnderline>(attrs[a].value));
+ break;
+ case PANGO_ATTR_BACKGROUND:
+ pango_attr = pango_attr_background_new(0, 0, 0);
+ break;
+ default:
+ NOTREACHED();
+ }
+ pango_attr->start_index =
+ g_utf8_offset_to_pointer(text, attrs[a].start_offset) - text;
+ pango_attr->end_index =
+ g_utf8_offset_to_pointer(text, attrs[a].end_offset) - text;
+ pango_attr_list_insert(pango_attrs, pango_attr);
+ }
+
+ string16 utf16_text;
+ std::vector<WebKit::WebCompositionUnderline> results;
+ int selection_start;
+ int selection_end;
+
+ GtkIMContextWrapper::ExtractCompositionInfo(text, pango_attrs, 0,
+ &utf16_text, &results, &selection_start, &selection_end);
+
+ const Underline* underlines = kTestData[i].underlines;
+ for (size_t u = 0; underlines[u].color && u < results.size(); ++u) {
+ SCOPED_TRACE(testing::Message() << "Underline:" << u);
+ CompareUnderline(underlines[u], results[u]);
+ }
+
+ pango_attr_list_unref(pango_attrs);
+ }
+}
diff --git a/chrome/browser/renderer_host/render_widget_host.cc b/chrome/browser/renderer_host/render_widget_host.cc
index e84a051..1920101 100644
--- a/chrome/browser/renderer_host/render_widget_host.cc
+++ b/chrome/browser/renderer_host/render_widget_host.cc
@@ -148,7 +148,10 @@ void RenderWidgetHost::OnMessageReceived(const IPC::Message &msg) {
IPC_MESSAGE_HANDLER(ViewHostMsg_Focus, OnMsgFocus)
IPC_MESSAGE_HANDLER(ViewHostMsg_Blur, OnMsgBlur)
IPC_MESSAGE_HANDLER(ViewHostMsg_SetCursor, OnMsgSetCursor)
- IPC_MESSAGE_HANDLER(ViewHostMsg_ImeUpdateStatus, OnMsgImeUpdateStatus)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ImeUpdateTextInputState,
+ OnMsgImeUpdateTextInputState)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ImeCancelComposition,
+ OnMsgImeCancelComposition)
IPC_MESSAGE_HANDLER(ViewHostMsg_GpuRenderingActivated,
OnMsgGpuRenderingActivated)
#if defined(OS_LINUX)
@@ -601,30 +604,32 @@ void RenderWidgetHost::NotifyTextDirection() {
}
}
-void RenderWidgetHost::ImeSetInputMode(bool activate) {
- Send(new ViewMsg_ImeSetInputMode(routing_id(), activate));
+void RenderWidgetHost::SetInputMethodActive(bool activate) {
+ Send(new ViewMsg_SetInputMethodActive(routing_id(), activate));
}
-void RenderWidgetHost::ImeSetComposition(const string16& ime_string,
- int cursor_position,
- int target_start,
- int target_end) {
- Send(new ViewMsg_ImeSetComposition(routing_id(),
- WebKit::WebCompositionCommandSet,
- cursor_position, target_start, target_end,
- ime_string));
+void RenderWidgetHost::ImeSetComposition(
+ const string16& text,
+ const std::vector<WebKit::WebCompositionUnderline>& underlines,
+ int selection_start,
+ int selection_end) {
+ Send(new ViewMsg_ImeSetComposition(
+ routing_id(), text, underlines, selection_start, selection_end));
}
-void RenderWidgetHost::ImeConfirmComposition(const string16& ime_string) {
+void RenderWidgetHost::ImeConfirmComposition(const string16& text) {
Send(new ViewMsg_ImeSetComposition(routing_id(),
- WebKit::WebCompositionCommandConfirm,
- -1, -1, -1, ime_string));
+ text, std::vector<WebKit::WebCompositionUnderline>(), 0, 0));
+ Send(new ViewMsg_ImeConfirmComposition(routing_id()));
+}
+
+void RenderWidgetHost::ImeConfirmComposition() {
+ Send(new ViewMsg_ImeConfirmComposition(routing_id()));
}
void RenderWidgetHost::ImeCancelComposition() {
- Send(new ViewMsg_ImeSetComposition(routing_id(),
- WebKit::WebCompositionCommandDiscard,
- -1, -1, -1, string16()));
+ Send(new ViewMsg_ImeSetComposition(routing_id(), string16(),
+ std::vector<WebKit::WebCompositionUnderline>(), 0, 0));
}
gfx::Rect RenderWidgetHost::GetRootWindowResizerRect() const {
@@ -888,11 +893,16 @@ void RenderWidgetHost::OnMsgSetCursor(const WebCursor& cursor) {
view_->UpdateCursor(cursor);
}
-void RenderWidgetHost::OnMsgImeUpdateStatus(int control,
- const gfx::Rect& caret_rect) {
- if (view_) {
- view_->IMEUpdateStatus(control, caret_rect);
- }
+void RenderWidgetHost::OnMsgImeUpdateTextInputState(
+ WebKit::WebTextInputType type,
+ const gfx::Rect& caret_rect) {
+ if (view_)
+ view_->ImeUpdateTextInputState(type, caret_rect);
+}
+
+void RenderWidgetHost::OnMsgImeCancelComposition() {
+ if (view_)
+ view_->ImeCancelComposition();
}
void RenderWidgetHost::OnMsgGpuRenderingActivated(bool activated) {
diff --git a/chrome/browser/renderer_host/render_widget_host.h b/chrome/browser/renderer_host/render_widget_host.h
index f4aa8df..c7e3cd6 100644
--- a/chrome/browser/renderer_host/render_widget_host.h
+++ b/chrome/browser/renderer_host/render_widget_host.h
@@ -22,7 +22,9 @@
#include "gfx/size.h"
#include "ipc/ipc_channel.h"
#include "ipc/ipc_channel_handle.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCompositionUnderline.h"
#include "third_party/WebKit/WebKit/chromium/public/WebTextDirection.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebTextInputType.h"
namespace gfx {
class Rect;
@@ -316,19 +318,19 @@ class RenderWidgetHost : public IPC::Channel::Listener,
void CancelUpdateTextDirection();
void NotifyTextDirection();
- // Notifies the renderer whether or not the IME attached to this process is
- // activated.
- // When the IME is activated, a renderer process sends IPC messages to notify
- // the status of its composition node. (This message is mainly used for
- // notifying the position of the input cursor so that the browser can
- // display IME windows under the cursor.)
- void ImeSetInputMode(bool activate);
+ // Notifies the renderer whether or not the input method attached to this
+ // process is activated.
+ // When the input method is activated, a renderer process sends IPC messages
+ // to notify the status of its composition node. (This message is mainly used
+ // for notifying the position of the input cursor so that the browser can
+ // display input method windows under the cursor.)
+ void SetInputMethodActive(bool activate);
// Update the composition node of the renderer (or WebKit).
- // WebKit has a special node (a composition node) for IMEs to change its text
- // without affecting any other DOM nodes. When the IME (attached to the
- // browser) updates its text, the browser sends IPC messages to update the
- // composition node of the renderer.
+ // WebKit has a special node (a composition node) for input method to change
+ // its text without affecting any other DOM nodes. When the input method
+ // (attached to the browser) updates its text, the browser sends IPC messages
+ // to update the composition node of the renderer.
// (Read the comments of each function for its detail.)
// Sets the text of the composition node.
@@ -339,10 +341,11 @@ class RenderWidgetHost : public IPC::Channel::Listener,
// (on Windows);
// * when it receives a "preedit_changed" signal of GtkIMContext (on Linux);
// * when markedText of NSTextInput is called (on Mac).
- void ImeSetComposition(const string16& ime_string,
- int cursor_position,
- int target_start,
- int target_end);
+ void ImeSetComposition(
+ const string16& text,
+ const std::vector<WebKit::WebCompositionUnderline>& underlines,
+ int selection_start,
+ int selection_end);
// Finishes an ongoing composition with the specified text.
// A browser should call this function:
@@ -350,7 +353,11 @@ class RenderWidgetHost : public IPC::Channel::Listener,
// (on Windows);
// * when it receives a "commit" signal of GtkIMContext (on Linux);
// * when insertText of NSTextInput is called (on Mac).
- void ImeConfirmComposition(const string16& ime_string);
+ void ImeConfirmComposition(const string16& text);
+
+ // Finishes an ongoing composition with the composition text set by last
+ // SetComposition() call.
+ void ImeConfirmComposition();
// Cancels an ongoing composition.
void ImeCancelComposition();
@@ -480,9 +487,9 @@ class RenderWidgetHost : public IPC::Channel::Listener,
void OnMsgBlur();
void OnMsgSetCursor(const WebCursor& cursor);
- // Using int instead of ViewHostMsg_ImeControl for control's type to avoid
- // having to bring in render_messages.h in a header file.
- void OnMsgImeUpdateStatus(int control, const gfx::Rect& caret_rect);
+ void OnMsgImeUpdateTextInputState(WebKit::WebTextInputType type,
+ const gfx::Rect& caret_rect);
+ void OnMsgImeCancelComposition();
void OnMsgGpuRenderingActivated(bool activated);
diff --git a/chrome/browser/renderer_host/render_widget_host_view.h b/chrome/browser/renderer_host/render_widget_host_view.h
index d9e7bae..9a20af0 100644
--- a/chrome/browser/renderer_host/render_widget_host_view.h
+++ b/chrome/browser/renderer_host/render_widget_host_view.h
@@ -14,6 +14,7 @@
#include "gfx/native_widget_types.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/WebKit/WebKit/chromium/public/WebPopupType.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebTextInputType.h"
#include "webkit/glue/plugins/webplugin.h"
#include "webkit/glue/webaccessibility.h"
@@ -109,8 +110,12 @@ class RenderWidgetHostView {
// Indicates whether the page has finished loading.
virtual void SetIsLoading(bool is_loading) = 0;
- // Enable or disable IME for the view.
- virtual void IMEUpdateStatus(int control, const gfx::Rect& caret_rect) = 0;
+ // Updates the state of the input method attached to the view.
+ virtual void ImeUpdateTextInputState(WebKit::WebTextInputType type,
+ const gfx::Rect& caret_rect) = 0;
+
+ // Cancel the ongoing composition of the input method attached to the view.
+ virtual void ImeCancelComposition() = 0;
// Informs the view that a portion of the widget's backing store was scrolled
// and/or painted. The view should ensure this gets copied to the screen.
diff --git a/chrome/browser/renderer_host/render_widget_host_view_gtk.cc b/chrome/browser/renderer_host/render_widget_host_view_gtk.cc
index c3ce693..542dcf6 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_gtk.cc
+++ b/chrome/browser/renderer_host/render_widget_host_view_gtk.cc
@@ -230,6 +230,10 @@ class RenderWidgetHostViewGtkWidget {
if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS)
return FALSE;
+ // Confirm existing composition text on mouse click events, to make sure
+ // the input caret won't be moved with an ongoing composition session.
+ host_view->im_context_->ConfirmComposition();
+
// We want to translate the coordinates of events that do not originate
// from this widget to be relative to the top left of the widget.
GtkWidget* event_widget = gtk_get_event_widget(
@@ -650,9 +654,14 @@ void RenderWidgetHostViewGtk::SetIsLoading(bool is_loading) {
ShowCurrentCursor();
}
-void RenderWidgetHostViewGtk::IMEUpdateStatus(int control,
- const gfx::Rect& caret_rect) {
- im_context_->UpdateStatus(control, caret_rect);
+void RenderWidgetHostViewGtk::ImeUpdateTextInputState(
+ WebKit::WebTextInputType type,
+ const gfx::Rect& caret_rect) {
+ im_context_->UpdateInputMethodState(type, caret_rect);
+}
+
+void RenderWidgetHostViewGtk::ImeCancelComposition() {
+ im_context_->CancelComposition();
}
void RenderWidgetHostViewGtk::DidUpdateBackingStore(
diff --git a/chrome/browser/renderer_host/render_widget_host_view_gtk.h b/chrome/browser/renderer_host/render_widget_host_view_gtk.h
index fa5ff74..f33cd55 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_gtk.h
+++ b/chrome/browser/renderer_host/render_widget_host_view_gtk.h
@@ -65,7 +65,9 @@ class RenderWidgetHostViewGtk : public RenderWidgetHostView {
virtual gfx::Rect GetViewBounds() const;
virtual void UpdateCursor(const WebCursor& cursor);
virtual void SetIsLoading(bool is_loading);
- virtual void IMEUpdateStatus(int control, const gfx::Rect& caret_rect);
+ virtual void ImeUpdateTextInputState(WebKit::WebTextInputType type,
+ const gfx::Rect& caret_rect);
+ virtual void ImeCancelComposition();
virtual void DidUpdateBackingStore(
const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy,
const std::vector<gfx::Rect>& copy_rects);
diff --git a/chrome/browser/renderer_host/render_widget_host_view_mac.h b/chrome/browser/renderer_host/render_widget_host_view_mac.h
index d12458c..0ed5a3b 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_mac.h
+++ b/chrome/browser/renderer_host/render_widget_host_view_mac.h
@@ -15,6 +15,7 @@
#include "chrome/browser/cocoa/base_view.h"
#include "chrome/browser/renderer_host/accelerated_surface_container_manager_mac.h"
#include "chrome/browser/renderer_host/render_widget_host_view.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCompositionUnderline.h"
#include "webkit/glue/webcursor.h"
#include "webkit/glue/webmenuitem.h"
@@ -32,7 +33,8 @@ class RWHVMEditCommandHelper;
// when it's removed from the view system.
@interface RenderWidgetHostViewCocoa
- : BaseView <RenderWidgetHostViewMacOwner, NSTextInput, NSChangeSpelling> {
+ : BaseView <RenderWidgetHostViewMacOwner, NSTextInputClient,
+ NSChangeSpelling> {
@private
scoped_ptr<RenderWidgetHostViewMac> renderWidgetHostView_;
BOOL canBeKeyView_;
@@ -79,6 +81,10 @@ class RWHVMEditCommandHelper;
// Indicates if there is any marked text.
BOOL hasMarkedText_;
+ // Indicates if unmarkText is called or not when handling a keyboard
+ // event.
+ BOOL unmarkTextCalled_;
+
// The range of current marked text inside the whole content of the DOM node
// being edited.
// TODO(suzhe): This is currently a fake value, as we do not support accessing
@@ -95,8 +101,11 @@ class RWHVMEditCommandHelper;
// Text to be inserted which was generated by handling a key down event.
string16 textToBeInserted_;
- // New marked text which was generated by handling a key down event.
- string16 newMarkedText_;
+ // Marked text which was generated by handling a key down event.
+ string16 markedText_;
+
+ // Underline information of the |markedText_|.
+ std::vector<WebKit::WebCompositionUnderline> underlines_;
}
@property(assign, nonatomic) NSRect caretRect;
@@ -119,6 +128,8 @@ class RWHVMEditCommandHelper;
- (void)renderWidgetHostWasResized;
// Cancel ongoing composition (abandon the marked text).
- (void)cancelComposition;
+// Confirm ongoing composition.
+- (void)confirmComposition;
@end
@@ -166,7 +177,9 @@ class RenderWidgetHostViewMac : public RenderWidgetHostView {
virtual gfx::Rect GetViewBounds() const;
virtual void UpdateCursor(const WebCursor& cursor);
virtual void SetIsLoading(bool is_loading);
- virtual void IMEUpdateStatus(int control, const gfx::Rect& caret_rect);
+ virtual void ImeUpdateTextInputState(WebKit::WebTextInputType state,
+ const gfx::Rect& caret_rect);
+ virtual void ImeCancelComposition();
virtual void DidUpdateBackingStore(
const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy,
const std::vector<gfx::Rect>& copy_rects);
@@ -214,6 +227,8 @@ class RenderWidgetHostViewMac : public RenderWidgetHostView {
void set_parent_view(NSView* parent_view) { parent_view_ = parent_view; }
+ void SetTextInputActive(bool active);
+
// These member variables should be private, but the associated ObjC class
// needs access to them and can't be made a friend.
@@ -243,6 +258,9 @@ class RenderWidgetHostViewMac : public RenderWidgetHostView {
// The time it took after this view was selected for it to be fully painted.
base::TimeTicks tab_switch_paint_time_;
+ // Current text input type.
+ WebKit::WebTextInputType text_input_type_;
+
private:
// Updates the display cursor to the current cursor if the cursor is over this
// render view.
diff --git a/chrome/browser/renderer_host/render_widget_host_view_mac.mm b/chrome/browser/renderer_host/render_widget_host_view_mac.mm
index bf30c38..fa1fb81 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/chrome/browser/renderer_host/render_widget_host_view_mac.mm
@@ -9,6 +9,7 @@
#include "app/surface/io_surface_support_mac.h"
#import "base/chrome_application_mac.h"
#include "base/histogram.h"
+#include "base/logging.h"
#import "base/scoped_nsobject.h"
#include "base/string_util.h"
#include "base/sys_string_conversions.h"
@@ -25,6 +26,7 @@
#include "chrome/common/plugin_messages.h"
#include "chrome/common/render_messages.h"
#include "skia/ext/platform_canvas.h"
+#include "third_party/skia/include/core/SkColor.h"
#include "third_party/WebKit/WebKit/chromium/public/mac/WebInputEventFactory.h"
#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
#include "webkit/glue/webmenurunner_mac.h"
@@ -51,13 +53,77 @@ static inline int ToWebKitModifiers(NSUInteger flags) {
- (void)attachPluginLayer;
@end
+// This API was published since 10.6. Provide the declaration so it can be
+// // called below when building with the 10.5 SDK.
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5
+@class NSTextInputContext;
+@interface NSResponder (AppKitDetails)
+- (NSTextInputContext *)inputContext;
+@end
+#endif
+
namespace {
// Maximum number of characters we allow in a tooltip.
const size_t kMaxTooltipLength = 1024;
+// TODO(suzhe): Upstream this function.
+WebKit::WebColor WebColorFromNSColor(NSColor *color) {
+ CGFloat r, g, b, a;
+ [color getRed:&r green:&g blue:&b alpha:&a];
+
+ return
+ std::max(0, std::min(static_cast<int>(lroundf(255.0f * a)), 255)) << 24 |
+ std::max(0, std::min(static_cast<int>(lroundf(255.0f * r)), 255)) << 16 |
+ std::max(0, std::min(static_cast<int>(lroundf(255.0f * g)), 255)) << 8 |
+ std::max(0, std::min(static_cast<int>(lroundf(255.0f * b)), 255));
+}
+
+// Extract underline information from an attributed string.
+// Mostly copied from third_party/WebKit/WebKit/mac/WebView/WebHTMLView.mm
+void ExtractUnderlines(
+ NSAttributedString* string,
+ std::vector<WebKit::WebCompositionUnderline>* underlines) {
+ int length = [[string string] length];
+ int i = 0;
+ while (i < length) {
+ NSRange range;
+ NSDictionary* attrs = [string attributesAtIndex:i
+ longestEffectiveRange:&range
+ inRange:NSMakeRange(i, length - i)];
+ if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
+ WebKit::WebColor color = SK_ColorBLACK;
+ if (NSColor *colorAttr =
+ [attrs objectForKey:NSUnderlineColorAttributeName]) {
+ color = WebColorFromNSColor(
+ [colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
+ }
+ underlines->push_back(WebKit::WebCompositionUnderline(
+ range.location, NSMaxRange(range), color, [style intValue] > 1));
+ }
+ i = range.location + range.length;
+ }
+}
+
+// EnablePasswordInput() and DisablePasswordInput() are copied from
+// enableSecureTextInput() and disableSecureTextInput() functions in
+// third_party/WebKit/WebCore/platform/SecureTextInput.cpp
+// But we don't call EnableSecureEventInput() and DisableSecureEventInput()
+// here, because they are already called in webkit and they are system wide
+// functions.
+void EnablePasswordInput() {
+ CFArrayRef inputSources = TISCreateASCIICapableInputSourceList();
+ TSMSetDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag,
+ sizeof(CFArrayRef), &inputSources);
+ CFRelease(inputSources);
}
+void DisablePasswordInput() {
+ TSMRemoveDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag);
+}
+
+} // namespace
+
// AcceleratedPluginLayer ------------------------------------------------------
// This subclass of CAOpenGLLayer hosts the output of accelerated plugins on
@@ -131,6 +197,7 @@ RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget)
: render_widget_host_(widget),
about_to_validate_and_paint_(false),
call_set_needs_display_in_rect_pending_(false),
+ text_input_type_(WebKit::WebTextInputTypeNone),
is_loading_(false),
is_hidden_(false),
is_popup_menu_(false),
@@ -314,22 +381,27 @@ void RenderWidgetHostViewMac::SetIsLoading(bool is_loading) {
UpdateCursorIfOverSelf();
}
-void RenderWidgetHostViewMac::IMEUpdateStatus(int control,
- const gfx::Rect& caret_rect) {
- // Reset the IME state and finish an ongoing composition in the renderer.
- if (control == IME_DISABLE || control == IME_COMPLETE_COMPOSITION ||
- control == IME_CANCEL_COMPOSITION) {
- [cocoa_view_ cancelComposition];
+void RenderWidgetHostViewMac::ImeUpdateTextInputState(
+ WebKit::WebTextInputType type,
+ const gfx::Rect& caret_rect) {
+ if (text_input_type_ != type) {
+ text_input_type_ = type;
+ if (HasFocus())
+ SetTextInputActive(true);
}
// We need to convert the coordinate of the cursor rectangle sent from the
- // renderer and save it. Our IME backend uses a coordinate system whose
- // origin is the upper-left corner of this view. On the other hand, Cocoa
- // uses a coordinate system whose origin is the lower-left corner of this
- // view. So, we convert the cursor rectangle and save it.
+ // renderer and save it. Our input method backend uses a coordinate system
+ // whose origin is the upper-left corner of this view. On the other hand,
+ // Cocoa uses a coordinate system whose origin is the lower-left corner of
+ // this view. So, we convert the cursor rectangle and save it.
[cocoa_view_ setCaretRect:[cocoa_view_ RectToNSRect:caret_rect]];
}
+void RenderWidgetHostViewMac::ImeCancelComposition() {
+ [cocoa_view_ cancelComposition];
+}
+
void RenderWidgetHostViewMac::DidUpdateBackingStore(
const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy,
const std::vector<gfx::Rect>& copy_rects) {
@@ -669,6 +741,8 @@ gfx::Rect RenderWidgetHostViewMac::GetRootWindowRect() {
void RenderWidgetHostViewMac::SetActive(bool active) {
if (render_widget_host_)
render_widget_host_->SetActive(active);
+ if (HasFocus())
+ SetTextInputActive(active);
}
void RenderWidgetHostViewMac::SetWindowVisibility(bool visible) {
@@ -701,6 +775,18 @@ bool RenderWidgetHostViewMac::ContainsNativeView(
return false;
}
+void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
+ if (active) {
+ if (text_input_type_ == WebKit::WebTextInputTypePassword)
+ EnablePasswordInput();
+ else
+ DisablePasswordInput();
+ } else {
+ if (text_input_type_ == WebKit::WebTextInputTypePassword)
+ DisablePasswordInput();
+ }
+}
+
// EditCommandMatcher ---------------------------------------------------------
// This class is used to capture the shortcuts that a given key event maps to.
@@ -791,20 +877,28 @@ bool RenderWidgetHostViewMac::ContainsNativeView(
// the focus, and then EditorClientImpl::textFieldDidEndEditing() would cancel
// the popup anyway, so we're OK.
+ NSEventType type = [theEvent type];
+ if (type == NSLeftMouseDown)
+ hasOpenMouseDown_ = YES;
+ else if (type == NSLeftMouseUp)
+ hasOpenMouseDown_ = NO;
+
+ // TODO(suzhe): We should send mouse events to the input method first if it
+ // wants to handle them. But it won't work without implementing method
+ // - (NSUInteger)characterIndexForPoint:.
+ // See: http://code.google.com/p/chromium/issues/detail?id=47141
+ // Instead of sending mouse events to the input method first, we now just
+ // simply confirm all ongoing composition here.
+ if (type == NSLeftMouseDown || type == NSRightMouseDown ||
+ type == NSOtherMouseDown) {
+ [self confirmComposition];
+ }
+
const WebMouseEvent& event =
WebInputEventFactory::mouseEvent(theEvent, self);
+
if (renderWidgetHostView_->render_widget_host_)
renderWidgetHostView_->render_widget_host_->ForwardMouseEvent(event);
-
- if ([theEvent type] == NSLeftMouseDown) {
- [self cancelComposition];
-
- hasOpenMouseDown_ = YES;
- }
-
- if ([theEvent type] == NSLeftMouseUp) {
- hasOpenMouseDown_ = NO;
- }
}
- (BOOL)performKeyEquivalent:(NSEvent*)theEvent {
@@ -899,7 +993,9 @@ bool RenderWidgetHostViewMac::ContainsNativeView(
// These two variables might be set when handling the keyboard event.
// Clear them here so that we can know whether they have changed afterwards.
textToBeInserted_.clear();
- newMarkedText_.clear();
+ markedText_.clear();
+ underlines_.clear();
+ unmarkTextCalled_ = NO;
// Sends key down events to input method first, then we can decide what should
// be done according to input method's feedback.
@@ -936,7 +1032,7 @@ bool RenderWidgetHostViewMac::ContainsNativeView(
// Then send keypress and/or composition related events.
// If there was a marked text or the text to be inserted is longer than 1
- // character, then we send the text by calling ImeConfirmComposition().
+ // character, then we send the text by calling ConfirmComposition().
// Otherwise, if the text to be inserted only contains 1 character, then we
// can just send a keypress event which is fabricated by changing the type of
// the keydown event, so that we can retain all necessary informations, such
@@ -945,7 +1041,6 @@ bool RenderWidgetHostViewMac::ContainsNativeView(
// Note that, |textToBeInserted_| is a UTF-16 string, but it's fine to only
// handle BMP characters here, as we can always insert non-BMP characters as
// text.
- const NSUInteger kCtrlCmdKeyMask = NSControlKeyMask | NSCommandKeyMask;
BOOL textInserted = NO;
if (textToBeInserted_.length() > (oldHasMarkedText ? 0 : 1)) {
widgetHost->ImeConfirmComposition(textToBeInserted_);
@@ -956,13 +1051,11 @@ bool RenderWidgetHostViewMac::ContainsNativeView(
event.text[1] = 0;
event.skip_in_browser = true;
widgetHost->ForwardKeyboardEvent(event);
- } else if (([theEvent modifierFlags] & kCtrlCmdKeyMask) &&
+ } else if (!hasMarkedText_ && !oldHasMarkedText &&
[[theEvent characters] length] > 0) {
// We don't get insertText: calls if ctrl is down and not even a keyDown:
- // call if cmd is down, so synthesize a keypress event for these cases.
- // Note that this makes our behavior deviate from the windows and linux
- // versions of chrome (however, see http://crbug.com/13891 ), but it makes
- // us behave similar to how Safari behaves.
+ // call if cmd is down, or in password input mode, so synthesize a keypress
+ // event for these cases.
event.type = WebKit::WebInputEvent::Char;
event.skip_in_browser = true;
widgetHost->ForwardKeyboardEvent(event);
@@ -970,18 +1063,19 @@ bool RenderWidgetHostViewMac::ContainsNativeView(
// Updates or cancels the composition. If some text has been inserted, then
// we don't need to cancel the composition explicitly.
- if (hasMarkedText_ && newMarkedText_.length()) {
+ if (hasMarkedText_ && markedText_.length()) {
// Sends the updated marked text to the renderer so it can update the
// composition node in WebKit.
// When marked text is available, |selectedRange_| will be the range being
- // selected inside the marked text. We put the cursor at the beginning of
- // the selected range.
- widgetHost->ImeSetComposition(newMarkedText_,
- selectedRange_.location,
- selectedRange_.location,
- NSMaxRange(selectedRange_));
+ // selected inside the marked text.
+ widgetHost->ImeSetComposition(markedText_, underlines_,
+ selectedRange_.location,
+ NSMaxRange(selectedRange_));
} else if (oldHasMarkedText && !hasMarkedText_ && !textInserted) {
- widgetHost->ImeCancelComposition();
+ if (unmarkTextCalled_)
+ widgetHost->ImeConfirmComposition();
+ else
+ widgetHost->ImeCancelComposition();
}
// Possibly autohide the cursor.
@@ -1183,19 +1277,34 @@ bool RenderWidgetHostViewMac::ContainsNativeView(
return NO;
renderWidgetHostView_->render_widget_host_->Focus();
- renderWidgetHostView_->render_widget_host_->ImeSetInputMode(true);
+ renderWidgetHostView_->render_widget_host_->SetInputMethodActive(true);
+ renderWidgetHostView_->SetTextInputActive(true);
+
+ // Cancel any onging composition text which was left before we lost focus.
+ // TODO(suzhe): We should do it in -resignFirstResponder: method, but
+ // somehow that method won't be called when switching among different tabs.
+ // See http://crbug.com/47209
+ [self cancelComposition];
+
return YES;
}
- (BOOL)resignFirstResponder {
+ renderWidgetHostView_->SetTextInputActive(false);
if (!renderWidgetHostView_->render_widget_host_)
return YES;
if (closeOnDeactivate_)
renderWidgetHostView_->KillSelf();
- renderWidgetHostView_->render_widget_host_->ImeSetInputMode(false);
+ renderWidgetHostView_->render_widget_host_->SetInputMethodActive(false);
renderWidgetHostView_->render_widget_host_->Blur();
+
+ // We should cancel any onging composition whenever RWH's Blur() method gets
+ // called, because in this case, webkit will confirm the ongoing composition
+ // internally.
+ [self cancelComposition];
+
return YES;
}
@@ -1469,7 +1578,7 @@ static const NSTrackingRectTag kTrackingRectTag = 0xBADFACE;
return [[toolTip_ copy] autorelease];
}
-// Below is our NSTextInput implementation.
+// Below is our NSTextInputClient implementation.
//
// When WebHTMLView receives a NSKeyDown event, WebHTMLView calls the following
// functions to process this event.
@@ -1509,9 +1618,9 @@ static const NSTrackingRectTag kTrackingRectTag = 0xBADFACE;
//
// This needs many sync IPC messages sent between a browser and a renderer for
// each key event, which would probably result in key-typing jank.
-// To avoid this problem, this implementation processes key events (and IME
-// events) totally in a browser process and sends asynchronous input events,
-// almost same as KeyboardEvents (and TextEvents) of DOM Level 3, to a
+// To avoid this problem, this implementation processes key events (and input
+// method events) totally in a browser process and sends asynchronous input
+// events, almost same as KeyboardEvents (and TextEvents) of DOM Level 3, to a
// renderer process.
//
// [RenderWidgetHostViewMac keyEvent] (browser) ->
@@ -1546,18 +1655,28 @@ extern NSString *NSTextInputReplacementRangeAttributeName;
return NSNotFound;
}
-- (NSRect)firstRectForCharacterRange:(NSRange)theRange {
+- (NSRect)firstRectForCharacterRange:(NSRange)theRange
+ actualRange:(NSRangePointer)actualRange {
// An input method requests a cursor rectangle to display its candidate
// window.
// Calculate the screen coordinate of the cursor rectangle saved in
- // RenderWidgetHostViewMac::IMEUpdateStatus() and send it to the IME.
+ // RenderWidgetHostViewMac::ImeUpdateTextInputState() and send it to the
+ // input method.
// Since this window may be moved since we receive the cursor rectangle last
- // time we sent the cursor rectangle to the IME, so we should map from the
- // view coordinate to the screen coordinate every time when an IME need it.
+ // time we sent the cursor rectangle to the input method, so we should map
+ // from the view coordinate to the screen coordinate every time when an input
+ // method need it.
NSRect resultRect = [self convertRect:caretRect_ toView:nil];
NSWindow* window = [self window];
if (window)
resultRect.origin = [window convertBaseToScreen:resultRect.origin];
+
+ // If marked text is available, then we actually return the rect of the
+ // selected range within the marked text. Otherwise, we actually can't get
+ // the rect of an arbitrary range in the web content, so just return the
+ // caret rect instead and don't touch actualRange at all.
+ if (actualRange && hasMarkedText_)
+ *actualRange = selectedRange_;
return resultRect;
}
@@ -1576,7 +1695,8 @@ extern NSString *NSTextInputReplacementRangeAttributeName;
return hasMarkedText_ ? markedRange_ : NSMakeRange(NSNotFound, 0);
}
-- (NSAttributedString *)attributedSubstringFromRange:(NSRange)nsRange {
+- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range
+ actualRange:(NSRangePointer)actualRange {
// TODO(hbono): Even though many input method works without implementing
// this method, we need to save a copy of the string in the setMarkedText
// method and create a NSAttributedString with the given range.
@@ -1588,6 +1708,20 @@ extern NSString *NSTextInputReplacementRangeAttributeName;
return reinterpret_cast<NSInteger>(self);
}
+// Each RenderWidgetHostViewCocoa has its own input context, but we return
+// nil when the caret is in non-editable content or password box to avoid
+// making input methods do their work.
+- (NSTextInputContext *)inputContext
+{
+ switch(renderWidgetHostView_->text_input_type_) {
+ case WebKit::WebTextInputTypeNone:
+ case WebKit::WebTextInputTypePassword:
+ return nil;
+ default:
+ return [super inputContext];
+ }
+}
+
- (BOOL)hasMarkedText {
// An input method calls this function to figure out whether or not an
// application is really composing a text. If it is composing, it calls
@@ -1605,37 +1739,53 @@ extern NSString *NSTextInputReplacementRangeAttributeName;
// text when it cancels an ongoing composition, i.e. I have never seen an
// input method calls this method.
hasMarkedText_ = NO;
+ markedText_.clear();
+ underlines_.clear();
- // If we are handling a key down event, then ImeCancelComposition() will be
+ // If we are handling a key down event, then ConfirmComposition() will be
// called in keyEvent: method.
if (!handlingKeyDown_)
- renderWidgetHostView_->render_widget_host_->ImeCancelComposition();
+ renderWidgetHostView_->render_widget_host_->ImeConfirmComposition();
+ else
+ unmarkTextCalled_ = YES;
}
-- (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange {
+- (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange
+ replacementRange:(NSRange)replacementRange {
// An input method updates the composition string.
// We send the given text and range to the renderer so it can update the
// composition node of WebKit.
+ // TODO(suzhe): It's hard for us to support replacementRange without accessing
+ // the full web content.
BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
NSString* im_text = isAttributedString ? [string string] : string;
int length = [im_text length];
markedRange_ = NSMakeRange(0, length);
selectedRange_ = newSelRange;
- newMarkedText_ = base::SysNSStringToUTF16(im_text);
+ markedText_ = base::SysNSStringToUTF16(im_text);
hasMarkedText_ = (length > 0);
- // If we are handling a key down event, then ImeSetComposition() will be
+ underlines_.clear();
+ if (isAttributedString) {
+ ExtractUnderlines(string, &underlines_);
+ } else {
+ // Use a thin black underline by default.
+ underlines_.push_back(
+ WebKit::WebCompositionUnderline(0, length, SK_ColorBLACK, false));
+ }
+
+ // If we are handling a key down event, then SetComposition() will be
// called in keyEvent: method.
// Input methods of Mac use setMarkedText calls with an empty text to cancel
// an ongoing composition. So, we should check whether or not the given text
- // is empty to update the IME state. (Our IME backend can automatically
- // cancels an ongoing composition when we send an empty text. So, it is OK
- // to send an empty text to the renderer.)
+ // is empty to update the input method state. (Our input method backend can
+ // automatically cancels an ongoing composition when we send an empty text.
+ // So, it is OK to send an empty text to the renderer.)
if (!handlingKeyDown_) {
renderWidgetHostView_->render_widget_host_->ImeSetComposition(
- newMarkedText_, newSelRange.location, newSelRange.location,
- NSMaxRange(newSelRange));
+ markedText_, underlines_,
+ newSelRange.location, NSMaxRange(newSelRange));
}
}
@@ -1662,7 +1812,7 @@ extern NSString *NSTextInputReplacementRangeAttributeName;
}
}
-- (void)insertText:(id)string {
+- (void)insertText:(id)string replacementRange:(NSRange)replacementRange {
// An input method has characters to be inserted.
// Same as Linux, Mac calls this method not only:
// * when an input method finishs composing text, but also;
@@ -1671,11 +1821,13 @@ extern NSString *NSTextInputReplacementRangeAttributeName;
// a Char event so it is dispatched to an onkeypress() event handler of
// JavaScript.
// On the other hand, when we are using input methods, we should send the
- // given characters as an IME event and prevent the characters from being
- // dispatched to onkeypress() event handlers.
+ // given characters as an input method event and prevent the characters from
+ // being dispatched to onkeypress() event handlers.
// Text inserting might be initiated by other source instead of keyboard
// events, such as the Characters dialog. In this case the text should be
- // sent as an IME event as well.
+ // sent as an input method event as well.
+ // TODO(suzhe): It's hard for us to support replacementRange without accessing
+ // the full web content.
BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]];
NSString* im_text = isAttributedString ? [string string] : string;
if (handlingKeyDown_) {
@@ -1823,7 +1975,19 @@ extern NSString *NSTextInputReplacementRangeAttributeName;
NSInputManager *currentInputManager = [NSInputManager currentInputManager];
[currentInputManager markedTextAbandoned:self];
- [self unmarkText];
+ hasMarkedText_ = NO;
+ // Should not call [self unmarkText] here, because it'll send unnecessary
+ // cancel composition IPC message to the renderer.
+}
+
+- (void)confirmComposition {
+ if (!hasMarkedText_)
+ return;
+
+ if (renderWidgetHostView_->render_widget_host_)
+ renderWidgetHostView_->render_widget_host_->ImeConfirmComposition();
+
+ [self cancelComposition];
}
@end
diff --git a/chrome/browser/renderer_host/render_widget_host_view_win.cc b/chrome/browser/renderer_host/render_widget_host_view_win.cc
index 0905f8e..5090b38 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_win.cc
+++ b/chrome/browser/renderer_host/render_widget_host_view_win.cc
@@ -281,7 +281,8 @@ RenderWidgetHostViewWin::RenderWidgetHostViewWin(RenderWidgetHost* widget)
shutdown_factory_(this),
parent_hwnd_(NULL),
is_loading_(false),
- visually_deemphasized_(false) {
+ visually_deemphasized_(false),
+ text_input_type_(WebKit::WebTextInputTypeNone) {
render_widget_host_->set_view(this);
registrar_.Add(this,
NotificationType::RENDERER_PROCESS_TERMINATED,
@@ -612,16 +613,24 @@ void RenderWidgetHostViewWin::SetIsLoading(bool is_loading) {
UpdateCursorIfOverSelf();
}
-void RenderWidgetHostViewWin::IMEUpdateStatus(int control,
- const gfx::Rect& caret_rect) {
- if (control == IME_DISABLE) {
- ime_input_.DisableIME(m_hWnd);
- } else if (control == IME_CANCEL_COMPOSITION) {
- ime_input_.CancelIME(m_hWnd);
- } else {
- ime_input_.EnableIME(m_hWnd, caret_rect,
- control == IME_COMPLETE_COMPOSITION);
+void RenderWidgetHostViewWin::ImeUpdateTextInputState(
+ WebKit::WebTextInputType type,
+ const gfx::Rect& caret_rect) {
+ if (text_input_type_ != type) {
+ text_input_type_ = type;
+ if (type == WebKit::WebTextInputTypeText)
+ ime_input_.EnableIME(m_hWnd);
+ else
+ ime_input_.DisableIME(m_hWnd);
}
+
+ // Only update caret position if the input method is enabled.
+ if (type == WebKit::WebTextInputTypeText)
+ ime_input_.UpdateCaretRect(m_hWnd, caret_rect);
+}
+
+void RenderWidgetHostViewWin::ImeCancelComposition() {
+ ime_input_.CancelIME(m_hWnd);
}
BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lparam) {
@@ -1095,8 +1104,8 @@ void RenderWidgetHostViewWin::OnInputLangChange(DWORD character_set,
// successfully (because Action 1 shows ime_status = !ime_notification_.)
bool ime_status = ime_input_.SetInputLanguage();
if (ime_status != ime_notification_) {
- if (Send(new ViewMsg_ImeSetInputMode(render_widget_host_->routing_id(),
- ime_status))) {
+ if (render_widget_host_) {
+ render_widget_host_->SetInputMethodActive(ime_status);
ime_notification_ = ime_status;
}
}
@@ -1148,8 +1157,8 @@ LRESULT RenderWidgetHostViewWin::OnImeSetContext(
// Therefore, we just start/stop status messages according to the activation
// status of this application without checks.
bool activated = (wparam == TRUE);
- if (Send(new ViewMsg_ImeSetInputMode(
- render_widget_host_->routing_id(), activated))) {
+ if (render_widget_host_) {
+ render_widget_host_->SetInputMethodActive(activated);
ime_notification_ = activated;
}
@@ -1188,12 +1197,7 @@ LRESULT RenderWidgetHostViewWin::OnImeComposition(
// and send it to a renderer process.
ImeComposition composition;
if (ime_input_.GetResult(m_hWnd, lparam, &composition)) {
- Send(new ViewMsg_ImeSetComposition(render_widget_host_->routing_id(),
- WebKit::WebCompositionCommandConfirm,
- composition.cursor_position,
- composition.target_start,
- composition.target_end,
- composition.ime_string));
+ render_widget_host_->ImeConfirmComposition(composition.ime_string);
ime_input_.ResetComposition(m_hWnd);
// Fall though and try reading the composition string.
// Japanese IMEs send a message containing both GCS_RESULTSTR and
@@ -1203,12 +1207,9 @@ LRESULT RenderWidgetHostViewWin::OnImeComposition(
// Retrieve the composition string and its attributes of the ongoing
// composition and send it to a renderer process.
if (ime_input_.GetComposition(m_hWnd, lparam, &composition)) {
- Send(new ViewMsg_ImeSetComposition(render_widget_host_->routing_id(),
- WebKit::WebCompositionCommandSet,
- composition.cursor_position,
- composition.target_start,
- composition.target_end,
- composition.ime_string));
+ render_widget_host_->ImeSetComposition(
+ composition.ime_string, composition.underlines,
+ composition.selection_start, composition.selection_end);
}
// We have to prevent WTL from calling ::DefWindowProc() because we do not
// want for the IMM (Input Method Manager) to send WM_IME_CHAR messages.
@@ -1226,10 +1227,7 @@ LRESULT RenderWidgetHostViewWin::OnImeEndComposition(
// i.e. the ongoing composition has been canceled.
// We need to reset the composition status both of the ImeInput object and
// of the renderer process.
- std::wstring empty_string;
- Send(new ViewMsg_ImeSetComposition(render_widget_host_->routing_id(),
- WebKit::WebCompositionCommandDiscard,
- -1, -1, -1, empty_string));
+ render_widget_host_->ImeCancelComposition();
ime_input_.ResetComposition(m_hWnd);
}
ime_input_.DestroyImeWindow(m_hWnd);
@@ -1282,9 +1280,13 @@ LRESULT RenderWidgetHostViewWin::OnMouseEvent(UINT message, WPARAM wparam,
switch (message) {
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ // Finish the ongoing composition whenever a mouse click happens.
+ // It matches IE's behavior.
+ ime_input_.CleanupComposition(m_hWnd);
+ // Fall through.
case WM_MOUSEMOVE:
- case WM_MOUSELEAVE:
- case WM_RBUTTONDOWN: {
+ case WM_MOUSELEAVE: {
// Give the TabContents first crack at the message. It may want to
// prevent forwarding to the renderer if some higher level browser
// functionality is invoked.
@@ -1303,12 +1305,6 @@ LRESULT RenderWidgetHostViewWin::OnMouseEvent(UINT message, WPARAM wparam,
return 1;
}
}
-
- // WebKit does not update its IME status when a user clicks a mouse button
- // to change the input focus onto a popup menu. As a workaround, we finish
- // an ongoing composition every time when we click a left button.
- if (message == WM_LBUTTONDOWN)
- ime_input_.CleanupComposition(m_hWnd);
}
ForwardMouseEventToRenderer(message, wparam, lparam);
diff --git a/chrome/browser/renderer_host/render_widget_host_view_win.h b/chrome/browser/renderer_host/render_widget_host_view_win.h
index fe524cd7..e0bc712 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_win.h
+++ b/chrome/browser/renderer_host/render_widget_host_view_win.h
@@ -135,7 +135,9 @@ class RenderWidgetHostViewWin
virtual gfx::Rect GetViewBounds() const;
virtual void UpdateCursor(const WebCursor& cursor);
virtual void SetIsLoading(bool is_loading);
- virtual void IMEUpdateStatus(int control, const gfx::Rect& caret_rect);
+ virtual void ImeUpdateTextInputState(WebKit::WebTextInputType type,
+ const gfx::Rect& caret_rect);
+ virtual void ImeCancelComposition();
virtual void DidUpdateBackingStore(
const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy,
const std::vector<gfx::Rect>& copy_rects);
@@ -336,6 +338,10 @@ class RenderWidgetHostViewWin
// Registrar so we can listen to RENDERER_PROCESS_TERMINATED events.
NotificationRegistrar registrar_;
+ // Stores the current text input type received by ImeUpdateTextInputState()
+ // method.
+ WebKit::WebTextInputType text_input_type_;
+
DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewWin);
};
diff --git a/chrome/browser/renderer_host/test/test_render_view_host.h b/chrome/browser/renderer_host/test/test_render_view_host.h
index bda5712..c3c5109 100644
--- a/chrome/browser/renderer_host/test/test_render_view_host.h
+++ b/chrome/browser/renderer_host/test/test_render_view_host.h
@@ -59,7 +59,9 @@ class TestRenderWidgetHostView : public RenderWidgetHostView {
virtual void SetIsLoading(bool is_loading) {}
virtual void UpdateCursor(const WebCursor& cursor) {}
virtual void UpdateCursorIfOverSelf() {}
- virtual void IMEUpdateStatus(int control, const gfx::Rect& caret_rect) {}
+ virtual void ImeUpdateTextInputState(WebKit::WebTextInputType state,
+ const gfx::Rect& caret_rect) {}
+ virtual void ImeCancelComposition() {}
virtual void DidUpdateBackingStore(
const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy,
const std::vector<gfx::Rect>& rects) {}
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 03b41b2..2c003f6 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -892,6 +892,7 @@
'browser/process_info_snapshot_mac_unittest.cc',
'browser/profile_manager_unittest.cc',
'browser/renderer_host/audio_renderer_host_unittest.cc',
+ 'browser/renderer_host/gtk_im_context_wrapper_unittest.cc',
'browser/renderer_host/render_widget_host_unittest.cc',
'browser/renderer_host/resource_dispatcher_host_unittest.cc',
'browser/renderer_host/resource_queue_unittest.cc',
diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h
index f5a3376..2b757e9 100644
--- a/chrome/common/render_messages.h
+++ b/chrome/common/render_messages.h
@@ -443,14 +443,6 @@ struct ViewHostMsg_DidPrintPage_Params {
double actual_shrink;
};
-// The first parameter for the ViewHostMsg_ImeUpdateStatus message.
-enum ViewHostMsg_ImeControl {
- IME_DISABLE = 0,
- IME_MOVE_WINDOWS,
- IME_COMPLETE_COMPOSITION,
- IME_CANCEL_COMPOSITION,
-};
-
// Parameters for creating an audio output stream.
struct ViewHostMsg_Audio_CreateStream_Params {
// Format request for the stream.
@@ -761,40 +753,6 @@ struct ParamTraits<ResourceType::Type> {
}
};
-template <>
-struct ParamTraits<ViewHostMsg_ImeControl> {
- typedef ViewHostMsg_ImeControl param_type;
- static void Write(Message* m, const param_type& p) {
- m->WriteInt(p);
- }
- static bool Read(const Message* m, void** iter, param_type* p) {
- int type;
- if (!m->ReadInt(iter, &type))
- return false;
- *p = static_cast<ViewHostMsg_ImeControl>(type);
- return true;
- }
- static void Log(const param_type& p, std::wstring* l) {
- std::wstring control;
- switch (p) {
- case IME_DISABLE:
- control = L"IME_DISABLE";
- break;
- case IME_MOVE_WINDOWS:
- control = L"IME_MOVE_WINDOWS";
- break;
- case IME_COMPLETE_COMPOSITION:
- control = L"IME_COMPLETE_COMPOSITION";
- break;
- default:
- control = L"UNKNOWN";
- break;
- }
-
- LogParam(control, l);
- }
-};
-
// Traits for ViewMsg_Navigate_Params structure to pack/unpack.
template <>
struct ParamTraits<ViewMsg_Navigate_Params> {
diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h
index df043e7..53c33a9 100644
--- a/chrome/common/render_messages_internal.h
+++ b/chrome/common/render_messages_internal.h
@@ -456,43 +456,30 @@ IPC_BEGIN_MESSAGES(View)
std::string /* property_name */,
std::string /* property_value_json */)
- // This message starts/stop monitoring the status of the focused edit
- // control of a renderer process.
+ // This message starts/stop monitoring the input method status of the focused
+ // edit control of a renderer process.
// Parameters
// * is_active (bool)
- // Represents whether or not the IME is active in a browser process.
+ // Indicates if an input method is active in the browser process.
// The possible actions when a renderer process receives this message are
// listed below:
// Value Action
- // true Start sending IPC messages, ViewHostMsg_ImeUpdateStatus
- // to notify the status of the focused edit control.
- // false Stop sending IPC messages, ViewHostMsg_ImeUpdateStatus.
- IPC_MESSAGE_ROUTED1(ViewMsg_ImeSetInputMode,
+ // true Start sending IPC message ViewHostMsg_ImeUpdateTextInputState
+ // to notify the input method status of the focused edit control.
+ // false Stop sending IPC message ViewHostMsg_ImeUpdateTextInputState.
+ IPC_MESSAGE_ROUTED1(ViewMsg_SetInputMethodActive,
bool /* is_active */)
- // This message sends a string being composed with IME.
- // Parameters
- // * string_type (int)
- // Represents the type of the 'ime_string' parameter.
- // Its possible values and description are listed below:
- // Value Description
- // -1 The parameter is not used.
- // 1 The parameter represents a result string.
- // 0 The parameter represents a composition string.
- // * cursor_position (int)
- // Represents the position of the cursor
- // * target_start (int)
- // Represents the position of the beginning of the selection
- // * target_end (int)
- // Represents the position of the end of the selection
- // * ime_string (std::wstring)
- // Represents the string retrieved from IME (Input Method Editor)
- IPC_MESSAGE_ROUTED5(ViewMsg_ImeSetComposition,
- WebKit::WebCompositionCommand, /* command */
- int, /* cursor_position */
- int, /* target_start */
- int, /* target_end */
- string16 /* ime_string */ )
+ // This message sends a string being composed with an input method.
+ IPC_MESSAGE_ROUTED4(
+ ViewMsg_ImeSetComposition,
+ string16, /* text */
+ std::vector<WebKit::WebCompositionUnderline>, /* underlines */
+ int, /* selectiont_start */
+ int /* selection_end */)
+
+ // This message confirms an ongoing composition.
+ IPC_MESSAGE_ROUTED0(ViewMsg_ImeConfirmComposition)
// This passes a set of webkit preferences down to the renderer.
IPC_MESSAGE_ROUTED1(ViewMsg_UpdateWebPreferences, WebPreferences)
@@ -1524,45 +1511,14 @@ IPC_BEGIN_MESSAGES(ViewHost)
GURL /* url of OS description document */,
bool /* autodetected */)
- // required for synchronizing IME windows.
- // Parameters
- // * control (ViewHostMsg_ImeControl)
- // It specifies the code for controlling the IME attached to
- // the browser process. This parameter should be one of the values
- // listed below.
- // + IME_DISABLE
- // Deactivate the IME attached to a browser process.
- // This code is typically used for notifying a renderer process
- // moves its input focus to a password input. A browser process
- // finishes the current composition and deactivate IME.
- // If a renderer process sets its input focus to another edit
- // control which is not a password input, it needs to re-activate
- // IME, it has to send another message with this code IME_MOVE_WINDOWS
- // and set the new caret position.
- // + IME_MOVE_WINDOWS
- // Activate the IME attached to a browser process and set the position
- // of its IME windows.
- // This code is typically used for the following cases:
- // - Notifying a renderer process moves the caret position of the
- // focused edit control, or;
- // - Notifying a renderer process moves its input focus from a
- // password input to an editable control which is NOT a password
- // input.
- // A renderer process also has to set caret_rect and
- // specify the new caret rectangle.
- // + IME_COMPLETE_COMPOSITION
- // Finish the current composition.
- // This code is used for notifying a renderer process moves its
- // input focus from an editable control being composed to another one
- // which is NOT a password input. A browser process closes its IME
- // windows without changing the activation status of its IME, i.e. it
- // keeps activating its IME.
- // * caret_rect (gfx::Rect)
- // They specify the rectangle of the input caret.
- IPC_MESSAGE_ROUTED2(ViewHostMsg_ImeUpdateStatus,
- ViewHostMsg_ImeControl, /* control */
+ // requires for updating text input state.
+ IPC_MESSAGE_ROUTED2(ViewHostMsg_ImeUpdateTextInputState,
+ WebKit::WebTextInputType, /* text_input_type */
gfx::Rect /* caret_rect */)
+ // requires for cancelling an ongoing input method composition.
+ IPC_MESSAGE_ROUTED0(ViewHostMsg_ImeCancelComposition)
+
// Tells the browser that the renderer is done calculating the number of
// rendered pages according to the specified settings.
IPC_MESSAGE_ROUTED2(ViewHostMsg_DidGetPrintedPagesCount,
diff --git a/chrome/common/webkit_param_traits.h b/chrome/common/webkit_param_traits.h
index 270c2f7..e277e38 100644
--- a/chrome/common/webkit_param_traits.h
+++ b/chrome/common/webkit_param_traits.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this
+// Copyright (c) 2010 The Chromium Authors. All rights reserved. Use of this
// source code is governed by a BSD-style license that can be found in the
// LICENSE file.
//
@@ -25,7 +25,7 @@
#include "ipc/ipc_message_utils.h"
#include "third_party/WebKit/WebKit/chromium/public/WebCache.h"
-#include "third_party/WebKit/WebKit/chromium/public/WebCompositionCommand.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCompositionUnderline.h"
#include "third_party/WebKit/WebKit/chromium/public/WebConsoleMessage.h"
#include "third_party/WebKit/WebKit/chromium/public/WebContextMenuData.h"
#include "third_party/WebKit/WebKit/chromium/public/WebDragOperation.h"
@@ -35,6 +35,7 @@
#include "third_party/WebKit/WebKit/chromium/public/WebPopupType.h"
#include "third_party/WebKit/WebKit/chromium/public/WebScreenInfo.h"
#include "third_party/WebKit/WebKit/chromium/public/WebTextDirection.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebTextInputType.h"
namespace IPC {
@@ -101,24 +102,6 @@ struct ParamTraits<WebKit::WebScreenInfo> {
};
template <>
-struct ParamTraits<WebKit::WebCompositionCommand> {
- typedef WebKit::WebCompositionCommand param_type;
- static void Write(Message* m, const param_type& p) {
- WriteParam(m, static_cast<int>(p));
- }
- static bool Read(const Message* m, void** iter, param_type* r) {
- int value;
- if (!ReadParam(m, iter, &value))
- return false;
- *r = static_cast<param_type>(value);
- return true;
- }
- static void Log(const param_type& p, std::wstring* l) {
- LogParam(static_cast<int>(p), l);
- }
-};
-
-template <>
struct ParamTraits<WebKit::WebConsoleMessage::Level> {
typedef WebKit::WebConsoleMessage::Level param_type;
static void Write(Message* m, const param_type& p) {
@@ -391,6 +374,69 @@ template <>
}
};
+template <>
+struct ParamTraits<WebKit::WebCompositionUnderline> {
+ typedef WebKit::WebCompositionUnderline param_type;
+ static void Write(Message* m, const param_type& p) {
+ WriteParam(m, p.startOffset);
+ WriteParam(m, p.endOffset);
+ WriteParam(m, p.color);
+ WriteParam(m, p.thick);
+ }
+ static bool Read(const Message* m, void** iter, param_type* p) {
+ return
+ ReadParam(m, iter, &p->startOffset) &&
+ ReadParam(m, iter, &p->endOffset) &&
+ ReadParam(m, iter, &p->color) &&
+ ReadParam(m, iter, &p->thick);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(L"(");
+ LogParam(p.startOffset, l);
+ l->append(L",");
+ LogParam(p.endOffset, l);
+ l->append(L":");
+ LogParam(p.color, l);
+ l->append(L":");
+ LogParam(p.thick, l);
+ l->append(L")");
+ }
+};
+
+template <>
+struct ParamTraits<WebKit::WebTextInputType> {
+ typedef WebKit::WebTextInputType param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteInt(p);
+ }
+ static bool Read(const Message* m, void** iter, param_type* p) {
+ int type;
+ if (!m->ReadInt(iter, &type))
+ return false;
+ *p = static_cast<param_type>(type);
+ return true;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ std::wstring control;
+ switch (p) {
+ case WebKit::WebTextInputTypeNone:
+ control = L"WebKit::WebTextInputTypeNone";
+ break;
+ case WebKit::WebTextInputTypeText:
+ control = L"WebKit::WebTextInputTypeText";
+ break;
+ case WebKit::WebTextInputTypePassword:
+ control = L"WebKit::WebTextInputTypePassword";
+ break;
+ default:
+ NOTIMPLEMENTED();
+ control = L"UNKNOWN";
+ break;
+ }
+ LogParam(control, l);
+ }
+};
+
} // namespace IPC
#endif // CHROME_COMMON_WEBKIT_PARAM_TRAITS_H_
diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc
index 19d9904..6b95e29 100644
--- a/chrome/renderer/render_view.cc
+++ b/chrome/renderer/render_view.cc
@@ -1760,16 +1760,6 @@ bool RenderView::isSelectTrailingWhitespaceEnabled() {
#endif
}
-void RenderView::setInputMethodEnabled(bool enabled) {
- // Save the updated IME status and mark the input focus has been updated.
- // The IME status is to be sent to a browser process next time when
- // the input caret is rendered.
- if (!ime_control_busy_) {
- ime_control_updated_ = true;
- ime_control_new_state_ = enabled;
- }
-}
-
void RenderView::didChangeSelection(bool is_empty_selection) {
#if defined(USE_X11)
if (!handling_input_event_)
diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h
index 1defef1..35db956 100644
--- a/chrome/renderer/render_view.h
+++ b/chrome/renderer/render_view.h
@@ -326,7 +326,6 @@ class RenderView : public RenderWidget,
virtual void didStopLoading();
virtual bool isSmartInsertDeleteEnabled();
virtual bool isSelectTrailingWhitespaceEnabled();
- virtual void setInputMethodEnabled(bool enabled);
virtual void didChangeSelection(bool is_selection_empty);
virtual void didExecuteCommand(const WebKit::WebString& command_name);
virtual bool handleCurrentKeyboardEvent();
diff --git a/chrome/renderer/render_view_unittest.cc b/chrome/renderer/render_view_unittest.cc
index b07f2b6..5f34637 100644
--- a/chrome/renderer/render_view_unittest.cc
+++ b/chrome/renderer/render_view_unittest.cc
@@ -24,7 +24,6 @@
#include "webkit/glue/form_data.h"
#include "webkit/glue/form_field.h"
-using WebKit::WebCompositionCommand;
using WebKit::WebDocument;
using WebKit::WebFrame;
using WebKit::WebInputElement;
@@ -34,19 +33,6 @@ using WebKit::WebURLError;
using webkit_glue::FormData;
using webkit_glue::FormField;
-static WebCompositionCommand ToCompositionCommand(int string_type) {
- switch (string_type) {
- default:
- NOTREACHED();
- case -1:
- return WebKit::WebCompositionCommandDiscard;
- case 0:
- return WebKit::WebCompositionCommandSet;
- case 1:
- return WebKit::WebCompositionCommandConfirm;
- }
-}
-
// Test that we get form state change notifications when input fields change.
TEST_F(RenderViewTest, OnNavStateChanged) {
// Don't want any delay for form state sync changes. This will still post a
@@ -73,7 +59,7 @@ TEST_F(RenderViewTest, OnNavStateChanged) {
// changes.
TEST_F(RenderViewTest, OnImeStateChanged) {
// Enable our IME backend code.
- view_->OnImeSetInputMode(true);
+ view_->OnSetInputMethodActive(true);
// Load an HTML page consisting of two input fields.
view_->set_send_content_state_immediately(true);
@@ -97,13 +83,13 @@ TEST_F(RenderViewTest, OnImeStateChanged) {
// Update the IME status and verify if our IME backend sends an IPC message
// to activate IMEs.
- view_->UpdateIME();
+ view_->UpdateInputMethod();
const IPC::Message* msg = render_thread_.sink().GetMessageAt(0);
EXPECT_TRUE(msg != NULL);
- EXPECT_EQ(ViewHostMsg_ImeUpdateStatus::ID, msg->type());
- ViewHostMsg_ImeUpdateStatus::Param params;
- ViewHostMsg_ImeUpdateStatus::Read(msg, &params);
- EXPECT_EQ(params.a, IME_COMPLETE_COMPOSITION);
+ EXPECT_EQ(ViewHostMsg_ImeUpdateTextInputState::ID, msg->type());
+ ViewHostMsg_ImeUpdateTextInputState::Param params;
+ ViewHostMsg_ImeUpdateTextInputState::Read(msg, &params);
+ EXPECT_EQ(params.a, WebKit::WebTextInputTypeText);
EXPECT_TRUE(params.b.x() > 0 && params.b.y() > 0);
// Move the input focus to the second <input> element, where we should
@@ -114,12 +100,12 @@ TEST_F(RenderViewTest, OnImeStateChanged) {
// Update the IME status and verify if our IME backend sends an IPC message
// to de-activate IMEs.
- view_->UpdateIME();
+ view_->UpdateInputMethod();
msg = render_thread_.sink().GetMessageAt(0);
EXPECT_TRUE(msg != NULL);
- EXPECT_EQ(ViewHostMsg_ImeUpdateStatus::ID, msg->type());
- ViewHostMsg_ImeUpdateStatus::Read(msg, &params);
- EXPECT_EQ(params.a, IME_DISABLE);
+ EXPECT_EQ(ViewHostMsg_ImeUpdateTextInputState::ID, msg->type());
+ ViewHostMsg_ImeUpdateTextInputState::Read(msg, &params);
+ EXPECT_EQ(params.a, WebKit::WebTextInputTypePassword);
}
}
@@ -140,60 +126,59 @@ TEST_F(RenderViewTest, ImeComposition) {
IME_SETINPUTMODE,
IME_SETFOCUS,
IME_SETCOMPOSITION,
+ IME_CONFIRMCOMPOSITION,
+ IME_CANCELCOMPOSITION
};
struct ImeMessage {
ImeCommand command;
bool enable;
- int string_type;
- int cursor_position;
- int target_start;
- int target_end;
+ int selection_start;
+ int selection_end;
const wchar_t* ime_string;
const wchar_t* result;
};
static const ImeMessage kImeMessages[] = {
// Scenario 1: input a Chinese word with Microsoft IME (on Vista).
- {IME_INITIALIZE, true, 0, 0, 0, 0, NULL, NULL},
- {IME_SETINPUTMODE, true, 0, 0, 0, 0, NULL, NULL},
- {IME_SETFOCUS, true, 0, 0, 0, 0, NULL, NULL},
- {IME_SETCOMPOSITION, false, 0, 1, -1, -1, L"n", L"n"},
- {IME_SETCOMPOSITION, false, 0, 2, -1, -1, L"ni", L"ni"},
- {IME_SETCOMPOSITION, false, 0, 3, -1, -1, L"nih", L"nih"},
- {IME_SETCOMPOSITION, false, 0, 4, -1, -1, L"niha", L"niha"},
- {IME_SETCOMPOSITION, false, 0, 5, -1, -1, L"nihao", L"nihao"},
- {IME_SETCOMPOSITION, false, 0, 2, -1, -1, L"\x4F60\x597D", L"\x4F60\x597D"},
- {IME_SETCOMPOSITION, false, 1, -1, -1, -1, L"\x4F60\x597D",
- L"\x4F60\x597D"},
- {IME_SETCOMPOSITION, false, -1, -1, -1, -1, L"", L"\x4F60\x597D"},
+ {IME_INITIALIZE, true, 0, 0, NULL, NULL},
+ {IME_SETINPUTMODE, true, 0, 0, NULL, NULL},
+ {IME_SETFOCUS, true, 0, 0, NULL, NULL},
+ {IME_SETCOMPOSITION, false, 1, 1, L"n", L"n"},
+ {IME_SETCOMPOSITION, false, 2, 2, L"ni", L"ni"},
+ {IME_SETCOMPOSITION, false, 3, 3, L"nih", L"nih"},
+ {IME_SETCOMPOSITION, false, 4, 4, L"niha", L"niha"},
+ {IME_SETCOMPOSITION, false, 5, 5, L"nihao", L"nihao"},
+ {IME_SETCOMPOSITION, false, 2, 2, L"\x4F60\x597D", L"\x4F60\x597D"},
+ {IME_CONFIRMCOMPOSITION, false, -1, -1, NULL, L"\x4F60\x597D"},
+ {IME_CANCELCOMPOSITION, false, -1, -1, L"", L"\x4F60\x597D"},
// Scenario 2: input a Japanese word with Microsoft IME (on Vista).
- {IME_INITIALIZE, true, 0, 0, 0, 0, NULL, NULL},
- {IME_SETINPUTMODE, true, 0, 0, 0, 0, NULL, NULL},
- {IME_SETFOCUS, true, 0, 0, 0, 0, NULL, NULL},
- {IME_SETCOMPOSITION, false, 0, 1, 0, 1, L"\xFF4B", L"\xFF4B"},
- {IME_SETCOMPOSITION, false, 0, 1, 0, 1, L"\x304B", L"\x304B"},
- {IME_SETCOMPOSITION, false, 0, 2, 0, 2, L"\x304B\xFF4E", L"\x304B\xFF4E"},
- {IME_SETCOMPOSITION, false, 0, 3, 0, 3, L"\x304B\x3093\xFF4A",
+ {IME_INITIALIZE, true, 0, 0, NULL, NULL},
+ {IME_SETINPUTMODE, true, 0, 0, NULL, NULL},
+ {IME_SETFOCUS, true, 0, 0, NULL, NULL},
+ {IME_SETCOMPOSITION, false, 0, 1, L"\xFF4B", L"\xFF4B"},
+ {IME_SETCOMPOSITION, false, 0, 1, L"\x304B", L"\x304B"},
+ {IME_SETCOMPOSITION, false, 0, 2, L"\x304B\xFF4E", L"\x304B\xFF4E"},
+ {IME_SETCOMPOSITION, false, 0, 3, L"\x304B\x3093\xFF4A",
L"\x304B\x3093\xFF4A"},
- {IME_SETCOMPOSITION, false, 0, 3, 0, 3, L"\x304B\x3093\x3058",
+ {IME_SETCOMPOSITION, false, 0, 3, L"\x304B\x3093\x3058",
L"\x304B\x3093\x3058"},
- {IME_SETCOMPOSITION, false, 0, 0, 0, 2, L"\x611F\x3058", L"\x611F\x3058"},
- {IME_SETCOMPOSITION, false, 0, 0, 0, 2, L"\x6F22\x5B57", L"\x6F22\x5B57"},
- {IME_SETCOMPOSITION, false, 1, -1, -1, -1, L"\x6F22\x5B57",
- L"\x6F22\x5B57"},
- {IME_SETCOMPOSITION, false, -1, -1, -1, -1, L"", L"\x6F22\x5B57"},
+ {IME_SETCOMPOSITION, false, 0, 2, L"\x611F\x3058", L"\x611F\x3058"},
+ {IME_SETCOMPOSITION, false, 0, 2, L"\x6F22\x5B57", L"\x6F22\x5B57"},
+ {IME_CONFIRMCOMPOSITION, false, -1, -1, NULL, L"\x6F22\x5B57"},
+ {IME_CANCELCOMPOSITION, false, -1, -1, L"", L"\x6F22\x5B57"},
// Scenario 3: input a Korean word with Microsot IME (on Vista).
- {IME_INITIALIZE, true, 0, 0, 0, 0, NULL, NULL},
- {IME_SETINPUTMODE, true, 0, 0, 0, 0, NULL, NULL},
- {IME_SETFOCUS, true, 0, 0, 0, 0, NULL, NULL},
- {IME_SETCOMPOSITION, false, 0, 0, 0, 1, L"\x3147", L"\x3147"},
- {IME_SETCOMPOSITION, false, 0, 0, 0, 1, L"\xC544", L"\xC544"},
- {IME_SETCOMPOSITION, false, 0, 0, 0, 1, L"\xC548", L"\xC548"},
- {IME_SETCOMPOSITION, false, 1, -1, -1, -1, L"\xC548", L"\xC548"},
- {IME_SETCOMPOSITION, false, 0, 0, 0, 1, L"\x3134", L"\xC548\x3134"},
- {IME_SETCOMPOSITION, false, 0, 0, 0, 1, L"\xB140", L"\xC548\xB140"},
- {IME_SETCOMPOSITION, false, 0, 0, 0, 1, L"\xB155", L"\xC548\xB155"},
- {IME_SETCOMPOSITION, false, -1, -1, -1, -1, L"", L"\xC548"},
- {IME_SETCOMPOSITION, false, 1, -1, -1, -1, L"\xB155", L"\xC548\xB155"},
+ {IME_INITIALIZE, true, 0, 0, NULL, NULL},
+ {IME_SETINPUTMODE, true, 0, 0, NULL, NULL},
+ {IME_SETFOCUS, true, 0, 0, NULL, NULL},
+ {IME_SETCOMPOSITION, false, 0, 1, L"\x3147", L"\x3147"},
+ {IME_SETCOMPOSITION, false, 0, 1, L"\xC544", L"\xC544"},
+ {IME_SETCOMPOSITION, false, 0, 1, L"\xC548", L"\xC548"},
+ {IME_CONFIRMCOMPOSITION, false, -1, -1, NULL, L"\xC548"},
+ {IME_SETCOMPOSITION, false, 0, 1, L"\x3134", L"\xC548\x3134"},
+ {IME_SETCOMPOSITION, false, 0, 1, L"\xB140", L"\xC548\xB140"},
+ {IME_SETCOMPOSITION, false, 0, 1, L"\xB155", L"\xC548\xB155"},
+ {IME_CANCELCOMPOSITION, false, -1, -1, L"", L"\xC548"},
+ {IME_SETCOMPOSITION, false, 0, 1, L"\xB155", L"\xC548\xB155"},
+ {IME_CONFIRMCOMPOSITION, false, -1, -1, NULL, L"\xC548\xB155"},
};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kImeMessages); i++) {
@@ -203,7 +188,7 @@ TEST_F(RenderViewTest, ImeComposition) {
// Load an HTML page consisting of a content-editable <div> element,
// and move the input focus to the <div> element, where we can use
// IMEs.
- view_->OnImeSetInputMode(ime_message->enable);
+ view_->OnSetInputMethodActive(ime_message->enable);
view_->set_send_content_state_immediately(true);
LoadHTML("<html>"
"<head>"
@@ -217,7 +202,7 @@ TEST_F(RenderViewTest, ImeComposition) {
case IME_SETINPUTMODE:
// Activate (or deactivate) our IME back-end.
- view_->OnImeSetInputMode(ime_message->enable);
+ view_->OnSetInputMethodActive(ime_message->enable);
break;
case IME_SETFOCUS:
@@ -227,17 +212,26 @@ TEST_F(RenderViewTest, ImeComposition) {
case IME_SETCOMPOSITION:
view_->OnImeSetComposition(
- ToCompositionCommand(ime_message->string_type),
- ime_message->cursor_position,
- ime_message->target_start,
- ime_message->target_end,
- WideToUTF16Hack(ime_message->ime_string));
+ WideToUTF16Hack(ime_message->ime_string),
+ std::vector<WebKit::WebCompositionUnderline>(),
+ ime_message->selection_start,
+ ime_message->selection_end);
+ break;
+
+ case IME_CONFIRMCOMPOSITION:
+ view_->OnImeConfirmComposition();
+ break;
+
+ case IME_CANCELCOMPOSITION:
+ view_->OnImeSetComposition(string16(),
+ std::vector<WebKit::WebCompositionUnderline>(),
+ 0, 0);
break;
}
// Update the status of our IME back-end.
// TODO(hbono): we should verify messages to be sent from the back-end.
- view_->UpdateIME();
+ view_->UpdateInputMethod();
ProcessPendingMessages();
render_thread_.sink().ClearMessages();
diff --git a/chrome/renderer/render_widget.cc b/chrome/renderer/render_widget.cc
index 37ea5a4..2366f78 100644
--- a/chrome/renderer/render_widget.cc
+++ b/chrome/renderer/render_widget.cc
@@ -34,7 +34,7 @@
#include "third_party/WebKit/WebKit/chromium/public/WebWidget.h"
-using WebKit::WebCompositionCommand;
+using WebKit::WebCompositionUnderline;
using WebKit::WebCursorInfo;
using WebKit::WebInputEvent;
using WebKit::WebNavigationPolicy;
@@ -44,6 +44,8 @@ using WebKit::WebRect;
using WebKit::WebScreenInfo;
using WebKit::WebSize;
using WebKit::WebTextDirection;
+using WebKit::WebTextInputType;
+using WebKit::WebVector;
RenderWidget::RenderWidget(RenderThreadBase* render_thread,
WebKit::WebPopupType popup_type)
@@ -61,13 +63,8 @@ RenderWidget::RenderWidget(RenderThreadBase* render_thread,
has_focus_(false),
handling_input_event_(false),
closing_(false),
- ime_is_active_(false),
- ime_control_enable_ime_(true),
- ime_control_x_(-1),
- ime_control_y_(-1),
- ime_control_new_state_(false),
- ime_control_updated_(false),
- ime_control_busy_(false),
+ input_method_is_active_(false),
+ text_input_type_(WebKit::WebTextInputTypeNone),
popup_type_(popup_type),
pending_window_rect_count_(0),
suppress_next_char_events_(false),
@@ -149,8 +146,9 @@ IPC_DEFINE_MESSAGE_MAP(RenderWidget)
IPC_MESSAGE_HANDLER(ViewMsg_HandleInputEvent, OnHandleInputEvent)
IPC_MESSAGE_HANDLER(ViewMsg_MouseCaptureLost, OnMouseCaptureLost)
IPC_MESSAGE_HANDLER(ViewMsg_SetFocus, OnSetFocus)
- IPC_MESSAGE_HANDLER(ViewMsg_ImeSetInputMode, OnImeSetInputMode)
+ IPC_MESSAGE_HANDLER(ViewMsg_SetInputMethodActive, OnSetInputMethodActive)
IPC_MESSAGE_HANDLER(ViewMsg_ImeSetComposition, OnImeSetComposition)
+ IPC_MESSAGE_HANDLER(ViewMsg_ImeConfirmComposition, OnImeConfirmComposition)
IPC_MESSAGE_HANDLER(ViewMsg_PaintAtSize, OnMsgPaintAtSize)
IPC_MESSAGE_HANDLER(ViewMsg_Repaint, OnMsgRepaint)
IPC_MESSAGE_HANDLER(ViewMsg_SetTextDirection, OnSetTextDirection)
@@ -359,13 +357,6 @@ void RenderWidget::OnSetFocus(bool enable) {
has_focus_ = enable;
if (webwidget_)
webwidget_->setFocus(enable);
- if (enable) {
- // Force to retrieve the state of the focused widget to determine if we
- // should activate IMEs next time when this process calls the UpdateIME()
- // function.
- ime_control_updated_ = true;
- ime_control_new_state_ = true;
- }
}
void RenderWidget::ClearFocus() {
@@ -531,7 +522,7 @@ void RenderWidget::DoDeferredUpdate() {
Send(new ViewHostMsg_UpdateRect(routing_id_, params));
next_paint_flags_ = 0;
- UpdateIME();
+ UpdateInputMethod();
// Let derived classes know we've painted.
DidInitiatePaint();
@@ -721,30 +712,32 @@ WebRect RenderWidget::windowResizerRect() {
return resizer_rect_;
}
-void RenderWidget::OnImeSetInputMode(bool is_active) {
+void RenderWidget::OnSetInputMethodActive(bool is_active) {
// To prevent this renderer process from sending unnecessary IPC messages to
// a browser process, we permit the renderer process to send IPC messages
- // only during the IME attached to the browser process is active.
- ime_is_active_ = is_active;
+ // only during the input method attached to the browser process is active.
+ input_method_is_active_ = is_active;
}
-void RenderWidget::OnImeSetComposition(WebCompositionCommand command,
- int cursor_position,
- int target_start, int target_end,
- const string16& ime_string) {
+void RenderWidget::OnImeSetComposition(
+ const string16& text,
+ const std::vector<WebCompositionUnderline>& underlines,
+ int selection_start, int selection_end) {
if (!webwidget_)
return;
- ime_control_busy_ = true;
- if (!webwidget_->handleCompositionEvent(command, cursor_position,
- target_start, target_end, ime_string) &&
- command == WebKit::WebCompositionCommandSet) {
- // If the composition event can't be handled while we were trying to update
- // the composition, let the browser process know so it can update it's
- // state.
- Send(new ViewHostMsg_ImeUpdateStatus(routing_id(), IME_CANCEL_COMPOSITION,
- WebRect()));
+ if (!webwidget_->setComposition(
+ text, WebVector<WebCompositionUnderline>(underlines),
+ selection_start, selection_end)) {
+ // 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
+ // sure we are in a consistent state.
+ Send(new ViewHostMsg_ImeCancelComposition(routing_id()));
}
- ime_control_busy_ = false;
+}
+
+void RenderWidget::OnImeConfirmComposition() {
+ if (webwidget_)
+ webwidget_->confirmComposition();
}
// This message causes the renderer to render an image of the
@@ -872,75 +865,26 @@ void RenderWidget::set_next_paint_is_repaint_ack() {
next_paint_flags_ |= ViewHostMsg_UpdateRect_Flags::IS_REPAINT_ACK;
}
-void RenderWidget::UpdateIME() {
- // If a browser process does not have IMEs, its IMEs are not active, or there
- // are not any attached widgets.
- // a renderer process does not have to retrieve information of the focused
- // control or send notification messages to a browser process.
- if (!ime_is_active_) {
+void RenderWidget::UpdateInputMethod() {
+ if (!input_method_is_active_)
return;
+
+ WebTextInputType new_type = WebKit::WebTextInputTypeNone;
+ WebRect new_caret_bounds;
+
+ if (webwidget_) {
+ new_type = webwidget_->textInputType();
+ new_caret_bounds = webwidget_->caretOrSelectionBounds();
}
- // Retrieve the caret position from the focused widget and verify we should
- // enabled IMEs attached to the browser process.
- bool enable_ime = false;
- WebRect caret_rect;
- if (!webwidget_ ||
- !webwidget_->queryCompositionStatus(&enable_ime, &caret_rect)) {
- // There are not any editable widgets attached to this process.
- // We should disable the IME to prevent it from sending CJK strings to
- // non-editable widgets.
- ime_control_updated_ = true;
- ime_control_new_state_ = false;
- }
- if (ime_control_new_state_ != enable_ime) {
- ime_control_updated_ = true;
- ime_control_new_state_ = enable_ime;
- }
- if (ime_control_updated_) {
- // The input focus has been changed.
- // Compare the current state with the updated state and choose actions.
- if (ime_control_enable_ime_) {
- if (ime_control_new_state_) {
- // Case 1: a text input -> another text input
- // Complete the current composition and notify the caret position.
- Send(new ViewHostMsg_ImeUpdateStatus(routing_id(),
- IME_COMPLETE_COMPOSITION,
- caret_rect));
- } else {
- // Case 2: a text input -> a password input (or a static control)
- // Complete the current composition and disable the IME.
- Send(new ViewHostMsg_ImeUpdateStatus(routing_id(), IME_DISABLE,
- caret_rect));
- }
- } else {
- if (ime_control_new_state_) {
- // Case 3: a password input (or a static control) -> a text input
- // Enable the IME and notify the caret position.
- Send(new ViewHostMsg_ImeUpdateStatus(routing_id(),
- IME_COMPLETE_COMPOSITION,
- caret_rect));
- } else {
- // Case 4: a password input (or a static contol) -> another password
- // input (or another static control).
- // The IME has been already disabled and we don't have to do anything.
- }
- }
- } else {
- // The input focus is not changed.
- // Notify the caret position to a browser process only if it is changed.
- if (ime_control_enable_ime_) {
- if (caret_rect.x != ime_control_x_ ||
- caret_rect.y != ime_control_y_) {
- Send(new ViewHostMsg_ImeUpdateStatus(routing_id(), IME_MOVE_WINDOWS,
- caret_rect));
- }
- }
+
+ // Only sends text input type and caret bounds to the browser process if they
+ // are changed.
+ if (text_input_type_ != new_type || caret_bounds_ != new_caret_bounds) {
+ text_input_type_ = new_type;
+ caret_bounds_ = new_caret_bounds;
+ Send(new ViewHostMsg_ImeUpdateTextInputState(
+ routing_id(), new_type, new_caret_bounds));
}
- // Save the updated IME status to prevent from sending the same IPC messages.
- ime_control_updated_ = false;
- ime_control_enable_ime_ = ime_control_new_state_;
- ime_control_x_ = caret_rect.x;
- ime_control_y_ = caret_rect.y;
}
WebScreenInfo RenderWidget::screenInfo() {
@@ -949,6 +893,20 @@ WebScreenInfo RenderWidget::screenInfo() {
return results;
}
+void RenderWidget::resetInputMethod() {
+ if (!input_method_is_active_)
+ return;
+
+ // If the last text input type is not None, then we should finish any
+ // ongoing composition regardless of the new text input type.
+ if (text_input_type_ != WebKit::WebTextInputTypeNone) {
+ // If a composition text exists, then we need to let the browser process
+ // to cancel the input method's ongoing composition session.
+ if (webwidget_->confirmComposition())
+ Send(new ViewHostMsg_ImeCancelComposition(routing_id()));
+ }
+}
+
void RenderWidget::SchedulePluginMove(
const webkit_glue::WebPluginGeometry& move) {
size_t i = 0;
diff --git a/chrome/renderer/render_widget.h b/chrome/renderer/render_widget.h
index d2c8347..d602fec4 100644
--- a/chrome/renderer/render_widget.h
+++ b/chrome/renderer/render_widget.h
@@ -19,10 +19,11 @@
#include "gfx/size.h"
#include "ipc/ipc_channel.h"
#include "skia/ext/platform_canvas.h"
-#include "third_party/WebKit/WebKit/chromium/public/WebCompositionCommand.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCompositionUnderline.h"
#include "third_party/WebKit/WebKit/chromium/public/WebPopupType.h"
#include "third_party/WebKit/WebKit/chromium/public/WebRect.h"
#include "third_party/WebKit/WebKit/chromium/public/WebTextDirection.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebTextInputType.h"
#include "third_party/WebKit/WebKit/chromium/public/WebWidgetClient.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "webkit/glue/webcursor.h"
@@ -92,6 +93,7 @@ class RenderWidget : public IPC::Channel::Listener,
virtual WebKit::WebRect windowResizerRect();
virtual WebKit::WebRect rootWindowRect();
virtual WebKit::WebScreenInfo screenInfo();
+ virtual void resetInputMethod();
// Called when a plugin is moved. These events are queued up and sent with
// the next paint or scroll message to the host.
@@ -156,11 +158,13 @@ class RenderWidget : public IPC::Channel::Listener,
void OnHandleInputEvent(const IPC::Message& message);
void OnMouseCaptureLost();
virtual void OnSetFocus(bool enable);
- void OnImeSetInputMode(bool is_active);
- void OnImeSetComposition(WebKit::WebCompositionCommand command,
- int cursor_position,
- int target_start, int target_end,
- const string16& ime_string);
+ void OnSetInputMethodActive(bool is_active);
+ void OnImeSetComposition(
+ const string16& text,
+ const std::vector<WebKit::WebCompositionUnderline>& underlines,
+ int selection_start,
+ int selection_end);
+ void OnImeConfirmComposition();
void OnMsgPaintAtSize(const TransportDIB::Handle& dib_id,
const gfx::Size& page_size,
const gfx::Size& desired_size);
@@ -192,12 +196,9 @@ class RenderWidget : public IPC::Channel::Listener,
void set_next_paint_is_restore_ack();
void set_next_paint_is_repaint_ack();
- // Called when a renderer process moves an input focus or updates the
- // position of its caret.
- // This function compares them with the previous values, and send them to
- // the browser process only if they are updated.
- // The browser process moves IME windows and context.
- void UpdateIME();
+ // Checks if the input method state and caret position have been changed.
+ // If they are changed, the new value will be sent to the browser process.
+ void UpdateInputMethod();
// Tells the renderer it does not have focus. Used to prevent us from getting
// the focus on our own when the browser did not focus us.
@@ -288,24 +289,14 @@ class RenderWidget : public IPC::Channel::Listener,
// be sent, except for a Close.
bool closing_;
- // Represents whether or not the IME of a browser process is active.
- bool ime_is_active_;
-
- // Represents the status of the selected edit control sent to a browser
- // process last time.
- // When a renderer process finishes rendering a region, it retrieves:
- // * The identifier of the selected edit control;
- // * Whether or not the selected edit control requires IME, and;
- // * The position of the caret (or cursor).
- // If the above values is updated, a renderer process sends an IPC message
- // to a browser process. A browser process uses these values to
- // activate/deactivate IME and set the position of IME windows.
- bool ime_control_enable_ime_;
- int ime_control_x_;
- int ime_control_y_;
- bool ime_control_new_state_;
- bool ime_control_updated_;
- bool ime_control_busy_;
+ // Indicates if an input method is active in the browser process.
+ bool input_method_is_active_;
+
+ // Stores the current text input type of |webwidget_|.
+ WebKit::WebTextInputType text_input_type_;
+
+ // Stores the current caret bounds of input focus.
+ WebKit::WebRect caret_bounds_;
// The kind of popup this widget represents, NONE if not a popup.
WebKit::WebPopupType popup_type_;