Home | History | Annotate | Download | only in renderer_host
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef CONTENT_BROWSER_RENDERER_HOST_GTK_IM_CONTEXT_WRAPPER_H_
      6 #define CONTENT_BROWSER_RENDERER_HOST_GTK_IM_CONTEXT_WRAPPER_H_
      7 
      8 #include <gdk/gdk.h>
      9 #include <pango/pango-attributes.h>
     10 #include <vector>
     11 
     12 #include "base/basictypes.h"
     13 #include "base/gtest_prod_util.h"
     14 #include "base/strings/string16.h"
     15 #include "third_party/WebKit/public/web/WebInputEvent.h"
     16 #include "ui/base/ime/composition_text.h"
     17 #include "ui/base/ime/text_input_type.h"
     18 
     19 typedef struct _GtkIMContext GtkIMContext;
     20 typedef struct _GtkWidget GtkWidget;
     21 
     22 namespace gfx {
     23 class Rect;
     24 }
     25 
     26 namespace content {
     27 class RenderWidgetHostViewGtk;
     28 struct NativeWebKeyboardEvent;
     29 
     30 // This class is a convenience wrapper for GtkIMContext.
     31 // It creates and manages two GtkIMContext instances, one is GtkIMMulticontext,
     32 // for plain text input box, another is GtkIMContextSimple, for password input
     33 // box.
     34 //
     35 // This class is in charge of dispatching key events to these two GtkIMContext
     36 // instances and handling signals emitted by them. Key events then will be
     37 // forwarded to renderer along with input method results via corresponding host
     38 // view.
     39 //
     40 // This class is used solely by RenderWidgetHostViewGtk.
     41 class GtkIMContextWrapper {
     42  public:
     43   explicit GtkIMContextWrapper(RenderWidgetHostViewGtk* host_view);
     44   ~GtkIMContextWrapper();
     45 
     46   // Processes a gdk key event received by |host_view|.
     47   void ProcessKeyEvent(GdkEventKey* event);
     48 
     49   void UpdateInputMethodState(ui::TextInputType type,
     50                               bool can_compose_inline);
     51   void UpdateCaretBounds(const gfx::Rect& caret_bounds);
     52   void OnFocusIn();
     53   void OnFocusOut();
     54   bool is_focused() const { return is_focused_; }
     55 
     56   GtkWidget* BuildInputMethodsGtkMenu();
     57 
     58   void CancelComposition();
     59 
     60   void ConfirmComposition();
     61 
     62  private:
     63   // Check if a text needs commit by forwarding a char event instead of
     64   // by confirming as a composition text.
     65   bool NeedCommitByForwardingCharEvent() const;
     66 
     67   // Check if the input method returned any result, eg. preedit and commit text.
     68   bool HasInputMethodResult() const;
     69 
     70   void ProcessFilteredKeyPressEvent(NativeWebKeyboardEvent* wke);
     71   void ProcessUnfilteredKeyPressEvent(NativeWebKeyboardEvent* wke);
     72 
     73   // Processes result returned from input method after filtering a key event.
     74   // |filtered| indicates if the key event was filtered by the input method.
     75   void ProcessInputMethodResult(const GdkEventKey* event, bool filtered);
     76 
     77   // Real code of "commit" signal handler.
     78   void HandleCommit(const string16& text);
     79 
     80   // Real code of "preedit-start" signal handler.
     81   void HandlePreeditStart();
     82 
     83   // Real code of "preedit-changed" signal handler.
     84   void HandlePreeditChanged(const gchar* text,
     85                             PangoAttrList* attrs,
     86                             int cursor_position);
     87 
     88   // Real code of "preedit-end" signal handler.
     89   void HandlePreeditEnd();
     90 
     91   // Real code of "retrieve-surrounding" signal handler.
     92   gboolean HandleRetrieveSurrounding(GtkIMContext* context);
     93 
     94   // Real code of "realize" signal handler, used for setting im context's client
     95   // window.
     96   void HandleHostViewRealize(GtkWidget* widget);
     97 
     98   // Real code of "unrealize" signal handler, used for unsetting im context's
     99   // client window.
    100   void HandleHostViewUnrealize();
    101 
    102   // Sends a fake composition key event with specified event type. A composition
    103   // key event is a key event with special key code 229.
    104   void SendFakeCompositionKeyEvent(WebKit::WebInputEvent::Type type);
    105 
    106   // Signal handlers of GtkIMContext object.
    107   static void HandleCommitThunk(GtkIMContext* context, gchar* text,
    108                                 GtkIMContextWrapper* self);
    109   static void HandlePreeditStartThunk(GtkIMContext* context,
    110                                       GtkIMContextWrapper* self);
    111   static void HandlePreeditChangedThunk(GtkIMContext* context,
    112                                         GtkIMContextWrapper* self);
    113   static void HandlePreeditEndThunk(GtkIMContext* context,
    114                                     GtkIMContextWrapper* self);
    115   static gboolean HandleRetrieveSurroundingThunk(GtkIMContext* context,
    116                                                  GtkIMContextWrapper* self);
    117 
    118   // Signal handlers connecting to |host_view_|'s native view widget.
    119   static void HandleHostViewRealizeThunk(GtkWidget* widget,
    120                                          GtkIMContextWrapper* self);
    121   static void HandleHostViewUnrealizeThunk(GtkWidget* widget,
    122                                            GtkIMContextWrapper* self);
    123 
    124   // The parent object.
    125   RenderWidgetHostViewGtk* host_view_;
    126 
    127   // The GtkIMContext object.
    128   // In terms of the DOM event specification Appendix A
    129   //   <http://www.w3.org/TR/DOM-Level-3-Events/keyset.html>,
    130   // GTK uses a GtkIMContext object for the following two purposes:
    131   //  1. Composing Latin characters (A.1.2), and;
    132   //  2. Composing CJK characters with an IME (A.1.3).
    133   // Many JavaScript pages assume composed Latin characters are dispatched to
    134   // their onkeypress() handlers but not dispatched CJK characters composed
    135   // with an IME. To emulate this behavior, we should monitor the status of
    136   // this GtkIMContext object and prevent sending Char events when a
    137   // GtkIMContext object sends a "commit" signal with the CJK characters
    138   // composed by an IME.
    139   GtkIMContext* context_;
    140 
    141   // A GtkIMContextSimple object, for supporting dead/compose keys when input
    142   // method is disabled, eg. in password input box.
    143   GtkIMContext* context_simple_;
    144 
    145   // Whether or not this widget is focused.
    146   bool is_focused_;
    147 
    148   // Whether or not the above GtkIMContext is composing a text with an IME.
    149   // This flag is used in "commit" signal handler of the GtkIMContext object,
    150   // which determines how to submit the result text to WebKit according to this
    151   // flag.
    152   // If this flag is true or there are more than one characters in the result,
    153   // then the result text will be committed to WebKit as a confirmed
    154   // composition. Otherwise, it'll be forwarded as a key event.
    155   //
    156   // The GtkIMContext object sends a "preedit_start" before it starts composing
    157   // a text and a "preedit_end" signal after it finishes composing it.
    158   // "preedit_start" signal is monitored to turn it on.
    159   // We don't monitor "preedit_end" signal to turn it off, because an input
    160   // method may fire "preedit_end" signal before "commit" signal.
    161   // A buggy input method may not fire "preedit_start" and/or "preedit_end"
    162   // at all, so this flag will also be set to true when "preedit_changed" signal
    163   // is fired with non-empty preedit text.
    164   bool is_composing_text_;
    165 
    166   // Whether or not the IME is enabled.
    167   bool is_enabled_;
    168 
    169   // Whether or not it's currently running inside key event handler.
    170   // If it's true, then preedit-changed and commit handler will backup the
    171   // preedit or commit text instead of sending them down to webkit.
    172   // key event handler will send them later.
    173   bool is_in_key_event_handler_;
    174 
    175   // The most recent composition text information retrieved from context_;
    176   ui::CompositionText composition_;
    177 
    178   // Whether or not the composition has been changed since last key event.
    179   bool is_composition_changed_;
    180 
    181   // Stores a copy of the most recent commit text received by commit signal
    182   // handler.
    183   string16 commit_text_;
    184 
    185   // If it's true then the next "commit" signal will be suppressed.
    186   // It's only used to workaround http://crbug.com/50485.
    187   // TODO(suzhe): Remove it after input methods get fixed.
    188   bool suppress_next_commit_;
    189 
    190   // Information of the last key event, for working around
    191   // http://crosbug.com/6582
    192   int last_key_code_;
    193   bool last_key_was_up_;
    194   bool last_key_filtered_no_result_;
    195 
    196   DISALLOW_COPY_AND_ASSIGN(GtkIMContextWrapper);
    197 };
    198 
    199 }  // namespace content
    200 
    201 #endif  // CONTENT_BROWSER_RENDERER_HOST_GTK_IM_CONTEXT_WRAPPER_H_
    202