1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/chromeos/input_method/input_method_engine.h"
6 
7 #include <map>
8 #include <memory>
9 #include <utility>
10 
11 #include "ash/keyboard/ui/keyboard_ui_controller.h"
12 #include "base/check.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/notreached.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "chrome/browser/chromeos/input_method/ui/input_method_menu_item.h"
19 #include "chrome/browser/chromeos/input_method/ui/input_method_menu_manager.h"
20 #include "chrome/browser/profiles/profile_manager.h"
21 #include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
22 #include "ui/aura/window.h"
23 #include "ui/aura/window_tree_host.h"
24 #include "ui/base/ime/candidate_window.h"
25 #include "ui/base/ime/chromeos/component_extension_ime_manager.h"
26 #include "ui/base/ime/chromeos/extension_ime_util.h"
27 #include "ui/base/ime/chromeos/ime_bridge.h"
28 #include "ui/base/ime/chromeos/ime_keymap.h"
29 #include "ui/base/ime/composition_text.h"
30 #include "ui/base/ime/constants.h"
31 #include "ui/base/ime/text_input_flags.h"
32 #include "ui/base/ui_base_features.h"
33 #include "ui/events/event.h"
34 #include "ui/events/event_sink.h"
35 #include "ui/events/event_utils.h"
36 #include "ui/events/keycodes/dom/dom_code.h"
37 #include "ui/events/keycodes/dom/keycode_converter.h"
38 
39 namespace chromeos {
40 
41 namespace {
42 
43 const char kErrorNotActive[] = "IME is not active.";
44 const char kErrorWrongContext[] = "Context is not active.";
45 const char kCandidateNotFound[] = "Candidate not found.";
46 const char kSuggestionNotFound[] = "Suggestion not found.";
47 
48 // The default entry number of a page in CandidateWindowProperty.
49 const int kDefaultPageSize = 9;
50 
51 }  // namespace
52 
53 InputMethodEngine::Candidate::Candidate() = default;
54 
55 InputMethodEngine::Candidate::Candidate(const Candidate& other) = default;
56 
57 InputMethodEngine::Candidate::~Candidate() = default;
58 
59 // When the default values are changed, please modify
60 // CandidateWindow::CandidateWindowProperty defined in chromeos/ime/ too.
CandidateWindowProperty()61 InputMethodEngine::CandidateWindowProperty::CandidateWindowProperty()
62     : page_size(kDefaultPageSize),
63       is_cursor_visible(true),
64       is_vertical(false),
65       show_window_at_composition(false),
66       is_auxiliary_text_visible(false) {}
67 
68 InputMethodEngine::CandidateWindowProperty::~CandidateWindowProperty() =
69     default;
70 InputMethodEngine::CandidateWindowProperty::CandidateWindowProperty(
71     const CandidateWindowProperty& other) = default;
72 
73 InputMethodEngine::InputMethodEngine() = default;
74 
75 InputMethodEngine::~InputMethodEngine() = default;
76 
Enable(const std::string & component_id)77 void InputMethodEngine::Enable(const std::string& component_id) {
78   InputMethodEngineBase::Enable(component_id);
79   EnableInputView();
80   // Resets candidate_window_property_ whenever a new component_id (aka
81   // engine_id) is enabled.
82   candidate_window_property_ = {component_id,
83                                 InputMethodEngine::CandidateWindowProperty()};
84 }
85 
IsActive() const86 bool InputMethodEngine::IsActive() const {
87   return !active_component_id_.empty();
88 }
89 
PropertyActivate(const std::string & property_name)90 void InputMethodEngine::PropertyActivate(const std::string& property_name) {
91   observer_->OnMenuItemActivated(active_component_id_, property_name);
92 }
93 
CandidateClicked(uint32_t index)94 void InputMethodEngine::CandidateClicked(uint32_t index) {
95   if (index > candidate_ids_.size()) {
96     return;
97   }
98 
99   // Only left button click is supported at this moment.
100   observer_->OnCandidateClicked(active_component_id_, candidate_ids_.at(index),
101                                 InputMethodEngineBase::MOUSE_BUTTON_LEFT);
102 }
103 
AssistiveWindowButtonClicked(const ui::ime::AssistiveWindowButton & button)104 void InputMethodEngine::AssistiveWindowButtonClicked(
105     const ui::ime::AssistiveWindowButton& button) {
106   observer_->OnAssistiveWindowButtonClicked(button);
107 }
108 
SetMirroringEnabled(bool mirroring_enabled)109 void InputMethodEngine::SetMirroringEnabled(bool mirroring_enabled) {
110   if (mirroring_enabled != is_mirroring_) {
111     is_mirroring_ = mirroring_enabled;
112     observer_->OnScreenProjectionChanged(is_mirroring_ || is_casting_);
113   }
114 }
115 
SetCastingEnabled(bool casting_enabled)116 void InputMethodEngine::SetCastingEnabled(bool casting_enabled) {
117   if (casting_enabled != is_casting_) {
118     is_casting_ = casting_enabled;
119     observer_->OnScreenProjectionChanged(is_mirroring_ || is_casting_);
120   }
121 }
122 
123 ui::InputMethodKeyboardController*
GetInputMethodKeyboardController() const124 InputMethodEngine::GetInputMethodKeyboardController() const {
125   // Callers expect a nullptr when the keyboard is disabled. See
126   // https://crbug.com/850020.
127   if (!keyboard::KeyboardUIController::HasInstance() ||
128       !keyboard::KeyboardUIController::Get()->IsEnabled()) {
129     return nullptr;
130   }
131   return keyboard::KeyboardUIController::Get()
132       ->input_method_keyboard_controller();
133 }
134 
OnSuggestionsChanged(const std::vector<std::string> & suggestions)135 void InputMethodEngine::OnSuggestionsChanged(
136     const std::vector<std::string>& suggestions) {
137   observer_->OnSuggestionsChanged(suggestions);
138 }
139 
SetButtonHighlighted(int context_id,const ui::ime::AssistiveWindowButton & button,bool highlighted,std::string * error)140 bool InputMethodEngine::SetButtonHighlighted(
141     int context_id,
142     const ui::ime::AssistiveWindowButton& button,
143     bool highlighted,
144     std::string* error) {
145   if (!IsActive()) {
146     *error = kErrorNotActive;
147     return false;
148   }
149   if (context_id != context_id_ || context_id_ == -1) {
150     *error = kErrorWrongContext;
151     return false;
152   }
153   IMEAssistiveWindowHandlerInterface* aw_handler =
154       ui::IMEBridge::Get()->GetAssistiveWindowHandler();
155   if (aw_handler)
156     aw_handler->SetButtonHighlighted(button, highlighted);
157   return true;
158 }
159 
ClickButton(const ui::ime::AssistiveWindowButton & button)160 void InputMethodEngine::ClickButton(
161     const ui::ime::AssistiveWindowButton& button) {
162   observer_->OnAssistiveWindowButtonClicked(button);
163 }
164 
AcceptSuggestionCandidate(int context_id,const base::string16 & suggestion,std::string * error)165 bool InputMethodEngine::AcceptSuggestionCandidate(
166     int context_id,
167     const base::string16& suggestion,
168     std::string* error) {
169   if (!IsActive()) {
170     *error = kErrorNotActive;
171     return false;
172   }
173   if (context_id != context_id_ || context_id_ == -1) {
174     *error = kErrorWrongContext;
175     return false;
176   }
177 
178   CommitText(context_id, base::UTF16ToUTF8(suggestion).c_str(), error);
179 
180   IMEAssistiveWindowHandlerInterface* aw_handler =
181       ui::IMEBridge::Get()->GetAssistiveWindowHandler();
182   if (aw_handler)
183     aw_handler->AcceptSuggestion(suggestion);
184   return true;
185 }
186 
187 const InputMethodEngine::CandidateWindowProperty&
GetCandidateWindowProperty(const std::string & engine_id)188 InputMethodEngine::GetCandidateWindowProperty(const std::string& engine_id) {
189   if (candidate_window_property_.first != engine_id)
190     candidate_window_property_ = {engine_id,
191                                   InputMethodEngine::CandidateWindowProperty()};
192   return candidate_window_property_.second;
193 }
194 
SetCandidateWindowProperty(const std::string & engine_id,const CandidateWindowProperty & property)195 void InputMethodEngine::SetCandidateWindowProperty(
196     const std::string& engine_id,
197     const CandidateWindowProperty& property) {
198   // Type conversion from InputMethodEngine::CandidateWindowProperty to
199   // CandidateWindow::CandidateWindowProperty defined in chromeos/ime/.
200   ui::CandidateWindow::CandidateWindowProperty dest_property;
201   dest_property.page_size = property.page_size;
202   dest_property.is_cursor_visible = property.is_cursor_visible;
203   dest_property.is_vertical = property.is_vertical;
204   dest_property.show_window_at_composition =
205       property.show_window_at_composition;
206   dest_property.cursor_position =
207       candidate_window_.GetProperty().cursor_position;
208   dest_property.auxiliary_text = property.auxiliary_text;
209   dest_property.is_auxiliary_text_visible = property.is_auxiliary_text_visible;
210   dest_property.current_candidate_index = property.current_candidate_index;
211   dest_property.total_candidates = property.total_candidates;
212 
213   candidate_window_.SetProperty(dest_property);
214   candidate_window_property_ = {engine_id, property};
215 
216   if (IsActive()) {
217     IMECandidateWindowHandlerInterface* cw_handler =
218         ui::IMEBridge::Get()->GetCandidateWindowHandler();
219     if (cw_handler)
220       cw_handler->UpdateLookupTable(candidate_window_, window_visible_);
221   }
222 }
223 
SetCandidateWindowVisible(bool visible,std::string * error)224 bool InputMethodEngine::SetCandidateWindowVisible(bool visible,
225                                                   std::string* error) {
226   if (!IsActive()) {
227     *error = kErrorNotActive;
228     return false;
229   }
230 
231   window_visible_ = visible;
232   IMECandidateWindowHandlerInterface* cw_handler =
233       ui::IMEBridge::Get()->GetCandidateWindowHandler();
234   if (cw_handler)
235     cw_handler->UpdateLookupTable(candidate_window_, window_visible_);
236   return true;
237 }
238 
SetCandidates(int context_id,const std::vector<Candidate> & candidates,std::string * error)239 bool InputMethodEngine::SetCandidates(
240     int context_id,
241     const std::vector<Candidate>& candidates,
242     std::string* error) {
243   if (!IsActive()) {
244     *error = kErrorNotActive;
245     return false;
246   }
247   if (context_id != context_id_ || context_id_ == -1) {
248     *error = kErrorWrongContext;
249     return false;
250   }
251 
252   // TODO: Nested candidates
253   candidate_ids_.clear();
254   candidate_indexes_.clear();
255   candidate_window_.mutable_candidates()->clear();
256   for (const auto& candidate : candidates) {
257     ui::CandidateWindow::Entry entry;
258     entry.value = base::UTF8ToUTF16(candidate.value);
259     entry.label = base::UTF8ToUTF16(candidate.label);
260     entry.annotation = base::UTF8ToUTF16(candidate.annotation);
261     entry.description_title = base::UTF8ToUTF16(candidate.usage.title);
262     entry.description_body = base::UTF8ToUTF16(candidate.usage.body);
263 
264     // Store a mapping from the user defined ID to the candidate index.
265     candidate_indexes_[candidate.id] = candidate_ids_.size();
266     candidate_ids_.push_back(candidate.id);
267 
268     candidate_window_.mutable_candidates()->push_back(entry);
269   }
270   if (IsActive()) {
271     IMECandidateWindowHandlerInterface* cw_handler =
272         ui::IMEBridge::Get()->GetCandidateWindowHandler();
273     if (cw_handler)
274       cw_handler->UpdateLookupTable(candidate_window_, window_visible_);
275   }
276   return true;
277 }
278 
SetCursorPosition(int context_id,int candidate_id,std::string * error)279 bool InputMethodEngine::SetCursorPosition(int context_id,
280                                           int candidate_id,
281                                           std::string* error) {
282   if (!IsActive()) {
283     *error = kErrorNotActive;
284     return false;
285   }
286   if (context_id != context_id_ || context_id_ == -1) {
287     *error = kErrorWrongContext;
288     return false;
289   }
290 
291   std::map<int, int>::const_iterator position =
292       candidate_indexes_.find(candidate_id);
293   if (position == candidate_indexes_.end()) {
294     *error = base::StringPrintf("%s candidate id = %d", kCandidateNotFound,
295                                 candidate_id);
296     return false;
297   }
298 
299   candidate_window_.set_cursor_position(position->second);
300   IMECandidateWindowHandlerInterface* cw_handler =
301       ui::IMEBridge::Get()->GetCandidateWindowHandler();
302   if (cw_handler)
303     cw_handler->UpdateLookupTable(candidate_window_, window_visible_);
304   return true;
305 }
306 
SetSuggestion(int context_id,const ui::ime::SuggestionDetails & details,std::string * error)307 bool InputMethodEngine::SetSuggestion(int context_id,
308                                       const ui::ime::SuggestionDetails& details,
309                                       std::string* error) {
310   if (!IsActive()) {
311     *error = kErrorNotActive;
312     return false;
313   }
314   if (context_id != context_id_ || context_id_ == -1) {
315     *error = kErrorWrongContext;
316     return false;
317   }
318 
319   IMEAssistiveWindowHandlerInterface* aw_handler =
320       ui::IMEBridge::Get()->GetAssistiveWindowHandler();
321   if (aw_handler)
322     aw_handler->ShowSuggestion(details);
323   return true;
324 }
325 
DismissSuggestion(int context_id,std::string * error)326 bool InputMethodEngine::DismissSuggestion(int context_id, std::string* error) {
327   if (!IsActive()) {
328     *error = kErrorNotActive;
329     return false;
330   }
331   if (context_id != context_id_ || context_id_ == -1) {
332     *error = kErrorWrongContext;
333     return false;
334   }
335 
336   IMEAssistiveWindowHandlerInterface* aw_handler =
337       ui::IMEBridge::Get()->GetAssistiveWindowHandler();
338   if (aw_handler)
339     aw_handler->HideSuggestion();
340   return true;
341 }
342 
AcceptSuggestion(int context_id,std::string * error)343 bool InputMethodEngine::AcceptSuggestion(int context_id, std::string* error) {
344   if (!IsActive()) {
345     *error = kErrorNotActive;
346     return false;
347   }
348   if (context_id != context_id_ || context_id_ == -1) {
349     *error = kErrorWrongContext;
350     return false;
351   }
352 
353   FinishComposingText(context_id_, error);
354   if (!error->empty()) {
355     return false;
356   }
357 
358   IMEAssistiveWindowHandlerInterface* aw_handler =
359       ui::IMEBridge::Get()->GetAssistiveWindowHandler();
360   if (aw_handler) {
361     base::string16 suggestion_text = aw_handler->GetSuggestionText();
362     if (suggestion_text.empty()) {
363       *error = kSuggestionNotFound;
364       return false;
365     }
366     size_t confirmed_length = aw_handler->GetConfirmedLength();
367     if (confirmed_length > 0) {
368       DeleteSurroundingText(context_id_, -confirmed_length, confirmed_length,
369                             error);
370     }
371     CommitText(context_id_, (base::UTF16ToUTF8(suggestion_text)).c_str(),
372                error);
373     aw_handler->HideSuggestion();
374   }
375   return true;
376 }
377 
SetAssistiveWindowProperties(int context_id,const AssistiveWindowProperties & assistive_window,std::string * error)378 bool InputMethodEngine::SetAssistiveWindowProperties(
379     int context_id,
380     const AssistiveWindowProperties& assistive_window,
381     std::string* error) {
382   if (!IsActive()) {
383     *error = kErrorNotActive;
384     return false;
385   }
386   if (context_id != context_id_ || context_id_ == -1) {
387     *error = kErrorWrongContext;
388     return false;
389   }
390 
391   IMEAssistiveWindowHandlerInterface* aw_handler =
392       ui::IMEBridge::Get()->GetAssistiveWindowHandler();
393   if (aw_handler)
394     aw_handler->SetAssistiveWindowProperties(assistive_window);
395   return true;
396 }
397 
SetMenuItems(const std::vector<input_method::InputMethodManager::MenuItem> & items,std::string * error)398 bool InputMethodEngine::SetMenuItems(
399     const std::vector<input_method::InputMethodManager::MenuItem>& items,
400     std::string* error) {
401   return UpdateMenuItems(items, error);
402 }
403 
UpdateMenuItems(const std::vector<input_method::InputMethodManager::MenuItem> & items,std::string * error)404 bool InputMethodEngine::UpdateMenuItems(
405     const std::vector<input_method::InputMethodManager::MenuItem>& items,
406     std::string* error) {
407   if (!IsActive()) {
408     *error = kErrorNotActive;
409     return false;
410   }
411 
412   ui::ime::InputMethodMenuItemList menu_item_list;
413   for (const auto& item : items) {
414     ui::ime::InputMethodMenuItem property;
415     MenuItemToProperty(item, &property);
416     menu_item_list.push_back(property);
417   }
418 
419   ui::ime::InputMethodMenuManager::GetInstance()
420       ->SetCurrentInputMethodMenuItemList(menu_item_list);
421 
422   input_method::InputMethodManager::Get()->NotifyImeMenuItemsChanged(
423       active_component_id_, items);
424   return true;
425 }
426 
HideInputView()427 void InputMethodEngine::HideInputView() {
428   auto* keyboard_client = ChromeKeyboardControllerClient::Get();
429   if (keyboard_client->is_keyboard_enabled())
430     keyboard_client->HideKeyboard(ash::HideReason::kUser);
431 }
432 
UpdateComposition(const ui::CompositionText & composition_text,uint32_t cursor_pos,bool is_visible)433 void InputMethodEngine::UpdateComposition(
434     const ui::CompositionText& composition_text,
435     uint32_t cursor_pos,
436     bool is_visible) {
437   ui::IMEInputContextHandlerInterface* input_context =
438       ui::IMEBridge::Get()->GetInputContextHandler();
439   if (input_context)
440     input_context->UpdateCompositionText(composition_text, cursor_pos,
441                                          is_visible);
442 }
443 
SetCompositionRange(uint32_t before,uint32_t after,const std::vector<ui::ImeTextSpan> & text_spans)444 bool InputMethodEngine::SetCompositionRange(
445     uint32_t before,
446     uint32_t after,
447     const std::vector<ui::ImeTextSpan>& text_spans) {
448   ui::IMEInputContextHandlerInterface* input_context =
449       ui::IMEBridge::Get()->GetInputContextHandler();
450   if (!input_context)
451     return false;
452   return input_context->SetCompositionRange(before, after, text_spans);
453 }
454 
SetComposingRange(uint32_t start,uint32_t end,const std::vector<ui::ImeTextSpan> & text_spans)455 bool InputMethodEngine::SetComposingRange(
456     uint32_t start,
457     uint32_t end,
458     const std::vector<ui::ImeTextSpan>& text_spans) {
459   ui::IMEInputContextHandlerInterface* input_context =
460       ui::IMEBridge::Get()->GetInputContextHandler();
461   if (!input_context)
462     return false;
463   return input_context->SetComposingRange(start, end, text_spans);
464 }
465 
GetAutocorrectRange()466 gfx::Range InputMethodEngine::GetAutocorrectRange() {
467   ui::IMEInputContextHandlerInterface* input_context =
468       ui::IMEBridge::Get()->GetInputContextHandler();
469   if (!input_context)
470     return gfx::Range();
471   return input_context->GetAutocorrectRange();
472 }
473 
GetAutocorrectCharacterBounds()474 gfx::Rect InputMethodEngine::GetAutocorrectCharacterBounds() {
475   ui::IMEInputContextHandlerInterface* input_context =
476       ui::IMEBridge::Get()->GetInputContextHandler();
477   if (!input_context)
478     return gfx::Rect();
479   return input_context->GetAutocorrectCharacterBounds();
480 }
481 
SetAutocorrectRange(const base::string16 & autocorrect_text,uint32_t start,uint32_t end)482 bool InputMethodEngine::SetAutocorrectRange(
483     const base::string16& autocorrect_text,
484     uint32_t start,
485     uint32_t end) {
486   ui::IMEInputContextHandlerInterface* input_context =
487       ui::IMEBridge::Get()->GetInputContextHandler();
488   if (!input_context)
489     return false;
490   return input_context->SetAutocorrectRange(autocorrect_text, start, end);
491 }
492 
SetSelectionRange(uint32_t start,uint32_t end)493 bool InputMethodEngine::SetSelectionRange(uint32_t start, uint32_t end) {
494   ui::IMEInputContextHandlerInterface* input_context =
495       ui::IMEBridge::Get()->GetInputContextHandler();
496   if (!input_context)
497     return false;
498   return input_context->SetSelectionRange(start, end);
499 }
500 
CommitTextToInputContext(int context_id,const std::string & text)501 void InputMethodEngine::CommitTextToInputContext(int context_id,
502                                                  const std::string& text) {
503   ui::IMEInputContextHandlerInterface* input_context =
504       ui::IMEBridge::Get()->GetInputContextHandler();
505   if (!input_context)
506     return;
507 
508   const bool had_composition_text = input_context->HasCompositionText();
509   input_context->CommitText(text);
510 
511   if (had_composition_text) {
512     // Records histograms for committed characters with composition text.
513     base::string16 wtext = base::UTF8ToUTF16(text);
514     UMA_HISTOGRAM_CUSTOM_COUNTS("InputMethod.CommitLength", wtext.length(), 1,
515                                 25, 25);
516   }
517 }
518 
SendKeyEvent(ui::KeyEvent * event,const std::string & code,std::string * error)519 bool InputMethodEngine::SendKeyEvent(ui::KeyEvent* event,
520                                      const std::string& code,
521                                      std::string* error) {
522   DCHECK(event);
523   if (event->key_code() == ui::VKEY_UNKNOWN)
524     event->set_key_code(ui::DomKeycodeToKeyboardCode(code));
525 
526   // Marks the simulated key event is from the Virtual Keyboard.
527   ui::Event::Properties properties;
528   properties[ui::kPropertyFromVK] =
529       std::vector<uint8_t>(ui::kPropertyFromVKSize);
530   properties[ui::kPropertyFromVK][ui::kPropertyFromVKIsMirroringIndex] =
531       (uint8_t)is_mirroring_;
532   event->SetProperties(properties);
533 
534   ui::IMEInputContextHandlerInterface* input_context =
535       ui::IMEBridge::Get()->GetInputContextHandler();
536   if (input_context) {
537     input_context->SendKeyEvent(event);
538     return true;
539   }
540 
541   *error = kErrorWrongContext;
542   return false;
543 }
544 
IsValidKeyEvent(const ui::KeyEvent * ui_event)545 bool InputMethodEngine::IsValidKeyEvent(const ui::KeyEvent* ui_event) {
546   // TODO(CRBUG/1070517): Update this check to verify that this KeyEvent should
547   // be allowed on this page, instead of assuming that it should be allowed.
548   return true;
549 }
550 
EnableInputView()551 void InputMethodEngine::EnableInputView() {
552   input_method::InputMethodManager::Get()
553       ->GetActiveIMEState()
554       ->EnableInputView();
555   auto* keyboard_client = ChromeKeyboardControllerClient::Get();
556   if (keyboard_client->is_keyboard_enabled())
557     keyboard_client->ReloadKeyboardIfNeeded();
558 }
559 
560 
561 // TODO(uekawa): rename this method to a more reasonable name.
MenuItemToProperty(const input_method::InputMethodManager::MenuItem & item,ui::ime::InputMethodMenuItem * property)562 void InputMethodEngine::MenuItemToProperty(
563     const input_method::InputMethodManager::MenuItem& item,
564     ui::ime::InputMethodMenuItem* property) {
565   property->key = item.id;
566 
567   if (item.modified & MENU_ITEM_MODIFIED_LABEL) {
568     property->label = item.label;
569   }
570   if (item.modified & MENU_ITEM_MODIFIED_VISIBLE) {
571     // TODO(nona): Implement it.
572   }
573   if (item.modified & MENU_ITEM_MODIFIED_CHECKED) {
574     property->is_selection_item_checked = item.checked;
575   }
576   if (item.modified & MENU_ITEM_MODIFIED_ENABLED) {
577     // TODO(nona): implement sensitive entry(crbug.com/140192).
578   }
579   if (item.modified & MENU_ITEM_MODIFIED_STYLE) {
580     if (!item.children.empty()) {
581       // TODO(nona): Implement it.
582     } else {
583       switch (item.style) {
584         case input_method::InputMethodManager::MENU_ITEM_STYLE_NONE:
585           NOTREACHED();
586           break;
587         case input_method::InputMethodManager::MENU_ITEM_STYLE_CHECK:
588           // TODO(nona): Implement it.
589           break;
590         case input_method::InputMethodManager::MENU_ITEM_STYLE_RADIO:
591           property->is_selection_item = true;
592           break;
593         case input_method::InputMethodManager::MENU_ITEM_STYLE_SEPARATOR:
594           // TODO(nona): Implement it.
595           break;
596       }
597     }
598   }
599 
600   // TODO(nona): Support item.children.
601 }
602 
603 }  // namespace chromeos
604