Enable IME support for Ozone Efl 30/285730/5 tizen.dev_m108
authorGajendra N <gajendra.n@samsung.com>
Mon, 19 Dec 2022 05:25:04 +0000 (10:55 +0530)
committerGajendra N <gajendra.n@samsung.com>
Tue, 20 Dec 2022 09:50:20 +0000 (15:20 +0530)
Functionalities:
1) IME show/hide panel on text input client's focus/blur respectively
2) IME focus/unfocus based on aura window's focus state
3) IME key filtering for HW KB input
4) Migrates text composition related base code

Change-Id: I9ad84780c70ac6a9aedb6d52e9d1cd1dce5db3a3
Signed-off-by: Gajendra N <gajendra.n@samsung.com>
13 files changed:
content/browser/web_contents/web_contents_impl.h
tizen_src/chromium_impl/content/browser/renderer_host/rwhv_aura_offscreen_helper_efl.cc
tizen_src/chromium_impl/content/browser/renderer_host/rwhv_aura_offscreen_helper_efl.h
tizen_src/chromium_impl/ui/ozone/platform/efl/BUILD.gn
tizen_src/chromium_impl/ui/ozone/platform/efl/efl_event_handler.cc
tizen_src/chromium_impl/ui/ozone/platform/efl/efl_event_handler.h
tizen_src/chromium_impl/ui/ozone/platform/efl/efl_input_method_context.cc [new file with mode: 0644]
tizen_src/chromium_impl/ui/ozone/platform/efl/efl_input_method_context.h [new file with mode: 0644]
tizen_src/chromium_impl/ui/ozone/platform/efl/im_context_efl.cc [new file with mode: 0644]
tizen_src/chromium_impl/ui/ozone/platform/efl/im_context_efl.h [new file with mode: 0644]
tizen_src/ewk/efl_integration/eweb_view.cc
ui/base/ime/linux/input_method_auralinux.cc
ui/events/event.h

index 738061d..f601711 100644 (file)
@@ -226,6 +226,8 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents,
 #if defined(USE_EFL)
   void CreateEflNativeView();
   Evas_Object* GetEflNativeView() const { return efl_native_view_; }
+  void set_ewk_view(void* ewk_view) { ewk_view_ = ewk_view; }
+  void* ewk_view() const { return ewk_view_; }
 #endif
 
   // Returns the SavePackage which manages the page saving job. May be NULL.
@@ -2386,6 +2388,7 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents,
 
 #if defined(USE_EFL)
   Evas_Object* efl_native_view_ = nullptr;
+  void* ewk_view_ = nullptr;
 #endif
 
   base::WeakPtrFactory<WebContentsImpl> loading_weak_factory_{this};
index 1f7a5e7..4ea4a0e 100644 (file)
@@ -24,6 +24,8 @@
 #include "ui/gfx/skbitmap_operations.h"
 #include "ui/gl/gl_share_group.h"
 #include "ui/gl/gl_shared_context_efl.h"
+#include "ui/ozone/platform/efl/efl_event_handler.h"
+#include "ui/ozone/platform/efl/im_context_efl.h"
 #include "ui/platform_window/platform_window.h"
 
 #define MAX_SURFACE_WIDTH_EGL 4096   // max supported Framebuffer width
@@ -442,10 +444,8 @@ void RWHVAuraOffscreenHelperEfl::OnFocusOut(void* data,
 
   aura::Window* focused_window =
       aura::client::GetFocusClient(window_host->window())->GetFocusedWindow();
-#if 0
   if (focused_window)
     focused_window->LostFocus();
-#endif
 }
 
 void RWHVAuraOffscreenHelperEfl::OnHostFocusIn(void* data,
@@ -486,6 +486,14 @@ void RWHVAuraOffscreenHelperEfl::EvasToBlinkCords(int x,
   }
 }
 
