| Index: chrome/browser/renderer_host/render_widget_host_view_gtk.cc | 
| =================================================================== | 
| --- chrome/browser/renderer_host/render_widget_host_view_gtk.cc	(revision 19224) | 
| +++ chrome/browser/renderer_host/render_widget_host_view_gtk.cc	(working copy) | 
| @@ -71,6 +71,17 @@ | 
| g_signal_connect(widget, "scroll-event", | 
| G_CALLBACK(MouseScrollEvent), host_view); | 
|  | 
| +    // Create a GtkIMContext instance and attach its signal handlers. | 
| +    host_view->im_context_ = gtk_im_multicontext_new(); | 
| +    g_signal_connect(host_view->im_context_, "preedit_start", | 
| +                     G_CALLBACK(InputMethodPreeditStart), host_view); | 
| +    g_signal_connect(host_view->im_context_, "preedit_end", | 
| +                     G_CALLBACK(InputMethodPreeditEnd), host_view); | 
| +    g_signal_connect(host_view->im_context_, "preedit_changed", | 
| +                     G_CALLBACK(InputMethodPreeditChanged), host_view); | 
| +    g_signal_connect(host_view->im_context_, "commit", | 
| +                     G_CALLBACK(InputMethodCommit), host_view); | 
| + | 
| GtkTargetList* target_list = gtk_target_list_new(NULL, 0); | 
| gtk_target_list_add_text_targets(target_list, 0); | 
| gint num_targets = 0; | 
| @@ -109,6 +120,37 @@ | 
| NativeWebKeyboardEvent wke(event); | 
| host_view->GetRenderWidgetHost()->ForwardKeyboardEvent(wke); | 
| } | 
| + | 
| +    // Dispatch this event to the GtkIMContext object. | 
| +    // It sends a "commit" signal when it has a character to be inserted | 
| +    // even when we use a US keyboard so that we can send a Char event | 
| +    // (or an IME event) to the renderer in our "commit"-signal handler. | 
| +    // We should send a KeyDown (or a KeyUp) event before dispatching this | 
| +    // event to the GtkIMContext object (and send a Char event) so that WebKit | 
| +    // can dispatch the JavaScript events in the following order: onkeydown(), | 
| +    // onkeypress(), and onkeyup(). (Many JavaScript pages assume this.) | 
| +    // TODO(hbono): we should not dispatch a key event when the input focus | 
| +    // is in a password input? | 
| +    if (!gtk_im_context_filter_keypress(host_view->im_context_, event)) { | 
| +      // The GtkIMContext object cannot handle this key event. | 
| +      // This case is caused by two reasons: | 
| +      // 1. The given key event is a control-key event, (e.g. return, page up, | 
| +      //    page down, tab, arrows, etc.) or; | 
| +      // 2. The given key event is not a control-key event but printable | 
| +      //    characters aren't assigned to the event, (e.g. alt+d, etc.) | 
| +      // Create a Char event manually from this key event and send it to the | 
| +      // renderer only when this event is a control-key event because | 
| +      // control-key events should be processed by WebKit. | 
| +      // TODO(hbono): Windows Chrome sends a Char event with its isSystemKey | 
| +      // value true for the above case 2. We should emulate this behavior? | 
| +      if (event->type == GDK_KEY_PRESS && | 
| +          !gdk_keyval_to_unicode(event->keyval)) { | 
| +        NativeWebKeyboardEvent wke(event); | 
| +        wke.type = WebKit::WebInputEvent::Char; | 
| +        host_view->GetRenderWidgetHost()->ForwardKeyboardEvent(wke); | 
| +      } | 
| +    } | 
| + | 
| // We return TRUE because we did handle the event. If it turns out webkit | 
| // can't handle the event, we'll deal with it in | 
| // RenderView::UnhandledKeyboardEvent(). | 
| @@ -215,6 +257,68 @@ | 
| return FALSE; | 
| } | 
|  | 
| +  static void InputMethodCommit(GtkIMContext* im_context, | 
| +                                gchar* text, | 
| +                                RenderWidgetHostViewGtk* host_view) { | 
| +    std::wstring im_text = UTF8ToWide(text); | 
| +    if (!host_view->im_is_composing_cjk_text_ && im_text.length() == 1) { | 
| +      // Send a Char event when we input a composed character without IMEs so | 
| +      // that this event is to be dispatched to onkeypress() handlers, | 
| +      // autofill, etc. | 
| +      ForwardCharEvent(host_view, im_text[0]); | 
| +    } else { | 
| +      // Send an IME event. | 
| +      // Unlike a Char event, an IME event is NOT dispatched to onkeypress() | 
| +      // handlers or autofill. | 
| +      host_view->GetRenderWidgetHost()->ImeConfirmComposition(im_text); | 
| +    } | 
| +  } | 
| + | 
| +  static void InputMethodPreeditStart(GtkIMContext* im_context, | 
| +                                      RenderWidgetHostViewGtk* host_view) { | 
| +    // Start monitoring IME events of the renderer. | 
| +    // TODO(hbono): a renderer sends these IME events not only for sending the | 
| +    // caret position, but also for enabling/disabling IMEs. If we need to | 
| +    // enable/disable IMEs, we should move this code to a better place. | 
| +    // (This signal handler is called only when an IME is enabled. So, once | 
| +    // we disable an IME, we cannot receive any IME events from the renderer, | 
| +    // i.e. we cannot re-enable the IME any longer.) | 
| +    host_view->GetRenderWidgetHost()->ImeSetInputMode(true); | 
| +    host_view->im_is_composing_cjk_text_ = true; | 
| +  } | 
| + | 
| +  static void InputMethodPreeditEnd(GtkIMContext* im_context, | 
| +                                    RenderWidgetHostViewGtk* host_view) { | 
| +    // End monitoring IME events. | 
| +    host_view->GetRenderWidgetHost()->ImeSetInputMode(false); | 
| +    host_view->im_is_composing_cjk_text_ = false; | 
| +  } | 
| + | 
| +  static void InputMethodPreeditChanged(GtkIMContext* im_context, | 
| +                                        RenderWidgetHostViewGtk* host_view) { | 
| +    // Send an IME event to update the composition node of the renderer. | 
| +    // TODO(hbono): an IME intercepts all key events while composing a text, | 
| +    // i.e. we cannot receive any GDK_KEY_PRESS (or GDK_KEY_UP) events. | 
| +    // Should we send pseudo KeyDown (and KeyUp) events to emulate Windows? | 
| +    gchar* preedit_text = NULL; | 
| +    gint cursor_position = 0; | 
| +    gtk_im_context_get_preedit_string(im_context, &preedit_text, NULL, | 
| +                                      &cursor_position); | 
| +    host_view->GetRenderWidgetHost()->ImeSetComposition( | 
| +        UTF8ToWide(preedit_text), cursor_position, -1, -1); | 
| +    g_free(preedit_text); | 
| +  } | 
| + | 
| +  static void ForwardCharEvent(RenderWidgetHostViewGtk* host_view, | 
| +                               wchar_t im_character) { | 
| +    if (!im_character) | 
| +      return; | 
| + | 
| +    NativeWebKeyboardEvent char_event(im_character, | 
| +                                      base::Time::Now().ToDoubleT()); | 
| +    host_view->GetRenderWidgetHost()->ForwardKeyboardEvent(char_event); | 
| +  } | 
| + | 
| DISALLOW_IMPLICIT_CONSTRUCTORS(RenderWidgetHostViewGtkWidget); | 
| }; | 
|  | 
| @@ -232,11 +336,15 @@ | 
| is_showing_context_menu_(false), | 
| parent_host_view_(NULL), | 
| parent_(NULL), | 
| -      is_popup_first_mouse_release_(true) { | 
| +      is_popup_first_mouse_release_(true), | 
| +      im_context_(NULL), | 
| +      im_is_composing_cjk_text_(false) { | 
| host_->set_view(this); | 
| } | 
|  | 
| RenderWidgetHostViewGtk::~RenderWidgetHostViewGtk() { | 
| +  if (im_context_) | 
| +    g_object_unref(im_context_); | 
| view_.Destroy(); | 
| } | 
|  | 
| @@ -396,7 +504,32 @@ | 
|  | 
| void RenderWidgetHostViewGtk::IMEUpdateStatus(int control, | 
| const gfx::Rect& caret_rect) { | 
| -  NOTIMPLEMENTED(); | 
| +  // The renderer has updated its IME status. | 
| +  // Control the GtkIMContext object according to this status. | 
| +  if (!im_context_) | 
| +    return; | 
| + | 
| +  if (control == IME_DISABLE) { | 
| +    // TODO(hbono): this code just resets the GtkIMContext object and | 
| +    // detaches it from this window. Should we prevent sending key events to | 
| +    // the GtkIMContext object (or unref it) when we disable IMEs? | 
| +    gtk_im_context_reset(im_context_); | 
| +    gtk_im_context_set_client_window(im_context_, NULL); | 
| +    gtk_im_context_set_cursor_location(im_context_, NULL); | 
| +  } else { | 
| +    // TODO(hbono): we should finish (not reset) an ongoing composition | 
| +    // when |control| is IME_COMPLETE_COMPOSITION. | 
| + | 
| +    // Attach the GtkIMContext object to this window. | 
| +    gtk_im_context_set_client_window(im_context_, view_.get()->window); | 
| + | 
| +    // 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 | 
| +    // position. | 
| +    GdkRectangle cursor_rect(caret_rect.ToGdkRectangle()); | 
| +    gtk_im_context_set_cursor_location(im_context_, &cursor_rect); | 
| +  } | 
| } | 
|  | 
| void RenderWidgetHostViewGtk::DidPaintRect(const gfx::Rect& rect) { | 
|  |