--- /dev/null
+// 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
--- /dev/null
+// 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