+Evas_Object* RWHVAuraOffscreenHelperEfl::ewk_view() const {
+  auto wci = static_cast<WebContentsImpl*>(web_contents_);
+  if (!wci)
+    return nullptr;
+
+  return static_cast<Evas_Object*>(wci->ewk_view());
+}
+
 void RWHVAuraOffscreenHelperEfl::FocusRWHVA() {
   if (!rwhv_aura_->HasFocus())
     rwhv_aura_->Focus();
@@ -533,6 +541,28 @@ RenderWidgetHostImpl* RWHVAuraOffscreenHelperEfl::GetRenderWidgetHostImpl() {
   return rwhv_aura_->host();
 }
 
+ui::EflEventHandler* RWHVAuraOffscreenHelperEfl::GetEventHandler() {
+  aura::WindowTreeHost* window_host = rwhv_aura_->window()->GetHost();
+  if (!window_host)
+    return nullptr;
+
+  return static_cast<aura::WindowTreeHostPlatform*>(window_host)
+      ->platform_window()
+      ->GetEventHandler();
+}
+
+ui::IMContextEfl* RWHVAuraOffscreenHelperEfl::GetIMContextEfl() {
+  if (!im_context_efl_) {
+    if (GetEventHandler() && GetEventHandler()->GetIMContextEfl()) {
+      im_context_efl_ = GetEventHandler()->GetIMContextEfl();
+      im_context_efl_->SetRWHVHelper(this);
+      return im_context_efl_;
+    }
+    LOG(ERROR) << "im_context_efl_ is not set";
+  }
+  return im_context_efl_;
+}
+
 gfx::NativeView RWHVAuraOffscreenHelperEfl::GetNativeView() {
   return rwhv_aura_->GetNativeView();
 }
index 3eec19a..6c49df0 100644 (file)
 #include "ui/gfx/geometry/size_f.h"
 #include "ui/gfx/native_widget_types.h"
 
+namespace ui {
+class EflEventHandler;
+class IMContextEfl;
+}  // namespace ui
+
 namespace content {
 
 class RenderWidgetHostImpl;
@@ -60,6 +65,7 @@ class CONTENT_EXPORT RWHVAuraOffscreenHelperEfl {
   void OnMouseOrTouchEvent(ui::Event* event);
   void EvasToBlinkCords(int x, int y, int* view_x, int* view_y);
 
+  Evas_Object* ewk_view() const;
   Evas_Object* content_image() const { return content_image_; }
   Evas_Object* content_image_elm_host() const {
     return content_image_elm_host_;
@@ -88,6 +94,9 @@ class CONTENT_EXPORT RWHVAuraOffscreenHelperEfl {
   bool MakeCurrent();
   void ClearBrowserFrame();
 
+  ui::EflEventHandler* GetEventHandler();
+  ui::IMContextEfl* GetIMContextEfl();
+
   Evas* evas_ = nullptr;
   Evas_GL* evas_gl_ = nullptr;
   Evas_GL_API* evas_gl_api_ = nullptr;
@@ -113,6 +122,7 @@ class CONTENT_EXPORT RWHVAuraOffscreenHelperEfl {
   gfx::SizeF scaled_contents_size_;
   gfx::Size custom_viewport_size_;
 
+  ui::IMContextEfl* im_context_efl_ = nullptr;
   RenderWidgetHostViewAura* rwhv_aura_ = nullptr;
   WebContents* web_contents_ = nullptr;
 };
index 30f4eeb..6bfe089 100644 (file)
@@ -12,6 +12,8 @@ source_set("efl") {
     "client_native_pixmap_factory_efl.h",
     "efl_event_handler.cc",
     "efl_event_handler.h",
+    "efl_input_method_context.cc",
+    "efl_input_method_context.h",
     "efl_platform_event_source.cc",
     "efl_platform_event_source.h",
     "efl_screen.cc",
@@ -21,6 +23,8 @@ source_set("efl") {
     "efl_window.cc",
     "efl_window.h",
     "efl_window_manager.h",
+    "im_context_efl.cc",
+    "im_context_efl.h",
     "ozone_platform_efl.cc",
     "ozone_platform_efl.h",
   ]
index 060ffa0..b471f6c 100644 (file)
@@ -18,6 +18,7 @@
 #include "ui/ozone/platform/efl/efl_keycode_map.h"
 #include "ui/ozone/platform/efl/efl_platform_event_source.h"
 #include "ui/ozone/platform/efl/efl_window.h"
+#include "ui/ozone/platform/efl/im_context_efl.h"
 
 #if defined(OS_TIZEN)
 #include "ui/events/keycodes/dom/dom_code.h"
@@ -563,8 +564,25 @@ void EflEventHandler::OnKeyDown(void* data,
   LOG(INFO) << "Before consumed check (Key Name: " << key_down->key
             << ", Key State: Down)";
 
+#if defined(OS_TIZEN)
+  if (IsMobileProfile() || IsTvProfile()) {
+    if (thiz->FilterIMEKeyDownEvent(key_down)) {
+      LOG(INFO) << "OnKeyDown, IME filtered the key : " << key_down->key;
+      return;
+    }
+  }
+#endif
+
   KeyEvent event = MakeWebKeyEvent(true, key_down);
-  EflPlatformEventSource::GetInstance()->DispatchEflEvent(&event);
+  auto im_context_efl = thiz->GetIMContextEfl();
+  if (!im_context_efl ||
+      im_context_efl->ShouldHandleImmediately(key_down->key)) {
+    EflPlatformEventSource::GetInstance()->DispatchEflEvent(&event);
+    return;
+  }
+
+  LOG(INFO) << "OnKeyUp,PushToKeyDownEventQueue,key:" << key_down->key;
+  im_context_efl->PushToKeyDownEventQueue(event);
 }
 
 // static
@@ -581,8 +599,106 @@ void EflEventHandler::OnKeyUp(void* data,
   LOG(INFO) << "Before consumed check (Key Name: " << key_up->key
             << ", Key State: Up)";
 
+#if defined(OS_TIZEN)
+  if (IsMobileProfile() || IsTvProfile()) {
+    if (thiz->FilterIMEKeyUpEvent(key_up)) {
+      LOG(INFO) << "OnKeyUp, IME filtered the key : " << key_up->key;
+      return;
+    }
+  }
+#endif
+
+  if (IsTvProfile()) {
+    // For TV IME "Select" and "Cancel" key
+    if (thiz->im_context_efl_ && thiz->im_context_efl_->IsVisible()) {
+      if (!strcmp(key_up->key, "Select") || !strcmp(key_up->key, "Cancel"))
+        thiz->im_context_efl_->HidePanel();
+    }
+  }
+
   KeyEvent event = MakeWebKeyEvent(false, key_up);
-  EflPlatformEventSource::GetInstance()->DispatchEflEvent(&event);
+  auto im_context_efl = thiz->GetIMContextEfl();
+  if (!im_context_efl || im_context_efl->ShouldHandleImmediately(key_up->key)) {
+    EflPlatformEventSource::GetInstance()->DispatchEflEvent(&event);
+    return;
+  }
+
+  LOG(INFO) << "OnKeyUp,PushToKeyUpEventQueue,key:" << key_up->key;
+  im_context_efl->PushToKeyUpEventQueue(event.key_code());
+}
+
+bool EflEventHandler::IsIMEHandleKeyEventEnabled() {
+  if (!GetIMContextEfl())
+    return false;
+
+#if defined(OS_TIZEN_TV_PRODUCT)
+  return GetIMContextEfl()->ImeHandleKeyEventEnabled();
+#endif
+  return true;
+}
+
+bool EflEventHandler::FilterIMEKeyDownEvent(Evas_Event_Key_Down* key_down) {
+  if (!IsIMEHandleKeyEventEnabled())
+    return false;
+
+  bool was_filtered = false;
+  im_context_efl_->HandleKeyDownEvent(key_down, &was_filtered);
+
+  // It is for checking whether key is filtered or not about keyUp event.
+  // Because the process related with key is processed at
+  // EVAS_CALLBACK_KEY_DOWN although key is processed by Ecore_IMF, it isn't
+  // filtered by ecore_imf_context_filter_event about EVAS_CALLBACK_KEY_UP.
+  was_keydown_filtered_by_platform_ = was_filtered;
+
+  if (IsTvProfile() && !strcmp(key_down->key, "Return"))
+    was_return_keydown_filtered_by_platform_ = was_filtered;
+  if (was_filtered)
+    LOG(INFO) << "OnKeyDown,IME filtered the key:" << key_down->key;
+
+  return was_filtered;
+}
+
+bool EflEventHandler::FilterIMEKeyUpEvent(Evas_Event_Key_Up* key_up) {
+  if (!IsIMEHandleKeyEventEnabled())
+    return false;
+
+  // IME will change language mode when receive "Alt" keyup event.
+  // For avoiding IME change language when "Alt + Enter" scene,
+  // don't send the "Alt" keyup event when "Alt + Enter" keydown.
+  if (IsTvProfile() &&
+      (!strcmp(key_up->key, "Alt_L") || !strcmp(key_up->key, "Alt_R")) &&
+      was_alt_enter_key_down_) {
+    was_alt_enter_key_down_ = false;
+  } else {
+    bool was_filtered = false;
+    im_context_efl_->HandleKeyUpEvent(key_up, &was_filtered);
+  }
+
+  // Both selected key and retrun key events are emitted together
+  // while typing a single key on OSK by using remote controller.
+  // The redundant retrun key event needs to be ignored here.
+  if (IsTvProfile() && evas_device_name_get(key_up->dev)) {
+    if (!strstr(evas_device_name_get(key_up->dev), "ime") &&
+        im_context_efl_->IsVisible() && !strcmp(key_up->key, "Return")) {
+      return true;
+    }
+  }
+
+  // When IME was focused out in keydown event handler,
+  // 'was_filtered' will not give the right value.
+  // Refer to 'was_return_keydown_filtered_by_platform_' in this case.
+  if (IsTvProfile() && !strcmp(key_up->key, "Return") &&
+      was_return_keydown_filtered_by_platform_) {
+    was_return_keydown_filtered_by_platform_ = false;
+    return true;
+  }
+
+  if (was_keydown_filtered_by_platform_) {
+    was_keydown_filtered_by_platform_ = false;
+    return true;
+  }
+
+  return false;
 }
 
 void EflEventHandler::OnMultiTouchDownEvent(void* data,
index 72e3347..102269a 100644 (file)
@@ -17,6 +17,7 @@ namespace ui {
 
 class EflWindow;
 class EflPlatformEventSource;
+class IMContextEfl;
 class KeyEvent;
 class TouchEvent;
 
@@ -49,6 +50,11 @@ class EflEventHandler {
   void SendMouseWheel(bool y_direction, int step, int x, int y);
   void SendMouseOut();
 
+  void SetIMContextEfl(IMContextEfl* im_context) {
+    im_context_efl_ = im_context;
+  }
+  IMContextEfl* GetIMContextEfl() { return im_context_efl_; }
+
  private:
   void RegisterCallbacks();
   void AddTouchCallbacks();
@@ -73,6 +79,10 @@ class EflEventHandler {
   template <typename EVT>
   bool GetTouchEventsEnabled(const EVT* evas_evt);
 
+  bool FilterIMEKeyDownEvent(Evas_Event_Key_Down* key_down);
+  bool FilterIMEKeyUpEvent(Evas_Event_Key_Up* key_up);
+  bool IsIMEHandleKeyEventEnabled();
+
   int key_modifiers_ = 0;
   bool touch_events_enabled_ = false;
   bool key_events_enabled_ = true;
@@ -84,6 +94,7 @@ class EflEventHandler {
 
   EflWindow* window_ = nullptr;
   Evas_Object* native_view_ = nullptr;
+  IMContextEfl* im_context_efl_ = nullptr;
 };
 
 }  // namespace ui
diff --git a/tizen_src/chromium_impl/ui/ozone/platform/efl/efl_input_method_context.cc b/tizen_src/chromium_impl/ui/ozone/platform/efl/efl_input_method_context.cc
new file mode 100644 (file)
index 0000000..34162ad
--- /dev/null
@@ -0,0 +1,59 @@
+// Copyright 2021 Samsung Electronics. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/efl/efl_input_method_context.h"
+
+#include "ui/aura/window_tree_host_platform.h"
+#include "ui/base/ime/linux/input_method_auralinux.h"
+#include "ui/ozone/platform/efl/efl_event_handler.h"
+#include "ui/ozone/platform/efl/efl_window.h"
+
+namespace ui {
+
+std::unique_ptr<LinuxInputMethodContext> CreateEflInputMethodContext(
+    ImeKeyEventDispatcher* ime_key_event_dispatcher) {
+  return std::make_unique<EflInputMethodContext>(ime_key_event_dispatcher);
+}
+
+EflInputMethodContext::EflInputMethodContext(
+    ImeKeyEventDispatcher* ime_key_event_dispatcher) {
+  EflWindow* window = static_cast<EflWindow*>(
+      static_cast<aura::WindowTreeHostPlatform*>(ime_key_event_dispatcher)
+          ->platform_window());
+
+  im_context_ = IMContextEfl::Create(window);
+  if (im_context_ && window->GetEventHandler())
+    window->GetEventHandler()->SetIMContextEfl(im_context_.get());
+}
+
+void EflInputMethodContext::UpdateFocus(bool has_client,
+                                        TextInputType old_type,
+                                        TextInputType new_type) {
+  if (old_type != TEXT_INPUT_TYPE_NONE)
+    Blur();
+  if (new_type != TEXT_INPUT_TYPE_NONE)
+    Focus();
+}
+
+void EflInputMethodContext::Reset() {
+  if (im_context_)
+    im_context_->ResetIMFContext();
+}
+
+void EflInputMethodContext::Focus() {
+  if (im_context_ && !im_context_->IsVisible())
+    im_context_->ShowPanel();
+}
+
+void EflInputMethodContext::Blur() {
+  if (im_context_ && im_context_->IsVisible())
+    im_context_->HidePanel();
+}
+
+void EflInputMethodContext::SetCursorLocation(const gfx::Rect& rect) {
+  if (im_context_)
+    im_context_->UpdateCaretBounds(rect);
+}
+
+}  // namespace ui
diff --git a/tizen_src/chromium_impl/ui/ozone/platform/efl/efl_input_method_context.h b/tizen_src/chromium_impl/ui/ozone/platform/efl/efl_input_method_context.h
new file mode 100644 (file)
index 0000000..3cb4a2e
--- /dev/null
@@ -0,0 +1,58 @@
+// Copyright 2021 Samsung Electronics. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_EFL_EFL_INPUT_METHOD_CONTEXT_H_
+#define UI_OZONE_PLATFORM_EFL_EFL_INPUT_METHOD_CONTEXT_H_
+
+#include "ui/base/ime/linux/linux_input_method_context.h"
+#include "ui/ozone/platform/efl/im_context_efl.h"
+
+namespace ui {
+
+class EflWindow;
+class ImeKeyEventDispatcher;
+
+class EflInputMethodContext : public LinuxInputMethodContext {
+ public:
+  EflInputMethodContext(ImeKeyEventDispatcher* ime_key_event_dispatcher);
+  ~EflInputMethodContext() override {}
+
+  // LinuxInputMethodContext overrides:
+  void SetCursorLocation(const gfx::Rect& rect) override;
+  void SetSurroundingText(const std::u16string& text,
+                          const gfx::Range& selection_range) override {}
+  void SetContentType(TextInputType type,
+                      TextInputMode mode,
+                      uint32_t flags,
+                      bool should_do_learning) override {}
+  void SetGrammarFragmentAtCursor(const GrammarFragment& fragment) override {}
+  void SetAutocorrectInfo(const gfx::Range& autocorrect_range,
+                          const gfx::Rect& autocorrect_bounds) override {}
+  void UpdateFocus(bool has_client,
+                   TextInputType old_type,
+                   TextInputType new_type) override;
+  void Reset() override;
+  VirtualKeyboardController* GetVirtualKeyboardController() override {
+    return nullptr;
+  }
+  bool DispatchKeyEvent(const ui::KeyEvent& key_event) override {
+    return false;
+  }
+  bool IsPeekKeyEvent(const ui::KeyEvent& key_event) override { return false; }
+
+  void Focus();
+  void Blur();
+
+  IMContextEfl* GetIMContextEfl() { return im_context_.get(); }
+
+ private:
+  std::unique_ptr<IMContextEfl> im_context_;
+};
+
+std::unique_ptr<LinuxInputMethodContext> CreateEflInputMethodContext(
+    ImeKeyEventDispatcher* ime_key_event_dispatcher);
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_EFL_EFL_INPUT_METHOD_CONTEXT_H_
diff --git a/tizen_src/chromium_impl/ui/ozone/platform/efl/im_context_efl.cc b/tizen_src/chromium_impl/ui/ozone/platform/efl/im_context_efl.cc
new file mode 100644 (file)
index 0000000..35ce817
--- /dev/null
@@ -0,0 +1,1007 @@
+// Copyright 2014 Samsung Electronics. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/efl/im_context_efl.h"
+
+#include <Ecore_Evas.h>
+#include <Ecore_IMF_Evas.h>
+
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/browser/renderer_host/rwhv_aura_offscreen_helper_efl.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "tizen/system_info.h"
+#include "ui/base/ime/ime_text_span.h"
+#include "ui/ozone/platform/efl/efl_event_handler.h"
+#include "ui/ozone/platform/efl/efl_platform_event_source.h"
+#include "ui/ozone/platform/efl/efl_window.h"
+
+#ifdef IM_CTX_DEBUG
+#define IM_CTX_LOG_CHANNEL LOG(ERROR)
+#else
+#define IM_CTX_LOG_CHANNEL LOG(INFO)
+#endif
+
+#define IM_CTX_LOG IM_CTX_LOG_CHANNEL << "## IMCTX ## " << __FUNCTION__ << " "
+
+namespace {
+
+Ecore_IMF_Context* CreateIMFContext(Evas* evas) {
+  IM_CTX_LOG;
+  const char* default_context_id = ecore_imf_context_default_id_get();
+  if (!default_context_id) {
+    LOG(ERROR) << "no default context id";
+    return NULL;
+  }
+  Ecore_IMF_Context* context = ecore_imf_context_add(default_context_id);
+  if (!context) {
+    LOG(ERROR) << "cannot create context";
+    return NULL;
+  }
+
+  Ecore_Window window = ecore_evas_window_get(ecore_evas_ecore_evas_get(evas));
+  ecore_imf_context_client_window_set(context, reinterpret_cast<void*>(window));
+  ecore_imf_context_client_canvas_set(context, evas);
+
+  return context;
+}
+
+Eina_Bool IsIMFVisible(Ecore_IMF_Context* context_) {
+  return ecore_imf_context_input_panel_state_get(context_) ==
+         ECORE_IMF_INPUT_PANEL_STATE_SHOW;
+}
+
+}  // namespace
+
+namespace ui {
+
+// static
+std::unique_ptr<IMContextEfl> IMContextEfl::Create(EflWindow* window) {
+  Ecore_IMF_Context* context = CreateIMFContext(window->evas());
+  if (!context)
+    return nullptr;
+
+  return std::make_unique<IMContextEfl>(context);
+}
+
+IMContextEfl::IMContextEfl(Ecore_IMF_Context* context) : context_(context) {
+  IM_CTX_LOG;
+  InitializeIMFContext();
+}
+
+void IMContextEfl::InitializeIMFContext() {
+  if (IsTvProfile()) {
+    const char* custom_conformant_enabled = "conformant:custom,enabled";
+    ecore_imf_context_input_panel_imdata_set(
+        context_, custom_conformant_enabled, strlen(custom_conformant_enabled));
+  }
+
+  ecore_imf_context_input_panel_enabled_set(context_, false);
+  ecore_imf_context_use_preedit_set(context_, false);
+  ecore_imf_context_event_callback_add(context_, ECORE_IMF_CALLBACK_COMMIT,
+                                       &IMFCommitCallback, this);
+  ecore_imf_context_event_callback_add(context_,
+                                       ECORE_IMF_CALLBACK_DELETE_SURROUNDING,
+                                       &IMFDeleteSurroundingCallback, this);
+  ecore_imf_context_event_callback_add(context_,
+                                       ECORE_IMF_CALLBACK_PREEDIT_CHANGED,
+                                       &IMFPreeditChangedCallback, this);
+  ecore_imf_context_event_callback_add(
+      context_, ECORE_IMF_CALLBACK_PRIVATE_COMMAND_SEND,
+      &IMFTransactionPrivateCommandSendCallback, this);
+  ecore_imf_context_input_panel_event_callback_add(
+      context_, ECORE_IMF_INPUT_PANEL_STATE_EVENT,
+      &IMFInputPanelStateChangedCallback, this);
+  ecore_imf_context_input_panel_event_callback_add(
+      context_, ECORE_IMF_INPUT_PANEL_GEOMETRY_EVENT,
+      &IMFInputPanelGeometryChangedCallback, this);
+  ecore_imf_context_input_panel_event_callback_add(
+      context_, ECORE_IMF_CANDIDATE_PANEL_STATE_EVENT,
+      &IMFCandidatePanelStateChangedCallback, this);
+  ecore_imf_context_input_panel_event_callback_add(
+      context_, ECORE_IMF_CANDIDATE_PANEL_GEOMETRY_EVENT,
+      &IMFCandidatePanelGeometryChangedCallback, this);
+  ecore_imf_context_input_panel_event_callback_add(
+      context_, ECORE_IMF_INPUT_PANEL_LANGUAGE_EVENT,
+      &IMFCandidatePanelLanguageChangedCallback, this);
+  ecore_imf_context_retrieve_surrounding_callback_set(
+      context_, &IMFRetrieveSurroundingCallback, this);
+}
+
+IMContextEfl::~IMContextEfl() {
+  ecore_imf_context_event_callback_del(context_, ECORE_IMF_CALLBACK_COMMIT,
+                                       &IMFCommitCallback);
+  ecore_imf_context_event_callback_del(context_,
+                                       ECORE_IMF_CALLBACK_DELETE_SURROUNDING,
+                                       &IMFDeleteSurroundingCallback);
+  ecore_imf_context_event_callback_del(
+      context_, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, &IMFPreeditChangedCallback);
+  ecore_imf_context_event_callback_del(
+      context_, ECORE_IMF_CALLBACK_PRIVATE_COMMAND_SEND,
+      &IMFTransactionPrivateCommandSendCallback);
+  ecore_imf_context_input_panel_event_callback_del(
+      context_, ECORE_IMF_INPUT_PANEL_STATE_EVENT,
+      &IMFInputPanelStateChangedCallback);
+  ecore_imf_context_input_panel_event_callback_del(
+      context_, ECORE_IMF_INPUT_PANEL_GEOMETRY_EVENT,
+      &IMFInputPanelGeometryChangedCallback);
+  ecore_imf_context_input_panel_event_callback_del(
+      context_, ECORE_IMF_CANDIDATE_PANEL_STATE_EVENT,
+      &IMFCandidatePanelStateChangedCallback);
+  ecore_imf_context_input_panel_event_callback_del(
+      context_, ECORE_IMF_CANDIDATE_PANEL_GEOMETRY_EVENT,
+      &IMFCandidatePanelGeometryChangedCallback);
+  ecore_imf_context_input_panel_event_callback_del(
+      context_, ECORE_IMF_INPUT_PANEL_LANGUAGE_EVENT,
+      &IMFCandidatePanelLanguageChangedCallback);
+  ecore_imf_context_del(context_);
+}
+
+void IMContextEfl::HandleKeyDownEvent(const Evas_Event_Key_Down* event,
+                                      bool* was_filtered) {
+  Ecore_IMF_Event im_event;
+  ecore_imf_evas_event_key_down_wrap(const_cast<Evas_Event_Key_Down*>(event),
+                                     &im_event.key_down);
+  *was_filtered = ecore_imf_context_filter_event(
+      context_, ECORE_IMF_EVENT_KEY_DOWN, &im_event);
+  is_keyevent_processing_ = true;
+}
+
+void IMContextEfl::HandleKeyUpEvent(const Evas_Event_Key_Up* event,
+                                    bool* was_filtered) {
+  Ecore_IMF_Event im_event;
+  ecore_imf_evas_event_key_up_wrap(const_cast<Evas_Event_Key_Up*>(event),
+                                   &im_event.key_up);
+  *was_filtered = ecore_imf_context_filter_event(
+      context_, ECORE_IMF_EVENT_KEY_UP, &im_event);
+}
+
+void IMContextEfl::UpdateInputMethodType(TextInputType type,
+                                         TextInputMode mode,
+                                         bool can_compose_inline
+#if defined(OS_TIZEN_TV_PRODUCT)
+                                         ,
+                                         int password_input_minlength,
+                                         int input_maxlength
+#endif
+) {
+  if (current_type_ == type && current_mode_ == mode &&
+      can_compose_inline_ == can_compose_inline
+#if defined(OS_TIZEN_TV_PRODUCT)
+      && password_input_minlength_ == password_input_minlength &&
+      input_maxlength_ == input_maxlength
+#endif
+  ) {
+    return;
+  }
+
+#if defined(OS_TIZEN_TV_PRODUCT)
+  password_input_minlength_ = password_input_minlength;
+  input_maxlength_ = input_maxlength;
+#endif
+
+  Ecore_IMF_Input_Panel_Layout layout;
+  Ecore_IMF_Input_Panel_Return_Key_Type return_key_type =
+      ECORE_IMF_INPUT_PANEL_RETURN_KEY_TYPE_DEFAULT;
+  Ecore_IMF_Autocapital_Type cap_type = ECORE_IMF_AUTOCAPITAL_TYPE_NONE;
+  bool allow_prediction = true;
+  bool is_multi_line __attribute__((unused)) = false;
+  bool disable_done_key = false;
+
+  return_key_type = is_in_form_tag_
+                        ? ECORE_IMF_INPUT_PANEL_RETURN_KEY_TYPE_GO
+                        : ECORE_IMF_INPUT_PANEL_RETURN_KEY_TYPE_DONE;
+
+  switch (type) {
+    case TEXT_INPUT_TYPE_TEXT:
+      layout = ECORE_IMF_INPUT_PANEL_LAYOUT_NORMAL;
+      break;
+    case TEXT_INPUT_TYPE_PASSWORD:
+      layout = ECORE_IMF_INPUT_PANEL_LAYOUT_PASSWORD;
+      allow_prediction = false;
+      break;
+    case TEXT_INPUT_TYPE_SEARCH:
+      layout = ECORE_IMF_INPUT_PANEL_LAYOUT_NORMAL;
+      return_key_type = ECORE_IMF_INPUT_PANEL_RETURN_KEY_TYPE_SEARCH;
+      break;
+    case TEXT_INPUT_TYPE_EMAIL:
+      layout = ECORE_IMF_INPUT_PANEL_LAYOUT_EMAIL;
+      break;
+    case TEXT_INPUT_TYPE_NUMBER:
+      layout = ECORE_IMF_INPUT_PANEL_LAYOUT_NUMBER;
+      break;
+    case TEXT_INPUT_TYPE_TELEPHONE:
+      layout = ECORE_IMF_INPUT_PANEL_LAYOUT_PHONENUMBER;
+      break;
+    case TEXT_INPUT_TYPE_URL:
+      layout = ECORE_IMF_INPUT_PANEL_LAYOUT_URL;
+      break;
+    case TEXT_INPUT_TYPE_MONTH:
+      layout = ECORE_IMF_INPUT_PANEL_LAYOUT_MONTH;
+      break;
+    case TEXT_INPUT_TYPE_TEXT_AREA:
+    case TEXT_INPUT_TYPE_CONTENT_EDITABLE:
+      layout = ECORE_IMF_INPUT_PANEL_LAYOUT_NORMAL;
+      cap_type = ECORE_IMF_AUTOCAPITAL_TYPE_SENTENCE;
+      return_key_type = ECORE_IMF_INPUT_PANEL_RETURN_KEY_TYPE_DEFAULT;
+      is_multi_line = true;
+      break;
+
+    // No direct mapping to Ecore_IMF API, use simple text layout.
+    case TEXT_INPUT_TYPE_DATE:
+    case TEXT_INPUT_TYPE_DATE_TIME:
+    case TEXT_INPUT_TYPE_DATE_TIME_LOCAL:
+    case TEXT_INPUT_TYPE_TIME:
+    case TEXT_INPUT_TYPE_WEEK:
+    case TEXT_INPUT_TYPE_DATE_TIME_FIELD:
+      layout = ECORE_IMF_INPUT_PANEL_LAYOUT_NORMAL;
+      break;
+
+    case TEXT_INPUT_TYPE_NONE:
+      return;
+
+    default:
+      NOTREACHED();
+      return;
+  }
+
+#if defined(OS_TIZEN_TV_PRODUCT)
+  // Always enable "Up" and "Down" key
+  // Set IMEUp=2, IMEDown=2 will always enable the 'up' and 'down' arrow key
+  std::string im_data("IMEUp=2&IMEDown=2");
+
+  if (allow_prediction && is_get_lookup_table_from_app_)
+    im_data.append("&layouttype=1");
+
+  // set input max length by entrylimit
+  if (input_maxlength_ != DEFAULT_MAX_LENGTH && input_maxlength_ != -1)
+    im_data.append("&entrylimit=" + std::to_string(input_maxlength_));
+
+  ecore_imf_context_input_panel_imdata_set(context_, im_data.c_str(),
+                                           im_data.length());
+
+  // For password input type,disable "Done" key when inputed text not meet
+  // minlength
+  if (TEXT_INPUT_TYPE_PASSWORD == type &&
+      surrounding_text_length_ < password_input_minlength_) {
+    LOG(INFO) << "surrounding_text_length_:" << surrounding_text_length_
+              << ",password_input_minlength_:" << password_input_minlength_
+              << ",disable done key";
+    disable_done_key = true;
+  }
+#endif
+
+  Ecore_IMF_Input_Mode imf_mode;
+  switch (mode) {
+    case TEXT_INPUT_MODE_NUMERIC:
+      imf_mode = ECORE_IMF_INPUT_MODE_NUMERIC;
+      break;
+    default:
+      imf_mode = ECORE_IMF_INPUT_MODE_ALPHA;
+  }
+
+  ecore_imf_context_input_panel_layout_set(context_, layout);
+  ecore_imf_context_input_mode_set(context_, imf_mode);
+  ecore_imf_context_input_panel_return_key_type_set(context_, return_key_type);
+  ecore_imf_context_autocapital_type_set(context_, cap_type);
+  ecore_imf_context_prediction_allow_set(context_, allow_prediction);
+  ecore_imf_context_input_panel_return_key_disabled_set(context_,
+                                                        disable_done_key);
+
+#if defined(OS_TIZEN)
+  Ecore_IMF_Input_Hints hints;
+  if (is_multi_line) {
+    hints = static_cast<Ecore_IMF_Input_Hints>(
+        ecore_imf_context_input_hint_get(context_) |
+        ECORE_IMF_INPUT_HINT_MULTILINE);
+  } else {
+    hints = static_cast<Ecore_IMF_Input_Hints>(
+        ecore_imf_context_input_hint_get(context_) &
+        ~ECORE_IMF_INPUT_HINT_MULTILINE);
+  }
+  ecore_imf_context_input_hint_set(context_, hints);
+#endif
+
+  // If the focused element supports inline rendering of composition text,
+  // we receive and send related events to it. Otherwise, the events related
+  // to the updates of composition text are directed to the candidate window.
+  ecore_imf_context_use_preedit_set(context_, can_compose_inline);
+
+  current_mode_ = mode;
+}
+
+bool IMContextEfl::ShouldHandleImmediately(const char* key) {
+  // Do not forward keyevent now if there is fake key event
+  // handling at the moment to preserve orders of events as in Webkit.
+  // Some keys should be processed after composition text is
+  // released.
+  // So some keys are pushed to KeyDownEventQueue first
+  // then it will be porcessed after composition text is processed.
+  return (
+      (IsPreeditQueueEmpty() || IsKeyUpQueueEmpty()) &&
+      !(!IsCommitQueueEmpty() &&
+        ((IsTvProfile() &&
+          (!strcmp(key, "Select") || !strcmp(key, "Cancel"))) ||
+         !strcmp(key, "Down") || !strcmp(key, "Up") || !strcmp(key, "Right") ||
+         !strcmp(key, "Left") || !strcmp(key, "space") || !strcmp(key, "Tab") ||
+         !strcmp(key, "Return"))));
+}
+
+void IMContextEfl::UpdateInputMethodState(TextInputType type,
+                                          bool can_compose_inline,
+                                          bool show_if_needed
+#if defined(OS_TIZEN_TV_PRODUCT)
+                                          ,
+                                          int password_input_minlength,
+                                          int input_maxlength
+#endif
+) {
+#if defined(OS_TIZEN_TV_PRODUCT)
+  if (current_type_ != type || can_compose_inline_ != can_compose_inline ||
+      password_input_minlength_ != password_input_minlength ||
+      input_maxlength_ != input_maxlength) {
+    UpdateInputMethodType(type, TEXT_INPUT_MODE_DEFAULT, can_compose_inline,
+                          password_input_minlength, input_maxlength);
+#else
+  if (current_type_ != type || can_compose_inline_ != can_compose_inline) {
+    UpdateInputMethodType(type, TEXT_INPUT_MODE_DEFAULT, can_compose_inline);
+#endif
+
+    // Workaround on platform issue:
+    // http://107.108.218.239/bugzilla/show_bug.cgi?id=11494
+    // Keyboard layout doesn't update after change.
+    if (IsVisible() && type != TEXT_INPUT_TYPE_NONE)
+      HidePanel();
+  }
+
+  current_type_ = type;
+  can_compose_inline_ = can_compose_inline;
+
+  bool focus_in = type != TEXT_INPUT_TYPE_NONE;
+  if (focus_in == is_focused_ && (!show_if_needed || IsVisible()))
+    return;
+
+  if (focus_in && show_if_needed) {
+    ShowPanel();
+  } else if (focus_in)
+    OnFocusIn();
+  else if (IsVisible()) {
+    HidePanel();
+  } else {
+    OnFocusOut();
+  }
+}
+
+void IMContextEfl::ShowPanel() {
+  LOG(INFO) << "Show Input Panel!";
+  is_showing_ = is_focused_ = true;
+  ecore_imf_context_focus_in(context_);
+  ecore_imf_context_input_panel_show(context_);
+}
+
+void IMContextEfl::HidePanel() {
+  LOG(INFO) << "Hide Input Panel!";
+  is_showing_ = is_focused_ = false;
+  ecore_imf_context_focus_out(context_);
+  ecore_imf_context_input_panel_hide(context_);
+}
+
+void IMContextEfl::OnFocusIn() {
+  if (current_type_ == ui::TEXT_INPUT_TYPE_NONE)
+    return;
+
+  LOG(INFO) << "IME Focus In";
+  ecore_imf_context_focus_in(context_);
+  is_focused_ = true;
+}
+
+void IMContextEfl::OnFocusOut() {
+  LOG(INFO) << "IME Focus Out";
+  is_focused_ = false;
+
+  CancelComposition();
+  ecore_imf_context_focus_out(context_);
+}
+
+void IMContextEfl::ResetIMFContext() {
+  is_ime_ctx_reset_ = true;
+  ecore_imf_context_reset(context_);
+  is_ime_ctx_reset_ = false;
+}
+
+void IMContextEfl::CancelComposition() {
+  ClearQueues();
+  ResetIMFContext();
+
+  if (composition_.text.length() > 0) {
+    std::u16string empty;
+    ConfirmComposition(empty);
+    composition_.text = u"";
+  }
+}
+
+void IMContextEfl::SetIsInFormTag(bool is_in_form_tag) {
+  is_in_form_tag_ = is_in_form_tag;
+  // TODO: workaround on tizen v3.0 platform issue
+  // Even if virtual keyboard is shown,
+  // the API 'ecore_imf_context_input_panel_state_get()'
+  // returns '1' which means the keyboard is not shown.
+  // It makes the 'HidePanel()' unreachable.
+  if (!IsVisible())
+    return;
+
+  if (ecore_imf_context_input_panel_return_key_type_get(context_) ==
+          ECORE_IMF_INPUT_PANEL_RETURN_KEY_TYPE_SEARCH ||
+      current_type_ == TEXT_INPUT_TYPE_TEXT_AREA) {
+    return;
+  }
+
+  if (is_in_form_tag_) {
+    ecore_imf_context_input_panel_return_key_type_set(
+        context_, ECORE_IMF_INPUT_PANEL_RETURN_KEY_TYPE_GO);
+  } else {
+    if (current_type_ == TEXT_INPUT_TYPE_TEXT ||
+        current_type_ == TEXT_INPUT_TYPE_NUMBER ||
+        current_type_ == TEXT_INPUT_TYPE_TELEPHONE ||
+        current_type_ == TEXT_INPUT_TYPE_PASSWORD ||
+        current_type_ == TEXT_INPUT_TYPE_EMAIL)
+      ecore_imf_context_input_panel_return_key_type_set(
+          context_, ECORE_IMF_INPUT_PANEL_RETURN_KEY_TYPE_DONE);
+    else {
+      ecore_imf_context_input_panel_return_key_type_set(
+          context_, ECORE_IMF_INPUT_PANEL_RETURN_KEY_TYPE_DEFAULT);
+    }
+  }
+}
+
+void IMContextEfl::SetCaretPosition(int position) {
+  if (!context_)
+    return;
+
+  caret_position_ = position;
+  if (!is_keyevent_processing_)
+    ecore_imf_context_cursor_position_set(context_, position);
+}
+
+void IMContextEfl::OnCommit(void* event_info) {
+  if (is_ime_ctx_reset_)
+    return;
+
+  composition_ = CompositionText();
+  char* text = static_cast<char*>(event_info);
+  std::u16string text16 = base::UTF8ToUTF16(text);
+  LOG(INFO) << "OnCommit, text: " << text16;
+  // Only add commit to queue, till we dont know if key event
+  // should be handled. It can be default prevented for exactly.
+  commit_queue_.push(text16);
+
+#if defined(OS_TIZEN_TV_PRODUCT)
+  // Fix IMFCommitCallback come but previous preedit_queue_ haven't handle
+  // cause show double text issue.
+  if (!preedit_queue_.empty())
+    preedit_queue_ = PreeditQueue();
+#endif
+
+  is_surrounding_text_change_in_progress_ = true;
+
+  // sending fake key event if hardware key is not handled as it is
+  // in Webkit.
+  SendFakeCompositionKeyEvent(text16);
+}
+
+void IMContextEfl::SendFakeCompositionKeyEvent(const std::u16string& buf) {
+  std::string str = base::UTF16ToUTF8(buf);
+  Evas_Event_Key_Down downEvent;
+  memset(&downEvent, 0, sizeof(Evas_Event_Key_Down));
+  downEvent.key = str.c_str();
+  downEvent.string = str.c_str();
+
+  KeyEvent event = MakeWebKeyEvent(true, &downEvent);
+  event.is_system_key = true;
+
+  // Key code should be '229' except ASCII key event about key down/up event.
+  // It is according to Web spec about key code [1].
+  // On TV WebAPPs case, key code should be '229' including ASCII key event
+  // about key down/up event.
+  // [1] https://lists.w3.org/Archives/Public/www-dom/2010JulSep/att-0182/
+  // keyCode-spec.html
+  // [2] http://developer.samsung.com/tv/develop/tutorials/user-input/
+  // text-input-ime-external-keyboard
+  if (event.key_code() == 0)
+    event.set_key_code(static_cast<KeyboardCode>(229));
+
+  is_keyevent_processing_ = true;
+  PushToKeyUpEventQueue(event.key_code());
+  EflPlatformEventSource::GetInstance()->DispatchEflEvent(&event);
+}
+
+void IMContextEfl::SetComposition(const char* buffer) {
+  std::u16string text16 = base::UTF8ToUTF16(buffer);
+  if (!text16.empty())
+    SendFakeCompositionKeyEvent(text16.substr(text16.length() - 1));
+  else
+    SendFakeCompositionKeyEvent(text16);
+
+  composition_ = CompositionText();
+  composition_.text = text16;
+  composition_.ime_text_spans.push_back(
+      ImeTextSpan(ImeTextSpan::Type::kComposition, 0,
+                  composition_.text.length(), ImeTextSpan::Thickness::kThin,
+                  ImeTextSpan::UnderlineStyle::kSolid, SK_ColorTRANSPARENT));
+  composition_.selection = gfx::Range(composition_.text.length());
+
+  is_surrounding_text_change_in_progress_ = true;
+
+  // Only add preedit to queue, till we dont know if key event
+  // should be handled. It can be default prevented for exactly.
+  preedit_queue_.push(composition_);
+}
+
+void IMContextEfl::OnPreeditChanged(void* data,
+                                    Ecore_IMF_Context* context,
+                                    void* event_info) {
+  if (is_ime_ctx_reset_)
+    return;
+
+  char* buffer = NULL;
+  ecore_imf_context_preedit_string_get(context, &buffer, 0);
+
+  if (!buffer)
+    return;
+
+  LOG(INFO) << "OnPreeditChanged,text:" << buffer;
+  // 'buffer' is a null teminated utf-8 string,
+  // it should be OK to find '\n' via strchr
+  char* new_line = std::strchr(buffer, '\n');
+
+  // SetComposition does not handle '\n' properly for
+  // 1. Single line input(<input>)
+  // 2. Multiple line input(<textarea>), when not in composition state
+  if (new_line) {
+    if (current_type_ != TEXT_INPUT_TYPE_TEXT_AREA &&
+        current_type_ != TEXT_INPUT_TYPE_CONTENT_EDITABLE) {
+      // 1. Single line case, replace '\n' to ' '
+      do {
+        *new_line = ' ';
+        new_line = std::strchr(new_line + 1, '\n');
+      } while (new_line != NULL);
+    } else if (composition_.text.length() == 0) {
+      // 2. Multiple line case, split to 2 SetComposition:
+      // a. The first line text(without '\n'),
+      // b. All of the text
+      if (new_line == buffer) {
+        // If first line starts with '\n', send a space instead
+        SetComposition(" ");
+      } else {
+        // Send the first line
+        *new_line = '\0';
+        SetComposition(buffer);
+        *new_line = '\n';
+      }
+    }
+  }
+  SetComposition(buffer);
+  free(buffer);
+}
+
+// TODO(kbalazs): figure out what do we need from these callbacks.
+// Tizen-WebKit-efl using all of them.
+
+void IMContextEfl::OnInputPanelStateChanged(int state) {
+  if (state == ECORE_IMF_INPUT_PANEL_STATE_SHOW) {
+    if (rwhv_helper_ && rwhv_helper_->ewk_view()) {
+      is_showing_ = true;
+      evas_object_smart_callback_call(rwhv_helper_->ewk_view(),
+                                      "editorclient,ime,opened", 0);
+    }
+  } else if (state == ECORE_IMF_INPUT_PANEL_STATE_HIDE) {
+    if (rwhv_helper_ && rwhv_helper_->ewk_view()) {
+      is_showing_ = false;
+      evas_object_smart_callback_call(rwhv_helper_->ewk_view(),
+                                      "editorclient,ime,closed", 0);
+    }
+  }
+}
+
+void IMContextEfl::OnInputPanelGeometryChanged() {
+  Eina_Rectangle rect;
+  ecore_imf_context_input_panel_geometry_get(context_, &rect.x, &rect.y,
+                                             &rect.w, &rect.h);
+  if (rwhv_helper_ && rwhv_helper_->ewk_view()) {
+    evas_object_smart_callback_call(rwhv_helper_->ewk_view(),
+                                    "inputmethod,changed",
+                                    static_cast<void*>(&rect));
+  }
+}
+void IMContextEfl::OnCandidateInputPanelStateChanged(int state) {
+  if (rwhv_helper_ && rwhv_helper_->ewk_view()) {
+    if (state == ECORE_IMF_CANDIDATE_PANEL_SHOW) {
+      evas_object_smart_callback_call(rwhv_helper_->ewk_view(),
+                                      "editorclient,candidate,opened", 0);
+    } else {
+      evas_object_smart_callback_call(rwhv_helper_->ewk_view(),
+                                      "editorclient,candidate,closed", 0);
+    }
+  }
+}
+
+void IMContextEfl::OnCandidateInputPanelGeometryChanged() {}
+
+bool IMContextEfl::OnRetrieveSurrounding(char** text, int* offset) {
+  // |text| is for providing the value of input field and
+  // |offset| is for providing cursor position,
+  // when surrounding text & cursor position is requested from otherside.
+  if (!text && !offset)
+    return false;
+
+  // If surrounding text is requested before the process of text is completed,
+  // return false to ensure the value(surrounding text & cursor position).
+  if (is_surrounding_text_change_in_progress_)
+    return false;
+
+  if (text)
+    *text = strdup(surrounding_text_.c_str());
+
+  if (offset)
+    *offset = caret_position_;
+
+  return true;
+}
+
+void IMContextEfl::OnDeleteSurrounding(void* event_info) {
+  auto rwhi = GetRenderWidgetHostImpl();
+  if (!rwhi) {
+    LOG(ERROR) << "rwhi is nullptr";
+    return;
+  }
+
+  is_surrounding_text_change_in_progress_ = true;
+
+  CancelComposition();
+
+  auto event = static_cast<Ecore_IMF_Event_Delete_Surrounding*>(event_info);
+
+  int before = -(event->offset);
+  int after = event->n_chars + event->offset;
+
+  if (is_transaction_finished_) {
+    if (rwhi->GetFrameWidgetInputHandler()) {
+      rwhi->GetFrameWidgetInputHandler()->ExtendSelectionAndDelete(before,
+                                                                   after);
+    }
+  } else {
+    int begin = event->offset < 0 ? 0 : event->offset;
+    int end = event->n_chars - begin;
+
+    std::vector<ui::ImeTextSpan> ime_text_spans;
+    ui::ImeTextSpan text_span;
+#if defined(OS_TIZEN_TV_PRODUCT)
+    text_span.underline_style = ui::ImeTextSpan::UnderlineStyle::kNone;
+#endif
+    ime_text_spans.push_back(text_span);
+    if (rwhi->GetFrameWidgetInputHandler()) {
+      rwhi->GetFrameWidgetInputHandler()->SetCompositionFromExistingText(
+          begin, end, ime_text_spans);
+    }
+  }
+}
+
+void IMContextEfl::OnCandidateInputPanelLanguageChanged(Ecore_IMF_Context*) {
+  auto rwhi = GetRenderWidgetHostImpl();
+
+  if (!rwhi || composition_.text.length() == 0)
+    return;
+
+  CancelComposition();
+}
+
+bool IMContextEfl::IsVisible() const {
+  if (!context_)
+    return false;
+
+  // API 'ecore_imf_context_input_panel_state_get()' seems
+  // to be buggy(ISF guys said it's an ASYNC api).
+  // Even if virtual keyboard is shown,
+  // the API 'ecore_imf_context_input_panel_state_get()' might
+  // returns '1' which means the keyboard is not shown.
+  return is_showing_;
+}
+
+bool IMContextEfl::IsFocused() const {
+  if (!context_)
+    return false;
+
+  return is_focused_;
+}
+
+bool IMContextEfl::WebViewWillBeResized() {
+  if (!context_ || !rwhv_helper_)
+    return false;
+
+  return IsVisible()
+             ? default_view_size_ == rwhv_helper_->GetPhysicalBackingSize()
+             : default_view_size_ != rwhv_helper_->GetPhysicalBackingSize();
+}
+
+void IMContextEfl::ClearQueues() {
+  commit_queue_ = CommitQueue();
+  preedit_queue_ = PreeditQueue();
+  // ClearQueues only called to reset composition state.
+  // To make sure keydown and keyup events are paired,
+  // keydown & keyup event queues should not be cleared.
+  // keyup_event_queue_ = KeyUpEventQueue();
+  // keydown_event_queue_ = KeyDownEventQueue();
+}
+
+void IMContextEfl::IMFCommitCallback(void* data,
+                                     Ecore_IMF_Context*,
+                                     void* event_info) {
+  static_cast<IMContextEfl*>(data)->OnCommit(event_info);
+}
+
+void IMContextEfl::IMFPreeditChangedCallback(void* data,
+                                             Ecore_IMF_Context* context,
+                                             void* event_info) {
+  static_cast<IMContextEfl*>(data)->OnPreeditChanged(data, context, event_info);
+}
+
+void IMContextEfl::IMFInputPanelStateChangedCallback(void* data,
+                                                     Ecore_IMF_Context*,
+                                                     int state) {
+  static_cast<IMContextEfl*>(data)->OnInputPanelStateChanged(state);
+}
+
+void IMContextEfl::IMFInputPanelGeometryChangedCallback(void* data,
+                                                        Ecore_IMF_Context*,
+                                                        int state) {
+  static_cast<IMContextEfl*>(data)->OnInputPanelGeometryChanged();
+}
+
+void IMContextEfl::IMFCandidatePanelStateChangedCallback(void* data,
+                                                         Ecore_IMF_Context*,
+                                                         int state) {
+  static_cast<IMContextEfl*>(data)->OnCandidateInputPanelStateChanged(state);
+}
+
+void IMContextEfl::IMFCandidatePanelGeometryChangedCallback(void* data,
+                                                            Ecore_IMF_Context*,
+                                                            int state) {
+  static_cast<IMContextEfl*>(data)->OnCandidateInputPanelGeometryChanged();
+}
+
+Eina_Bool IMContextEfl::IMFRetrieveSurroundingCallback(void* data,
+                                                       Ecore_IMF_Context*,
+                                                       char** text,
+                                                       int* offset) {
+  return static_cast<IMContextEfl*>(data)->OnRetrieveSurrounding(text, offset);
+}
+
+void IMContextEfl::IMFDeleteSurroundingCallback(void* data,
+                                                Ecore_IMF_Context*,
+                                                void* event_info) {
+  static_cast<IMContextEfl*>(data)->OnDeleteSurrounding(event_info);
+}
+
+void IMContextEfl::IMFCandidatePanelLanguageChangedCallback(
+    void* data,
+    Ecore_IMF_Context* context,
+    int value) {
+  static_cast<IMContextEfl*>(data)->OnCandidateInputPanelLanguageChanged(
+      context);
+}
+
+// static
+void IMContextEfl::IMFTransactionPrivateCommandSendCallback(
+    void* data,
+    Ecore_IMF_Context* context,
+    void* eventInfo) {
+  if (!data || !eventInfo)
+    return;
+
+  char* cmd = static_cast<char*>(eventInfo);
+  auto imce = static_cast<IMContextEfl*>(data);
+  if (!strcmp(cmd, "TRANSACTION_START")) {
+    imce->is_transaction_finished_ = false;
+    imce->composition_ = CompositionText();
+  } else if (!strcmp(cmd, "TRANSACTION_END")) {
+    imce->is_transaction_finished_ = true;
+#if defined(OS_TIZEN_TV_PRODUCT)
+    // When using mobile IME input, build the same process as TV IME:
+    // add SetComposition empty text after
+    // SetCompositionFromExistingText/OnCommit, to fix can not
+    // ScrollRectToVisible to RevealSelection when input the text exceed the
+    // input box length.
+    imce->SetComposition("");
+#endif
+  }
+}
+
+void IMContextEfl::HandleKeyEvent(bool is_key_down, bool processed) {
+  if (is_key_down) {
+    LOG(INFO) << "HandleKeyEvent,keyDown,processed:" << processed;
+    ProcessNextCommitText(processed);
+    ProcessNextPreeditText(processed);
+    ProcessNextKeyUpEvent();
+    ProcessNextKeyDownEvent();
+  } else {
+    is_keyevent_processing_ = false;
+    ecore_imf_context_cursor_position_set(context_, caret_position_);
+  }
+}
+
+void IMContextEfl::ProcessNextCommitText(bool processed) {
+  if (commit_queue_.empty())
+    return;
+
+  while (!commit_queue_.empty()) {
+    if (!processed && !(commit_queue_.front().empty())) {
+      LOG(INFO) << "ProcessNextCommitText,text:" << commit_queue_.front();
+      ConfirmComposition(commit_queue_.front());
+    }
+    commit_queue_.pop();
+  }
+}
+
+void IMContextEfl::ProcessNextPreeditText(bool processed) {
+  if (preedit_queue_.empty())
+    return;
+
+  if (!processed) {
+    LOG(INFO) << "ProcessNextPreeditText,text:" << preedit_queue_.front().text;
+    auto composition_text = preedit_queue_.front();
+    // ECORE_IMF_CALLBACK_PREEDIT_CHANGED is came about H/W BackKey
+    // in KOREAN ISF engine.
+    // It removes selected text because '' is came as composition text.
+    // Do not request ImeSetComposition to prevent delivering empty string
+    // if text is selected status.
+    bool text_selected = composition_text.text.length() == 0;
+#if !defined(EWK_BRINGUP)
+    if (rwhv_helper_) {
+      text_selected =
+          text_selected && rwhv_helper_->GetSelectionController() &&
+          rwhv_helper_->GetSelectionController()->ExistsSelectedText();
+    }
+#endif
+    if (!text_selected) {
+      if (auto rwhi = GetRenderWidgetHostImpl()) {
+        const std::vector<ImeTextSpan>& underlines =
+            reinterpret_cast<const std::vector<ImeTextSpan>&>(
+                composition_text.ime_text_spans);
+        rwhi->ImeSetComposition(composition_text.text, underlines,
+                                gfx::Range::InvalidRange(),
+                                composition_text.selection.start(),
+                                composition_text.selection.end());
+      } else
+        LOG(ERROR) << "rwhi is nullptr";
+    }
+  } else {
+    ResetIMFContext();
+  }
+
+  preedit_queue_.pop();
+}
+
+void IMContextEfl::ProcessNextKeyDownEvent() {
+  if (keydown_event_queue_.empty())
+    return;
+
+  LOG(INFO) << "ProcessNextKeyDownEvent,key:"
+            << keydown_event_queue_.front().key_code();
+  EflPlatformEventSource::GetInstance()->DispatchEflEvent(
+      &keydown_event_queue_.front());
+  keydown_event_queue_.pop();
+}
+
+void IMContextEfl::ProcessNextKeyUpEvent() {
+  if (keyup_event_queue_.empty())
+    return;
+
+  LOG(INFO) << "ProcessNextKeyUpEvent,key:" << keyup_event_queue_.front();
+  KeyEvent event(ET_KEY_RELEASED, keyup_event_queue_.front(), EF_NONE,
+                 base::TimeTicks());
+  EflPlatformEventSource::GetInstance()->DispatchEflEvent(&event);
+  keyup_event_queue_.pop();
+}
+
+void IMContextEfl::PushToKeyUpEventQueue(KeyboardCode code) {
+  keyup_event_queue_.push(code);
+}
+
+void IMContextEfl::PushToKeyDownEventQueue(KeyEvent key) {
+  keydown_event_queue_.push(key);
+}
+
+content::RenderWidgetHostImpl* IMContextEfl::GetRenderWidgetHostImpl() const {
+  return rwhv_helper_ ? rwhv_helper_->GetRenderWidgetHostImpl() : nullptr;
+}
+
+void IMContextEfl::ConfirmComposition(std::u16string& text) {
+  auto rwhi = GetRenderWidgetHostImpl();
+  if (!rwhi) {
+    LOG(ERROR) << "rwhi is nullptr";
+    return;
+  }
+
+  if (text.length()) {
+    rwhi->ImeCommitText(text, std::vector<ImeTextSpan>(),
+                        gfx::Range::InvalidRange(), 0);
+  }
+  rwhi->ImeFinishComposingText(false);
+}
+
+void IMContextEfl::SetSurroundingText(std::string value) {
+  is_surrounding_text_change_in_progress_ = false;
+  surrounding_text_ = value;
+
+#if defined(OS_TIZEN_TV_PRODUCT)
+  // For password input type,disable "Done" key when inputed text not meet
+  // minlength
+  surrounding_text_length_ = base::UTF8ToUTF16(value).length();
+
+  if (ui::TEXT_INPUT_TYPE_PASSWORD == current_type_ &&
+      password_input_minlength_ != -1) {
+    if (surrounding_text_length_ < password_input_minlength_) {
+      ecore_imf_context_input_panel_return_key_disabled_set(context_, true);
+    } else {
+      ecore_imf_context_input_panel_return_key_disabled_set(context_, false);
+    }
+  }
+#endif
+}
+
+bool IMContextEfl::IsShow() {
+  return context_ && is_focused_ && IsIMFVisible(context_);
+}
+
+void IMContextEfl::UpdateCaretBounds(const gfx::Rect& caret_bounds) {
+  if (IsIMFVisible(context_)) {
+    int x = caret_bounds.x();
+    int y = caret_bounds.y();
+    int w = caret_bounds.width();
+    int h = caret_bounds.height();
+    ecore_imf_context_cursor_location_set(context_, x, y, w, h);
+  }
+}
+
+void IMContextEfl::SetWindowIfNeeded(Evas_Object* evas) {
+  if (ecore_imf_context_client_window_get(context_) &&
+      ecore_imf_context_client_canvas_get(context_)) {
+    return;
+  }
+
+  LOG(INFO) << "Setting IMF window context";
+  Ecore_Window window = ecore_evas_window_get(ecore_evas_ecore_evas_get(evas));
+  ecore_imf_context_client_window_set(context_,
+                                      reinterpret_cast<void*>(window));
+  ecore_imf_context_client_canvas_set(context_, evas);
+}
+
+#if defined(OS_TIZEN_TV_PRODUCT)
+void IMContextEfl::SetIMERecommendedWords(const std::string& im_data) {
+  LOG(INFO) << "im_data:" << im_data;
+  ecore_imf_context_input_panel_imdata_set(context_, im_data.c_str(),
+                                           im_data.length());
+}
+
+void IMContextEfl::SetIMERecommendedWordsType(bool should_enable) {
+  // currently IME only support enable setting recommended words
+  // and not support disable it.
+  // If support later, fix it.
+  is_get_lookup_table_from_app_ = should_enable;
+
+  LOG(INFO) << "should_enable:" << should_enable;
+  if (should_enable) {
+    std::string im_data("layouttype=1");
+    ecore_imf_context_input_panel_imdata_set(context_, im_data.c_str(),
+                                             im_data.length());
+  }
+}
+
+bool IMContextEfl::ImeHandleKeyEventEnabled() {
+  return true;
+}
+#endif
+
+}  // namespace ui
diff --git a/tizen_src/chromium_impl/ui/ozone/platform/efl/im_context_efl.h b/tizen_src/chromium_impl/ui/ozone/platform/efl/im_context_efl.h
new file mode 100644 (file)
index 0000000..92b51e7
--- /dev/null
@@ -0,0 +1,221 @@
+// Copyright 2014 Samsung Electronics. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_EFL_IM_CONTEXT_EFL_H_
+#define UI_OZONE_PLATFORM_EFL_IM_CONTEXT_EFL_H_
+
+#include <Evas.h>
+
+#include <queue>
+
+#include "content/public/browser/native_web_keyboard_event.h"
+#include "ui/base/ime/composition_text.h"
+#include "ui/base/ime/text_input_mode.h"
+#include "ui/base/ime/text_input_type.h"
+#include "ui/events/keycodes/keyboard_codes.h"
+#include "ui/gfx/geometry/rect.h"
+
+#if defined(OS_TIZEN_TV_PRODUCT)
+#define DEFAULT_MAX_LENGTH std::numeric_limits<int>::max()
+#endif
+
+typedef struct _Ecore_IMF_Context Ecore_IMF_Context;
+
+namespace content {
+class RenderWidgetHostImpl;
+class RWHVAuraOffscreenHelperEfl;
+}  // namespace content
+
+namespace ui {
+
+class EflWindow;
+class InputMethodAuraLinux;
+class KeyEvent;
+
+class IMContextEfl {
+ public:
+  // Returns nullptr if there is no available backend.
+  static std::unique_ptr<IMContextEfl> Create(EflWindow*);
+
+  IMContextEfl(Ecore_IMF_Context*);
+  ~IMContextEfl();
+
+  void HandleKeyDownEvent(const Evas_Event_Key_Down* event, bool* was_filtered);
+  void HandleKeyUpEvent(const Evas_Event_Key_Up* event, bool* was_filtered);
+
+  void UpdateInputMethodState(TextInputType,
+                              bool can_compose_inline,
+                              bool show_if_needed
+#if defined(OS_TIZEN_TV_PRODUCT)
+                              ,
+                              int password_input_minlength,
+                              int input_maxlength
+#endif
+  );
+
+  void SetWindowIfNeeded(Evas_Object* evas);
+  void OnFocusIn();
+  void OnFocusOut();
+  void ShowPanel();
+  void HidePanel();
+  bool IsVisible() const;
+  bool IsFocused() const;
+  void HandleKeyEvent(bool is_key_down, bool processed);
+
+  void PushToKeyUpEventQueue(KeyboardCode code);
+  void PushToKeyDownEventQueue(KeyEvent key);
+
+  bool ShouldHandleImmediately(const char* key);
+  void ResetIMFContext();
+  void CancelComposition();
+  void SetSurroundingText(std::string value);
+  void SetIsInFormTag(bool is_in_form_tag);
+  void SetCaretPosition(int position);
+  bool WebViewWillBeResized();
+  void UpdateCaretBounds(const gfx::Rect& caret_bounds);
+  bool IsShow();
+
+  void SetRWHVHelper(content::RWHVAuraOffscreenHelperEfl* rwhv_helper) {
+    rwhv_helper_ = rwhv_helper;
+  }
+
+#if defined(OS_TIZEN_TV_PRODUCT)
+  void SetIMERecommendedWords(const std::string&);
+  void SetIMERecommendedWordsType(bool should_enable);
+  bool ImeHandleKeyEventEnabled();
+#endif
+
+ private:
+  void UpdateInputMethodType(TextInputType,
+                             TextInputMode,
+                             bool can_compose_inline
+#if defined(OS_TIZEN_TV_PRODUCT)
+                             ,
+                             int password_input_minlength,
+                             int input_maxlength
+
+#endif
+  );
+
+  bool IsPreeditQueueEmpty() const { return preedit_queue_.empty(); }
+  bool IsCommitQueueEmpty() const { return commit_queue_.empty(); }
+  bool IsKeyUpQueueEmpty() const { return keyup_event_queue_.empty(); }
+  bool IsKeyDownQueueEmpty() const { return keydown_event_queue_.empty(); }
+  void PreeditQueuePop() { preedit_queue_.pop(); }
+  void ClearQueues();
+
+  void InitializeIMFContext();
+  void ConfirmComposition(std::u16string& text);
+  void SetComposition(const char* buffer);
+
+  // callbacks
+  static void IMFCandidatePanelStateChangedCallback(void* data,
+                                                    Ecore_IMF_Context*,
+                                                    int state);
+  static void IMFCommitCallback(void* data,
+                                Ecore_IMF_Context*,
+                                void* event_info);
+  static void IMFInputPanelStateChangedCallback(void* data,
+                                                Ecore_IMF_Context*,
+                                                int state);
+  static void IMFPreeditChangedCallback(void* data,
+                                        Ecore_IMF_Context* context,
+                                        void* event_info);
+  static void IMFTransactionPrivateCommandSendCallback(
+      void* data,
+      Ecore_IMF_Context* context,
+      void* event_info);
+  static Eina_Bool IMFRetrieveSurroundingCallback(void* data,
+                                                  Ecore_IMF_Context*,
+                                                  char** text,
+                                                  int* offset);
+  static void IMFCandidatePanelGeometryChangedCallback(void* data,
+                                                       Ecore_IMF_Context*,
+                                                       int state);
+  static void IMFCandidatePanelLanguageChangedCallback(
+      void* data,
+      Ecore_IMF_Context* context,
+      int value);
+  static void IMFDeleteSurroundingCallback(void* data,
+                                           Ecore_IMF_Context*,
+                                           void* event_info);
+
+  static void IMFInputPanelGeometryChangedCallback(void* data,
+                                                   Ecore_IMF_Context*,
+                                                   int state);
+  // callback handlers
+  void OnCommit(void* event_info);
+  void OnPreeditChanged(void* data,
+                        Ecore_IMF_Context* context,
+                        void* event_info);
+  void OnInputPanelStateChanged(int state);
+  void OnInputPanelGeometryChanged();
+
+  void OnCandidateInputPanelStateChanged(int state);
+  void OnCandidateInputPanelGeometryChanged();
+
+  bool OnRetrieveSurrounding(char** text, int* offset);
+  void OnDeleteSurrounding(void* event_info);
+  void OnSelectionCalled(void* event_info);
+  void OnCandidateInputPanelLanguageChanged(Ecore_IMF_Context* context);
+#if defined(OS_TIZEN_TV_PRODUCT)
+  void OnIMFEventPrivateCommand(std::string command);
+#endif
+
+  void ProcessNextCommitText(bool processed);
+  void ProcessNextPreeditText(bool processed);
+  void ProcessNextKeyDownEvent();
+  void ProcessNextKeyUpEvent();
+  content::RenderWidgetHostImpl* GetRenderWidgetHostImpl() const;
+
+  void SendFakeCompositionKeyEvent(const std::u16string& buf);
+
+  Ecore_IMF_Context* context_;
+
+  // The current soft keyboard mode and layout
+  TextInputMode current_mode_ = TEXT_INPUT_MODE_DEFAULT;
+  TextInputType current_type_ = TEXT_INPUT_TYPE_NONE;
+
+  CompositionText composition_;
+
+  // View size when IME isn't shown.
+  gfx::Size default_view_size_;
+
+  typedef std::queue<std::u16string> CommitQueue;
+  typedef std::queue<CompositionText> PreeditQueue;
+  typedef std::queue<KeyboardCode> KeyUpEventQueue;
+  typedef std::queue<KeyEvent> KeyDownEventQueue;
+
+  CommitQueue commit_queue_;
+  PreeditQueue preedit_queue_;
+  KeyUpEventQueue keyup_event_queue_;
+  KeyDownEventQueue keydown_event_queue_;
+
+#if defined(OS_TIZEN_TV_PRODUCT)
+  int password_input_minlength_ = -1;
+  int input_maxlength_ = DEFAULT_MAX_LENGTH;
+  int surrounding_text_length_ = 0;
+#endif
+
+  std::string surrounding_text_;
+  bool is_surrounding_text_change_in_progress_ = false;
+
+  // Whether or not the associated widget is focused.
+  bool is_focused_ = false;
+  // Whether or not is in form tag.
+  bool is_in_form_tag_ = false;
+  bool is_showing_ = false;
+  bool can_compose_inline_ = false;
+  bool is_ime_ctx_reset_ = false;
+  bool is_get_lookup_table_from_app_ = false;
+  int caret_position_ = 0;
+  bool is_keyevent_processing_ = false;
+  bool is_transaction_finished_ = true;
+  InputMethodAuraLinux* im_aura_ = nullptr;
+  content::RWHVAuraOffscreenHelperEfl* rwhv_helper_ = nullptr;
+};
+
+}  // namespace ui
+
+#endif
index 4a4c755..cfea836 100644 (file)
@@ -2313,6 +2313,8 @@ void EWebView::InitializeContent() {
   native_view_ =
       static_cast<WebContentsImplEfl*>(web_contents_.get())->GetEflNativeView();
   evas_object_smart_member_add(native_view_, evas_object_);
+  static_cast<WebContentsImpl*>(web_contents_.get())
+      ->set_ewk_view(evas_object_);
   InitializeWindowTreeHost();
 }
 
index 59ffd98..b00ca80 100644 (file)
 #include "ui/base/ime/text_input_flags.h"
 #include "ui/events/event.h"
 
+#if defined(OS_TIZEN)
+#include "ui/ozone/platform/efl/efl_input_method_context.h"
+#endif
+
 namespace {
 
 constexpr base::TimeDelta kIgnoreCommitsDuration = base::Milliseconds(100);
@@ -49,7 +53,11 @@ InputMethodAuraLinux::InputMethodAuraLinux(
       text_input_type_(TEXT_INPUT_TYPE_NONE),
       is_sync_mode_(false),
       composition_changed_(false) {
+#if defined(OS_TIZEN)
+  context_ = CreateEflInputMethodContext(ime_key_event_dispatcher);
+#else
   context_ = CreateLinuxInputMethodContext(this);
+#endif
 }
 
 InputMethodAuraLinux::~InputMethodAuraLinux() = default;
index afda88e..10f232b 100644 (file)
@@ -907,6 +907,10 @@ class EVENTS_EXPORT KeyEvent : public Event {
   std::string ToString() const override;
   std::unique_ptr<Event> Clone() const override;
 
+#if defined(USE_EFL)
+  bool is_system_key = false;
+#endif
+
  protected:
   friend class KeyEventTestApi;