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