1 // Copyright 2018 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/arc/input_method_manager/arc_input_method_manager_service.h"
6 
7 #include <memory>
8 #include <tuple>
9 #include <utility>
10 #include <vector>
11 
12 #include "ash/public/cpp/ash_pref_names.h"
13 #include "ash/public/cpp/keyboard/arc/arc_input_method_bounds_tracker.h"
14 #include "ash/public/cpp/keyboard/keyboard_switches.h"
15 #include "ash/public/cpp/tablet_mode.h"
16 #include "base/macros.h"
17 #include "base/memory/ptr_util.h"
18 #include "base/memory/scoped_refptr.h"
19 #include "base/run_loop.h"
20 #include "base/stl_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/test/scoped_command_line.h"
24 #include "chrome/browser/chromeos/arc/input_method_manager/test_input_method_manager_bridge.h"
25 #include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client_test_helper.h"
26 #include "chrome/common/pref_names.h"
27 #include "chrome/test/base/testing_profile.h"
28 #include "components/arc/arc_service_manager.h"
29 #include "components/arc/test/test_browser_context.h"
30 #include "components/crx_file/id_util.h"
31 #include "content/public/test/browser_task_environment.h"
32 #include "testing/gmock/include/gmock/gmock.h"
33 #include "testing/gtest/include/gtest/gtest.h"
34 #include "ui/base/ime/chromeos/extension_ime_util.h"
35 #include "ui/base/ime/chromeos/ime_bridge.h"
36 #include "ui/base/ime/chromeos/mock_ime_input_context_handler.h"
37 #include "ui/base/ime/chromeos/mock_input_method_manager.h"
38 #include "ui/base/ime/dummy_text_input_client.h"
39 #include "ui/base/ime/mock_input_method.h"
40 #include "ui/views/widget/widget.h"
41 
42 namespace arc {
43 namespace {
44 
45 namespace im = chromeos::input_method;
46 
GenerateImeInfo(const std::string & id,const std::string & name,const std::string & url,bool enabled,bool always_allowed)47 mojom::ImeInfoPtr GenerateImeInfo(const std::string& id,
48                                   const std::string& name,
49                                   const std::string& url,
50                                   bool enabled,
51                                   bool always_allowed) {
52   mojom::ImeInfoPtr info = mojom::ImeInfo::New();
53   info->ime_id = id;
54   info->display_name = name;
55   info->settings_url = url;
56   info->enabled = enabled;
57   info->is_allowed_in_clamshell_mode = always_allowed;
58   return info;
59 }
60 
61 class FakeTabletMode : public ash::TabletMode {
62  public:
63   FakeTabletMode() = default;
64   ~FakeTabletMode() override = default;
65 
66   // ash::TabletMode overrides:
AddObserver(ash::TabletModeObserver * observer)67   void AddObserver(ash::TabletModeObserver* observer) override {
68     observer_ = observer;
69   }
70 
RemoveObserver(ash::TabletModeObserver * observer)71   void RemoveObserver(ash::TabletModeObserver* observer) override {
72     observer_ = nullptr;
73   }
74 
InTabletMode() const75   bool InTabletMode() const override { return in_tablet_mode; }
76 
ForceUiTabletModeState(base::Optional<bool> enabled)77   void ForceUiTabletModeState(base::Optional<bool> enabled) override {}
78 
SetEnabledForTest(bool enabled)79   void SetEnabledForTest(bool enabled) override {
80     bool changed = (in_tablet_mode != enabled);
81     in_tablet_mode = enabled;
82 
83     if (changed && observer_) {
84       if (in_tablet_mode)
85         observer_->OnTabletModeStarted();
86       else
87         observer_->OnTabletModeEnded();
88     }
89   }
90 
91  private:
92   ash::TabletModeObserver* observer_ = nullptr;
93   bool in_tablet_mode = false;
94 };
95 
96 class FakeInputMethodBoundsObserver
97     : public ArcInputMethodManagerService::Observer {
98  public:
99   FakeInputMethodBoundsObserver() = default;
100   FakeInputMethodBoundsObserver(const FakeInputMethodBoundsObserver&) = delete;
101   ~FakeInputMethodBoundsObserver() override = default;
102 
Reset()103   void Reset() {
104     last_visibility_ = false;
105     visibility_changed_call_count_ = 0;
106   }
107 
last_visibility() const108   bool last_visibility() const { return last_visibility_; }
109 
visibility_changed_call_count() const110   int visibility_changed_call_count() const {
111     return visibility_changed_call_count_;
112   }
113 
114   // ArcInputMethodManagerService::Observer:
OnAndroidVirtualKeyboardVisibilityChanged(bool visible)115   void OnAndroidVirtualKeyboardVisibilityChanged(bool visible) override {
116     last_visibility_ = visible;
117     ++visibility_changed_call_count_;
118   }
119 
120  private:
121   bool last_visibility_ = false;
122   int visibility_changed_call_count_ = 0;
123 };
124 
125 // The fake im::InputMethodManager for testing.
126 class TestInputMethodManager : public im::MockInputMethodManager {
127  public:
128   // The fake im::InputMethodManager::State implementation for testing.
129   class TestState : public im::MockInputMethodManager::State {
130    public:
TestState()131     TestState()
132         : added_input_method_extensions_(), active_input_method_ids_() {}
133 
GetActiveInputMethodIds() const134     const std::vector<std::string>& GetActiveInputMethodIds() const override {
135       return active_input_method_ids_;
136     }
137 
GetCurrentInputMethod() const138     im::InputMethodDescriptor GetCurrentInputMethod() const override {
139       im::InputMethodDescriptor descriptor(
140           active_ime_id_, "", "", std::vector<std::string>(),
141           std::vector<std::string>(), false /* is_login_keyboard */, GURL(),
142           GURL());
143       return descriptor;
144     }
145 
AddInputMethodExtension(const std::string & extension_id,const im::InputMethodDescriptors & descriptors,ui::IMEEngineHandlerInterface * instance)146     void AddInputMethodExtension(
147         const std::string& extension_id,
148         const im::InputMethodDescriptors& descriptors,
149         ui::IMEEngineHandlerInterface* instance) override {
150       added_input_method_extensions_.push_back(
151           std::make_tuple(extension_id, descriptors, instance));
152     }
153 
RemoveInputMethodExtension(const std::string & extension_id)154     void RemoveInputMethodExtension(const std::string& extension_id) override {
155       removed_input_method_extensions_.push_back(extension_id);
156     }
157 
EnableInputMethod(const std::string & new_active_input_method_id)158     bool EnableInputMethod(
159         const std::string& new_active_input_method_id) override {
160       enabled_input_methods_.push_back(new_active_input_method_id);
161       return true;
162     }
163 
AddActiveInputMethodId(const std::string & ime_id)164     void AddActiveInputMethodId(const std::string& ime_id) {
165       if (!std::count(active_input_method_ids_.begin(),
166                       active_input_method_ids_.end(), ime_id)) {
167         active_input_method_ids_.push_back(ime_id);
168       }
169     }
170 
RemoveActiveInputMethodId(const std::string & ime_id)171     void RemoveActiveInputMethodId(const std::string& ime_id) {
172       base::EraseIf(active_input_method_ids_,
173                     [&ime_id](const std::string& id) { return id == ime_id; });
174     }
175 
SetActiveInputMethod(const std::string & ime_id)176     void SetActiveInputMethod(const std::string& ime_id) {
177       active_ime_id_ = ime_id;
178     }
179 
GetInputMethodExtensions(im::InputMethodDescriptors * descriptors)180     void GetInputMethodExtensions(
181         im::InputMethodDescriptors* descriptors) override {
182       for (const auto& id : active_input_method_ids_) {
183         descriptors->push_back(im::InputMethodDescriptor(
184             id, "", "", {}, {}, false, GURL(), GURL()));
185       }
186     }
187 
Reset()188     void Reset() {
189       added_input_method_extensions_.clear();
190       removed_input_method_extensions_.clear();
191       enabled_input_methods_.clear();
192     }
193 
194     std::vector<std::tuple<std::string,
195                            im::InputMethodDescriptors,
196                            ui::IMEEngineHandlerInterface*>>
197         added_input_method_extensions_;
198     std::vector<std::string> removed_input_method_extensions_;
199     std::vector<std::string> enabled_input_methods_;
200 
201    protected:
202     friend base::RefCounted<InputMethodManager::State>;
203     ~TestState() override = default;
204 
205    private:
206     std::vector<std::string> active_input_method_ids_;
207     std::string active_ime_id_;
208   };
209 
TestInputMethodManager()210   TestInputMethodManager() {
211     state_ = scoped_refptr<TestState>(new TestState());
212   }
213   ~TestInputMethodManager() override = default;
214 
GetActiveIMEState()215   scoped_refptr<InputMethodManager::State> GetActiveIMEState() override {
216     return state_;
217   }
218 
state()219   TestState* state() { return state_.get(); }
220 
221  private:
222   scoped_refptr<TestState> state_;
223 
224   DISALLOW_COPY_AND_ASSIGN(TestInputMethodManager);
225 };
226 
227 class TestIMEInputContextHandler : public ui::MockIMEInputContextHandler {
228  public:
TestIMEInputContextHandler(ui::InputMethod * input_method)229   explicit TestIMEInputContextHandler(ui::InputMethod* input_method)
230       : input_method_(input_method) {}
231 
GetInputMethod()232   ui::InputMethod* GetInputMethod() override { return input_method_; }
233 
234  private:
235   ui::InputMethod* const input_method_;
236 
237   DISALLOW_COPY_AND_ASSIGN(TestIMEInputContextHandler);
238 };
239 
240 class ArcInputMethodManagerServiceTest : public testing::Test {
241  protected:
ArcInputMethodManagerServiceTest()242   ArcInputMethodManagerServiceTest()
243       : arc_service_manager_(std::make_unique<ArcServiceManager>()) {}
244   ~ArcInputMethodManagerServiceTest() override = default;
245 
service()246   ArcInputMethodManagerService* service() { return service_; }
247 
bridge()248   TestInputMethodManagerBridge* bridge() { return test_bridge_; }
249 
imm()250   TestInputMethodManager* imm() { return input_method_manager_; }
251 
profile()252   TestingProfile* profile() { return profile_.get(); }
253 
ToggleTabletMode(bool enabled)254   void ToggleTabletMode(bool enabled) {
255     tablet_mode_controller_->SetEnabledForTest(enabled);
256   }
257 
NotifyNewBounds(const gfx::Rect & bounds)258   void NotifyNewBounds(const gfx::Rect& bounds) {
259     input_method_bounds_tracker_->NotifyArcInputMethodBoundsChanged(bounds);
260   }
261 
GetEnabledInputMethodIds()262   std::vector<std::string> GetEnabledInputMethodIds() {
263     return base::SplitString(
264         profile()->GetPrefs()->GetString(prefs::kLanguageEnabledImes), ",",
265         base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
266   }
267 
SetUp()268   void SetUp() override {
269     ui::IMEBridge::Initialize();
270     input_method_manager_ = new TestInputMethodManager();
271     chromeos::input_method::InputMethodManager::Initialize(
272         input_method_manager_);
273     profile_ = std::make_unique<TestingProfile>();
274 
275     tablet_mode_controller_ = std::make_unique<FakeTabletMode>();
276     input_method_bounds_tracker_ =
277         std::make_unique<ash::ArcInputMethodBoundsTracker>();
278 
279     chrome_keyboard_controller_client_test_helper_ =
280         ChromeKeyboardControllerClientTestHelper::InitializeWithFake();
281     chrome_keyboard_controller_client_test_helper_->SetProfile(profile_.get());
282 
283     service_ = ArcInputMethodManagerService::GetForBrowserContextForTesting(
284         profile_.get());
285     test_bridge_ = new TestInputMethodManagerBridge();
286     service_->SetInputMethodManagerBridgeForTesting(
287         base::WrapUnique(test_bridge_));
288   }
289 
TearDown()290   void TearDown() override {
291     test_bridge_ = nullptr;
292     service_->Shutdown();
293     chrome_keyboard_controller_client_test_helper_.reset();
294     input_method_bounds_tracker_.reset();
295     tablet_mode_controller_.reset();
296     profile_.reset();
297     chromeos::input_method::InputMethodManager::Shutdown();
298     ui::IMEBridge::Shutdown();
299   }
300 
301  private:
302   content::BrowserTaskEnvironment task_environment_;
303 
304   std::unique_ptr<ArcServiceManager> arc_service_manager_;
305   std::unique_ptr<TestingProfile> profile_;
306   std::unique_ptr<ChromeKeyboardControllerClientTestHelper>
307       chrome_keyboard_controller_client_test_helper_;
308   std::unique_ptr<FakeTabletMode> tablet_mode_controller_;
309   std::unique_ptr<ash::ArcInputMethodBoundsTracker>
310       input_method_bounds_tracker_;
311   TestInputMethodManager* input_method_manager_ = nullptr;
312   TestInputMethodManagerBridge* test_bridge_ = nullptr;  // Owned by |service_|
313   ArcInputMethodManagerService* service_ = nullptr;
314 
315   DISALLOW_COPY_AND_ASSIGN(ArcInputMethodManagerServiceTest);
316 };
317 
318 }  // anonymous namespace
319 
TEST_F(ArcInputMethodManagerServiceTest,EnableIme)320 TEST_F(ArcInputMethodManagerServiceTest, EnableIme) {
321   namespace ceiu = chromeos::extension_ime_util;
322   using crx_file::id_util::GenerateId;
323 
324   ToggleTabletMode(true);
325 
326   ASSERT_EQ(0u, bridge()->enable_ime_calls_.size());
327 
328   const std::string extension_ime_id =
329       ceiu::GetInputMethodID(GenerateId("test.extension.ime"), "us");
330   const std::string component_extension_ime_id =
331       ceiu::GetComponentInputMethodID(
332           GenerateId("test.component.extension.ime"), "us");
333   const std::string arc_ime_id =
334       ceiu::GetArcInputMethodID(GenerateId("test.arc.ime"), "us");
335 
336   // EnableIme is called only when ARC IME is enable or disabled.
337   imm()->state()->AddActiveInputMethodId(extension_ime_id);
338   service()->ImeMenuListChanged();
339   EXPECT_EQ(0u, bridge()->enable_ime_calls_.size());
340 
341   imm()->state()->AddActiveInputMethodId(component_extension_ime_id);
342   service()->ImeMenuListChanged();
343   EXPECT_EQ(0u, bridge()->enable_ime_calls_.size());
344 
345   // Enable the ARC IME and verify that EnableIme is called.
346   imm()->state()->AddActiveInputMethodId(arc_ime_id);
347   service()->ImeMenuListChanged();
348   ASSERT_EQ(1u, bridge()->enable_ime_calls_.size());
349   EXPECT_EQ(ceiu::GetComponentIDByInputMethodID(arc_ime_id),
350             std::get<std::string>(bridge()->enable_ime_calls_[0]));
351   EXPECT_TRUE(std::get<bool>(bridge()->enable_ime_calls_[0]));
352 
353   // Disable the ARC IME and verify that EnableIme is called with false.
354   imm()->state()->RemoveActiveInputMethodId(arc_ime_id);
355   service()->ImeMenuListChanged();
356   ASSERT_EQ(2u, bridge()->enable_ime_calls_.size());
357   EXPECT_EQ(ceiu::GetComponentIDByInputMethodID(arc_ime_id),
358             std::get<std::string>(bridge()->enable_ime_calls_[1]));
359   EXPECT_FALSE(std::get<bool>(bridge()->enable_ime_calls_[1]));
360 
361   // EnableIme is not called when non ARC IME is disabled.
362   imm()->state()->RemoveActiveInputMethodId(extension_ime_id);
363   service()->ImeMenuListChanged();
364   EXPECT_EQ(2u, bridge()->enable_ime_calls_.size());
365 }
366 
TEST_F(ArcInputMethodManagerServiceTest,EnableIme_WithPrefs)367 TEST_F(ArcInputMethodManagerServiceTest, EnableIme_WithPrefs) {
368   namespace ceiu = chromeos::extension_ime_util;
369   using crx_file::id_util::GenerateId;
370 
371   ToggleTabletMode(true);
372 
373   ASSERT_EQ(0u, bridge()->enable_ime_calls_.size());
374 
375   const std::string component_extension_ime_id =
376       ceiu::GetComponentInputMethodID(
377           GenerateId("test.component.extension.ime"), "us");
378   const std::string arc_ime_id =
379       ceiu::GetArcInputMethodID(GenerateId("test.arc.ime"), "us");
380 
381   imm()->state()->AddActiveInputMethodId(component_extension_ime_id);
382   service()->ImeMenuListChanged();
383   EXPECT_EQ(0u, bridge()->enable_ime_calls_.size());
384 
385   imm()->state()->AddActiveInputMethodId(arc_ime_id);
386   service()->ImeMenuListChanged();
387   ASSERT_EQ(1u, bridge()->enable_ime_calls_.size());
388 
389   // Test the case where |arc_ime_id| is temporarily disallowed because of the
390   // toggling to the laptop mode. In that case, the prefs still have the IME's
391   // ID.
392   profile()->GetPrefs()->SetString(
393       prefs::kLanguageEnabledImes,
394       base::StringPrintf("%s,%s", component_extension_ime_id.c_str(),
395                          arc_ime_id.c_str()));
396   imm()->state()->RemoveActiveInputMethodId(arc_ime_id);
397   service()->ImeMenuListChanged();
398   // Verify that EnableIme(id, false) is NOT called.
399   EXPECT_EQ(1u, bridge()->enable_ime_calls_.size());  // still 1u, not 2u.
400 }
401 
TEST_F(ArcInputMethodManagerServiceTest,SwitchImeTo)402 TEST_F(ArcInputMethodManagerServiceTest, SwitchImeTo) {
403   namespace ceiu = chromeos::extension_ime_util;
404   using crx_file::id_util::GenerateId;
405 
406   const std::string arc_ime_service_id =
407       "org.chromium.arc.ime/.ArcInputMethodService";
408 
409   ToggleTabletMode(true);
410 
411   ASSERT_EQ(0u, bridge()->switch_ime_to_calls_.size());
412 
413   const std::string extension_ime_id =
414       ceiu::GetInputMethodID(GenerateId("test.extension.ime"), "us");
415   const std::string component_extension_ime_id =
416       ceiu::GetComponentInputMethodID(
417           GenerateId("test.component.extension.ime"), "us");
418   const std::string arc_ime_id = ceiu::GetArcInputMethodID(
419       GenerateId("test.arc.ime"), "ime.id.in.arc.container");
420 
421   // Set active input method to the extension ime.
422   imm()->state()->SetActiveInputMethod(extension_ime_id);
423   service()->InputMethodChanged(imm(), nullptr, false /* show_message */);
424   // ArcImeService should be selected.
425   ASSERT_EQ(1u, bridge()->switch_ime_to_calls_.size());
426   EXPECT_EQ(arc_ime_service_id, bridge()->switch_ime_to_calls_[0]);
427 
428   // Set active input method to the component extension ime.
429   imm()->state()->SetActiveInputMethod(component_extension_ime_id);
430   service()->InputMethodChanged(imm(), nullptr, false /* show_message */);
431   // ArcImeService should be selected.
432   ASSERT_EQ(2u, bridge()->switch_ime_to_calls_.size());
433   EXPECT_EQ(arc_ime_service_id, bridge()->switch_ime_to_calls_[1]);
434 
435   // Set active input method to the arc ime.
436   imm()->state()->SetActiveInputMethod(arc_ime_id);
437   service()->InputMethodChanged(imm(), nullptr, false /* show_message */);
438   ASSERT_EQ(3u, bridge()->switch_ime_to_calls_.size());
439   EXPECT_EQ("ime.id.in.arc.container", bridge()->switch_ime_to_calls_[2]);
440 }
441 
TEST_F(ArcInputMethodManagerServiceTest,OnImeDisabled)442 TEST_F(ArcInputMethodManagerServiceTest, OnImeDisabled) {
443   namespace ceiu = chromeos::extension_ime_util;
444 
445   constexpr char kNonArcIme[] = "ime_a";
446   constexpr char kArcImeX[] = "arc_ime_x";
447   constexpr char kArcImeY[] = "arc_ime_y";
448   constexpr char kArcIMEProxyExtensionName[] =
449       "org.chromium.arc.inputmethod.proxy";
450 
451   const std::string proxy_ime_extension_id =
452       crx_file::id_util::GenerateId(kArcIMEProxyExtensionName);
453   const std::string arc_ime_x_component =
454       ceiu::GetArcInputMethodID(proxy_ime_extension_id, kArcImeX);
455   const std::string arc_ime_y_component =
456       ceiu::GetArcInputMethodID(proxy_ime_extension_id, kArcImeY);
457   mojom::ImeInfoPtr arc_ime_x = GenerateImeInfo(kArcImeX, "", "", false, false);
458   mojom::ImeInfoPtr arc_ime_y = GenerateImeInfo(kArcImeY, "", "", false, false);
459 
460   ToggleTabletMode(true);
461 
462   // Adding two ARC IMEs.
463   {
464     std::vector<mojom::ImeInfoPtr> info_array;
465     info_array.emplace_back(arc_ime_x.Clone());
466     info_array.emplace_back(arc_ime_y.Clone());
467     service()->OnImeInfoChanged(std::move(info_array));
468   }
469 
470   // Enable one non-ARC IME, then remove an ARC IME. This usually does not
471   // happen, but confirm that OnImeDisabled() does not do anything bad even
472   // if the IPC is called that way.
473   profile()->GetPrefs()->SetString(prefs::kLanguageEnabledImes, kNonArcIme);
474   service()->OnImeDisabled(kArcImeX);
475   EXPECT_EQ(kNonArcIme,
476             profile()->GetPrefs()->GetString(prefs::kLanguageEnabledImes));
477 
478   // Enable two IMEs (one non-ARC and one ARC), remove the ARC IME, and then
479   // confirm the non-ARC one remains.
480   arc_ime_x->enabled = true;
481   {
482     std::vector<mojom::ImeInfoPtr> info_array;
483     info_array.emplace_back(arc_ime_x.Clone());
484     info_array.emplace_back(arc_ime_y.Clone());
485     service()->OnImeInfoChanged(std::move(info_array));
486   }
487   std::string pref_str =
488       base::StringPrintf("%s,%s", kNonArcIme, arc_ime_x_component.c_str());
489   EXPECT_EQ(pref_str,
490             profile()->GetPrefs()->GetString(prefs::kLanguageEnabledImes));
491   service()->OnImeDisabled(kArcImeX);
492   EXPECT_EQ(kNonArcIme,
493             profile()->GetPrefs()->GetString(prefs::kLanguageEnabledImes));
494 
495   // Enable two ARC IMEs along with one non-ARC one, remove one of two ARC IMEs,
496   // then confirm one non-ARC IME and one ARC IME still remain.
497   arc_ime_y->enabled = true;
498   {
499     std::vector<mojom::ImeInfoPtr> info_array;
500     info_array.emplace_back(arc_ime_x.Clone());
501     info_array.emplace_back(arc_ime_y.Clone());
502     service()->OnImeInfoChanged(std::move(info_array));
503   }
504   pref_str =
505       base::StringPrintf("%s,%s,%s", kNonArcIme, arc_ime_x_component.c_str(),
506                          arc_ime_y_component.c_str());
507   EXPECT_EQ(pref_str,
508             profile()->GetPrefs()->GetString(prefs::kLanguageEnabledImes));
509   service()->OnImeDisabled(kArcImeX);
510   pref_str =
511       base::StringPrintf("%s,%s", kNonArcIme, arc_ime_y_component.c_str());
512   EXPECT_EQ(pref_str,
513             profile()->GetPrefs()->GetString(prefs::kLanguageEnabledImes));
514 }
515 
TEST_F(ArcInputMethodManagerServiceTest,OnImeInfoChanged)516 TEST_F(ArcInputMethodManagerServiceTest, OnImeInfoChanged) {
517   namespace ceiu = chromeos::extension_ime_util;
518 
519   ToggleTabletMode(true);
520 
521   // Preparing 2 ImeInfo.
522   const std::string android_ime_id1 = "test.arc.ime";
523   const std::string display_name1 = "DisplayName";
524   const std::string settings_url1 = "url_to_settings";
525   mojom::ImeInfoPtr info1 = GenerateImeInfo(android_ime_id1, display_name1,
526                                             settings_url1, false, false);
527 
528   const std::string android_ime_id2 = "test.arc.ime2";
529   const std::string display_name2 = "DisplayName2";
530   const std::string settings_url2 = "url_to_settings2";
531   mojom::ImeInfoPtr info2 = GenerateImeInfo(android_ime_id2, display_name2,
532                                             settings_url2, true, false);
533 
534   std::vector<
535       std::tuple<std::string, chromeos::input_method::InputMethodDescriptors,
536                  ui::IMEEngineHandlerInterface*>>& added_extensions =
537       imm()->state()->added_input_method_extensions_;
538   ASSERT_EQ(0u, added_extensions.size());
539 
540   {
541     // Passing empty info_array shouldn't call AddInputMethodExtension.
542     std::vector<mojom::ImeInfoPtr> info_array{};
543     service()->OnImeInfoChanged(std::move(info_array));
544     EXPECT_TRUE(added_extensions.empty());
545   }
546 
547   {
548     // Adding one ARC IME.
549     std::vector<mojom::ImeInfoPtr> info_array;
550     info_array.emplace_back(info1.Clone());
551     service()->OnImeInfoChanged(std::move(info_array));
552     ASSERT_EQ(1u, added_extensions.size());
553     ASSERT_EQ(1u, std::get<1>(added_extensions[0]).size());
554     EXPECT_EQ(android_ime_id1, ceiu::GetComponentIDByInputMethodID(
555                                    std::get<1>(added_extensions[0])[0].id()));
556     EXPECT_EQ(display_name1, std::get<1>(added_extensions[0])[0].name());
557     ASSERT_EQ(1u, std::get<1>(added_extensions[0])[0].language_codes().size());
558     EXPECT_TRUE(chromeos::extension_ime_util::IsArcIME(
559         std::get<1>(added_extensions[0])[0].id()));
560 
561     // Emulate enabling ARC IME from chrome://settings.
562     const std::string& arc_ime_id = std::get<1>(added_extensions[0])[0].id();
563     profile()->GetPrefs()->SetString(prefs::kLanguageEnabledImes, arc_ime_id);
564     EXPECT_EQ(arc_ime_id,
565               profile()->GetPrefs()->GetString(prefs::kLanguageEnabledImes));
566 
567     // Removing the ARC IME should clear the pref
568     std::vector<mojom::ImeInfoPtr> empty_info_array;
569     service()->OnImeInfoChanged(std::move(empty_info_array));
570     EXPECT_TRUE(
571         profile()->GetPrefs()->GetString(prefs::kLanguageEnabledImes).empty());
572     added_extensions.clear();
573   }
574 
575   {
576     // Adding two ARC IMEs. One is already enabled.
577     std::vector<mojom::ImeInfoPtr> info_array;
578     info_array.emplace_back(info1.Clone());
579     info_array.emplace_back(info2.Clone());
580     service()->OnImeInfoChanged(std::move(info_array));
581     // The ARC IMEs should be registered as two IMEs in one extension.
582     ASSERT_EQ(1u, added_extensions.size());
583     ASSERT_EQ(2u, std::get<1>(added_extensions[0]).size());
584     EXPECT_EQ(android_ime_id1, ceiu::GetComponentIDByInputMethodID(
585                                    std::get<1>(added_extensions[0])[0].id()));
586     EXPECT_EQ(display_name1, std::get<1>(added_extensions[0])[0].name());
587     EXPECT_EQ(android_ime_id2, ceiu::GetComponentIDByInputMethodID(
588                                    std::get<1>(added_extensions[0])[1].id()));
589     EXPECT_EQ(display_name2, std::get<1>(added_extensions[0])[1].name());
590 
591     // Already enabled IME should be added to the pref automatically.
592     const std::string& arc_ime_id2 = std::get<1>(added_extensions[0])[1].id();
593     EXPECT_EQ(arc_ime_id2,
594               profile()->GetPrefs()->GetString(prefs::kLanguageEnabledImes));
595 
596     added_extensions.clear();
597   }
598 }
599 
TEST_F(ArcInputMethodManagerServiceTest,EnableArcIMEsOnlyInTabletMode)600 TEST_F(ArcInputMethodManagerServiceTest, EnableArcIMEsOnlyInTabletMode) {
601   namespace ceiu = chromeos::extension_ime_util;
602   using crx_file::id_util::GenerateId;
603 
604   constexpr char kArcIMEProxyExtensionName[] =
605       "org.chromium.arc.inputmethod.proxy";
606 
607   const std::string extension_ime_id =
608       ceiu::GetInputMethodID(GenerateId("test.extension.ime"), "us");
609   const std::string component_extension_ime_id =
610       ceiu::GetComponentInputMethodID(
611           GenerateId("test.component.extension.ime"), "us");
612   const std::string proxy_ime_extension_id =
613       crx_file::id_util::GenerateId(kArcIMEProxyExtensionName);
614   const std::string android_ime_id = "test.arc.ime";
615   const std::string arc_ime_id =
616       ceiu::GetArcInputMethodID(proxy_ime_extension_id, android_ime_id);
617 
618   // Start from tablet mode.
619   ToggleTabletMode(true);
620 
621   // Activate the extension IME and the component extension IME.
622   imm()->state()->AddActiveInputMethodId(extension_ime_id);
623   imm()->state()->AddActiveInputMethodId(component_extension_ime_id);
624   // Update the prefs because the testee checks them.
625   profile()->GetPrefs()->SetString(
626       prefs::kLanguageEnabledImes,
627       base::StringPrintf("%s,%s", extension_ime_id.c_str(),
628                          component_extension_ime_id.c_str()));
629   service()->ImeMenuListChanged();
630 
631   imm()->state()->Reset();
632 
633   // Enable the ARC IME.
634   {
635     mojom::ImeInfoPtr info =
636         GenerateImeInfo(android_ime_id, "", "", true, false);
637     std::vector<mojom::ImeInfoPtr> info_array{};
638     info_array.emplace_back(info.Clone());
639     service()->OnImeInfoChanged(std::move(info_array));
640   }
641   // IMM should get the newly enabled IME id.
642   EXPECT_EQ(1u, imm()->state()->added_input_method_extensions_.size());
643   EXPECT_EQ(1u, imm()->state()->enabled_input_methods_.size());
644   EXPECT_EQ(arc_ime_id, imm()->state()->enabled_input_methods_.at(0));
645   imm()->state()->enabled_input_methods_.clear();
646   {
647     // Pref should get updated.
648     const auto enabled_ime_in_pref = GetEnabledInputMethodIds();
649     EXPECT_EQ(3u, enabled_ime_in_pref.size());
650     EXPECT_EQ(arc_ime_id, enabled_ime_in_pref.at(2));
651   }
652 
653   imm()->state()->Reset();
654 
655   // Change to laptop mode.
656   ToggleTabletMode(false);
657 
658   // ARC IME is not allowed in laptop mode.
659   // The fake IME extension is uninstalled.
660   EXPECT_EQ(1u, imm()->state()->removed_input_method_extensions_.size());
661   EXPECT_TRUE(imm()->state()->enabled_input_methods_.empty());
662   {
663     const auto enabled_ime_in_pref = GetEnabledInputMethodIds();
664     EXPECT_EQ(2u, enabled_ime_in_pref.size());
665   }
666 
667   imm()->state()->Reset();
668 
669   // Back to tablet mode.
670   ToggleTabletMode(true);
671 
672   // All IMEs are allowed to use.
673   EXPECT_EQ(1u, imm()->state()->added_input_method_extensions_.size());
674   EXPECT_EQ(1u, imm()->state()->enabled_input_methods_.size());
675   EXPECT_EQ(arc_ime_id, imm()->state()->enabled_input_methods_.at(0));
676   imm()->state()->enabled_input_methods_.clear();
677   {
678     const auto enabled_ime_in_pref = GetEnabledInputMethodIds();
679     EXPECT_EQ(3u, enabled_ime_in_pref.size());
680     EXPECT_EQ(arc_ime_id, enabled_ime_in_pref.at(2));
681   }
682 
683   imm()->state()->Reset();
684 
685   // Confirm that entering the same mode twice in a row is no-op.
686   ToggleTabletMode(true);
687   EXPECT_TRUE(imm()->state()->removed_input_method_extensions_.empty());
688   EXPECT_TRUE(imm()->state()->added_input_method_extensions_.empty());
689   EXPECT_TRUE(imm()->state()->enabled_input_methods_.empty());
690 
691   ToggleTabletMode(false);
692   EXPECT_EQ(1u, imm()->state()->removed_input_method_extensions_.size());
693   EXPECT_TRUE(imm()->state()->enabled_input_methods_.empty());
694   {
695     const auto enabled_ime_in_pref = GetEnabledInputMethodIds();
696     EXPECT_EQ(2u, enabled_ime_in_pref.size());
697   }
698 
699   ToggleTabletMode(false);
700   EXPECT_EQ(1u, imm()->state()->removed_input_method_extensions_.size());
701   EXPECT_TRUE(imm()->state()->enabled_input_methods_.empty());
702   {
703     const auto enabled_ime_in_pref = GetEnabledInputMethodIds();
704     EXPECT_EQ(2u, enabled_ime_in_pref.size());
705   }
706 }
707 
TEST_F(ArcInputMethodManagerServiceTest,RemoveArcIMEsWhenAccessibilityKeyboardEnabled)708 TEST_F(ArcInputMethodManagerServiceTest,
709        RemoveArcIMEsWhenAccessibilityKeyboardEnabled) {
710   namespace ceiu = chromeos::extension_ime_util;
711   using crx_file::id_util::GenerateId;
712 
713   constexpr char kArcIMEProxyExtensionName[] =
714       "org.chromium.arc.inputmethod.proxy";
715 
716   const std::string extension_ime_id =
717       ceiu::GetInputMethodID(GenerateId("test.extension.ime"), "us");
718   const std::string component_extension_ime_id =
719       ceiu::GetComponentInputMethodID(
720           GenerateId("test.component.extension.ime"), "us");
721   const std::string proxy_ime_extension_id =
722       crx_file::id_util::GenerateId(kArcIMEProxyExtensionName);
723   const std::string android_ime_id = "test.arc.ime";
724   const std::string arc_ime_id =
725       ceiu::GetArcInputMethodID(proxy_ime_extension_id, android_ime_id);
726 
727   // Start from tablet mode.
728   ToggleTabletMode(true);
729 
730   // Activate the extension IME and the component extension IME.
731   imm()->state()->AddActiveInputMethodId(extension_ime_id);
732   imm()->state()->AddActiveInputMethodId(component_extension_ime_id);
733   // Update the prefs because the testee checks them.
734   profile()->GetPrefs()->SetString(
735       prefs::kLanguageEnabledImes,
736       base::StringPrintf("%s,%s", extension_ime_id.c_str(),
737                          component_extension_ime_id.c_str()));
738   service()->ImeMenuListChanged();
739 
740   imm()->state()->Reset();
741 
742   // All IMEs are allowed to use.
743   // Enable the ARC IME.
744   {
745     mojom::ImeInfoPtr info =
746         GenerateImeInfo(android_ime_id, "", "", true, false);
747     std::vector<mojom::ImeInfoPtr> info_array{};
748     info_array.emplace_back(info.Clone());
749     service()->OnImeInfoChanged(std::move(info_array));
750   }
751   // IMM should get the newly enabled IME id.
752   EXPECT_EQ(1u, imm()->state()->added_input_method_extensions_.size());
753   EXPECT_EQ(1u, imm()->state()->enabled_input_methods_.size());
754   EXPECT_EQ(arc_ime_id, imm()->state()->enabled_input_methods_.at(0));
755   imm()->state()->enabled_input_methods_.clear();
756   {
757     // Pref should get updated.
758     const auto enabled_ime_in_pref = GetEnabledInputMethodIds();
759     EXPECT_EQ(3u, enabled_ime_in_pref.size());
760     EXPECT_EQ(arc_ime_id, enabled_ime_in_pref.at(2));
761   }
762 
763   imm()->state()->Reset();
764 
765   // Enable a11y keyboard option.
766   profile()->GetPrefs()->SetBoolean(
767       ash::prefs::kAccessibilityVirtualKeyboardEnabled, true);
768   // Notify ArcInputMethodManagerService.
769   service()->OnAccessibilityStatusChanged(
770       {chromeos::ACCESSIBILITY_TOGGLE_VIRTUAL_KEYBOARD, true});
771 
772   // ARC IME is not allowed when a11y keyboard is enabled.
773   EXPECT_EQ(1u, imm()->state()->removed_input_method_extensions_.size());
774   EXPECT_TRUE(imm()->state()->enabled_input_methods_.empty());
775   {
776     const auto enabled_ime_in_pref = GetEnabledInputMethodIds();
777     EXPECT_EQ(2u, enabled_ime_in_pref.size());
778   }
779 
780   imm()->state()->removed_input_method_extensions_.clear();
781   imm()->state()->added_input_method_extensions_.clear();
782   imm()->state()->enabled_input_methods_.clear();
783 
784   // Disable a11y keyboard option.
785   profile()->GetPrefs()->SetBoolean(
786       ash::prefs::kAccessibilityVirtualKeyboardEnabled, false);
787   // Notify ArcInputMethodManagerService.
788   service()->OnAccessibilityStatusChanged(
789       {chromeos::ACCESSIBILITY_TOGGLE_VIRTUAL_KEYBOARD, false});
790 
791   // ARC IME can be enabled.
792   EXPECT_EQ(1u, imm()->state()->added_input_method_extensions_.size());
793   EXPECT_EQ(1u, imm()->state()->enabled_input_methods_.size());
794   EXPECT_EQ(arc_ime_id, imm()->state()->enabled_input_methods_.at(0));
795   imm()->state()->enabled_input_methods_.clear();
796   {
797     // Pref should get updated.
798     const auto enabled_ime_in_pref = GetEnabledInputMethodIds();
799     EXPECT_EQ(3u, enabled_ime_in_pref.size());
800     EXPECT_EQ(arc_ime_id, enabled_ime_in_pref.at(2));
801   }
802 }
803 
TEST_F(ArcInputMethodManagerServiceTest,AllowArcIMEsWhileCommandLineFlagIsSet)804 TEST_F(ArcInputMethodManagerServiceTest,
805        AllowArcIMEsWhileCommandLineFlagIsSet) {
806   namespace ceiu = chromeos::extension_ime_util;
807   using crx_file::id_util::GenerateId;
808 
809   constexpr char kArcIMEProxyExtensionName[] =
810       "org.chromium.arc.inputmethod.proxy";
811 
812   const std::string extension_ime_id =
813       ceiu::GetInputMethodID(GenerateId("test.extension.ime"), "us");
814   const std::string component_extension_ime_id =
815       ceiu::GetComponentInputMethodID(
816           GenerateId("test.component.extension.ime"), "us");
817   const std::string proxy_ime_extension_id =
818       crx_file::id_util::GenerateId(kArcIMEProxyExtensionName);
819   const std::string android_ime_id = "test.arc.ime";
820   const std::string arc_ime_id =
821       ceiu::GetArcInputMethodID(proxy_ime_extension_id, android_ime_id);
822 
823   // Add '--enable-virtual-keyboard' flag.
824   base::test::ScopedCommandLine scoped_command_line;
825   base::CommandLine* command_line = scoped_command_line.GetProcessCommandLine();
826   command_line->AppendSwitch(keyboard::switches::kEnableVirtualKeyboard);
827 
828   // Start from tablet mode.
829   ToggleTabletMode(true);
830 
831   // Activate the extension IME and the component extension IME.
832   imm()->state()->AddActiveInputMethodId(extension_ime_id);
833   imm()->state()->AddActiveInputMethodId(component_extension_ime_id);
834   // Update the prefs because the testee checks them.
835   profile()->GetPrefs()->SetString(
836       prefs::kLanguageEnabledImes,
837       base::StringPrintf("%s,%s", extension_ime_id.c_str(),
838                          component_extension_ime_id.c_str()));
839   service()->ImeMenuListChanged();
840 
841   imm()->state()->removed_input_method_extensions_.clear();
842   imm()->state()->added_input_method_extensions_.clear();
843   imm()->state()->enabled_input_methods_.clear();
844 
845   // Enable the ARC IME.
846   {
847     mojom::ImeInfoPtr info =
848         GenerateImeInfo(android_ime_id, "", "", true, false);
849     std::vector<mojom::ImeInfoPtr> info_array{};
850     info_array.emplace_back(info.Clone());
851     service()->OnImeInfoChanged(std::move(info_array));
852   }
853   // IMM should get the newly enabled IME id.
854   EXPECT_EQ(1u, imm()->state()->added_input_method_extensions_.size());
855   EXPECT_EQ(1u, imm()->state()->enabled_input_methods_.size());
856   EXPECT_EQ(arc_ime_id, imm()->state()->enabled_input_methods_.at(0));
857   {
858     // Pref should get updated.
859     const auto enabled_ime_in_pref = GetEnabledInputMethodIds();
860     EXPECT_EQ(3u, enabled_ime_in_pref.size());
861     EXPECT_EQ(arc_ime_id, enabled_ime_in_pref.at(2));
862   }
863 
864   imm()->state()->removed_input_method_extensions_.clear();
865   imm()->state()->added_input_method_extensions_.clear();
866   imm()->state()->enabled_input_methods_.clear();
867 
868   // Change to laptop mode.
869   ToggleTabletMode(false);
870 
871   // All IMEs are allowed to use even in laptop mode if the flag is set.
872   EXPECT_EQ(1u, imm()->state()->added_input_method_extensions_.size());
873   EXPECT_EQ(1u, imm()->state()->enabled_input_methods_.size());
874   EXPECT_EQ(arc_ime_id, imm()->state()->enabled_input_methods_.at(0));
875   {
876     const auto enabled_ime_in_pref = GetEnabledInputMethodIds();
877     EXPECT_EQ(3u, enabled_ime_in_pref.size());
878     EXPECT_EQ(arc_ime_id, enabled_ime_in_pref.at(2));
879   }
880 }
881 
TEST_F(ArcInputMethodManagerServiceTest,FocusAndBlur)882 TEST_F(ArcInputMethodManagerServiceTest, FocusAndBlur) {
883   ToggleTabletMode(true);
884 
885   // Adding one ARC IME.
886   {
887     const std::string android_ime_id = "test.arc.ime";
888     const std::string display_name = "DisplayName";
889     const std::string settings_url = "url_to_settings";
890     mojom::ImeInfoPtr info = GenerateImeInfo(android_ime_id, display_name,
891                                              settings_url, false, false);
892 
893     std::vector<mojom::ImeInfoPtr> info_array;
894     info_array.emplace_back(std::move(info));
895     service()->OnImeInfoChanged(std::move(info_array));
896   }
897   // The proxy IME engine should be added.
898   ASSERT_EQ(1u, imm()->state()->added_input_method_extensions_.size());
899   ui::IMEEngineHandlerInterface* engine_handler =
900       std::get<2>(imm()->state()->added_input_method_extensions_.at(0));
901 
902   // Set up mock input context.
903   constexpr int test_context_id = 0;
904   const ui::IMEEngineHandlerInterface::InputContext test_context{
905       test_context_id,
906       ui::TEXT_INPUT_TYPE_TEXT,
907       ui::TEXT_INPUT_MODE_DEFAULT,
908       0 /* flags */,
909       ui::TextInputClient::FOCUS_REASON_MOUSE,
910       true /* should_do_learning */};
911   ui::MockInputMethod mock_input_method(nullptr);
912   TestIMEInputContextHandler test_context_handler(&mock_input_method);
913   ui::DummyTextInputClient dummy_text_input_client(ui::TEXT_INPUT_TYPE_TEXT);
914   ui::IMEBridge::Get()->SetInputContextHandler(&test_context_handler);
915 
916   // Enable the ARC IME.
917   ui::IMEBridge::Get()->SetCurrentEngineHandler(engine_handler);
918   engine_handler->Enable(
919       chromeos::extension_ime_util::GetComponentIDByInputMethodID(
920           std::get<1>(imm()->state()->added_input_method_extensions_.at(0))
921               .at(0)
922               .id()));
923   mock_input_method.SetFocusedTextInputClient(&dummy_text_input_client);
924 
925   ASSERT_EQ(0, bridge()->focus_calls_count_);
926 
927   engine_handler->FocusIn(test_context);
928   EXPECT_EQ(1, bridge()->focus_calls_count_);
929 
930   engine_handler->FocusOut();
931   EXPECT_EQ(1, bridge()->focus_calls_count_);
932 }
933 
TEST_F(ArcInputMethodManagerServiceTest,DisableFallbackVirtualKeyboard)934 TEST_F(ArcInputMethodManagerServiceTest, DisableFallbackVirtualKeyboard) {
935   namespace ceiu = chromeos::extension_ime_util;
936   using crx_file::id_util::GenerateId;
937 
938   ToggleTabletMode(true);
939 
940   const std::string extension_ime_id =
941       ceiu::GetInputMethodID(GenerateId("test.extension.ime"), "us");
942   const std::string component_extension_ime_id =
943       ceiu::GetComponentInputMethodID(
944           GenerateId("test.component.extension.ime"), "us");
945   const std::string arc_ime_id = ceiu::GetArcInputMethodID(
946       GenerateId("test.arc.ime"), "ime.id.in.arc.container");
947 
948   // Set active input method to the extension ime.
949   imm()->state()->SetActiveInputMethod(extension_ime_id);
950   service()->InputMethodChanged(imm(), profile(), false /* show_message */);
951 
952   // Enable Chrome OS virtual keyboard
953   auto* client = ChromeKeyboardControllerClient::Get();
954   client->ClearEnableFlag(keyboard::KeyboardEnableFlag::kAndroidDisabled);
955   client->SetEnableFlag(keyboard::KeyboardEnableFlag::kTouchEnabled);
956   base::RunLoop().RunUntilIdle();  // Allow observers to fire and process.
957   ASSERT_FALSE(
958       client->IsEnableFlagSet(keyboard::KeyboardEnableFlag::kAndroidDisabled));
959 
960   // It's disabled when the ARC IME is activated.
961   imm()->state()->SetActiveInputMethod(arc_ime_id);
962   service()->InputMethodChanged(imm(), profile(), false);
963   EXPECT_TRUE(
964       client->IsEnableFlagSet(keyboard::KeyboardEnableFlag::kAndroidDisabled));
965 
966   // It's re-enabled when the ARC IME is deactivated.
967   imm()->state()->SetActiveInputMethod(component_extension_ime_id);
968   service()->InputMethodChanged(imm(), profile(), false);
969   EXPECT_FALSE(
970       client->IsEnableFlagSet(keyboard::KeyboardEnableFlag::kAndroidDisabled));
971 }
972 
TEST_F(ArcInputMethodManagerServiceTest,ShowVirtualKeyboard)973 TEST_F(ArcInputMethodManagerServiceTest, ShowVirtualKeyboard) {
974   ToggleTabletMode(true);
975 
976   // Adding one ARC IME.
977   {
978     const std::string android_ime_id = "test.arc.ime";
979     const std::string display_name = "DisplayName";
980     const std::string settings_url = "url_to_settings";
981     mojom::ImeInfoPtr info = GenerateImeInfo(android_ime_id, display_name,
982                                              settings_url, false, false);
983 
984     std::vector<mojom::ImeInfoPtr> info_array;
985     info_array.emplace_back(std::move(info));
986     service()->OnImeInfoChanged(std::move(info_array));
987   }
988   // The proxy IME engine should be added.
989   ASSERT_EQ(1u, imm()->state()->added_input_method_extensions_.size());
990   ui::IMEEngineHandlerInterface* engine_handler =
991       std::get<2>(imm()->state()->added_input_method_extensions_.at(0));
992 
993   // Set up mock input context.
994   constexpr int test_context_id = 0;
995   const ui::IMEEngineHandlerInterface::InputContext test_context{
996       test_context_id,
997       ui::TEXT_INPUT_TYPE_TEXT,
998       ui::TEXT_INPUT_MODE_DEFAULT,
999       0 /* flags */,
1000       ui::TextInputClient::FOCUS_REASON_MOUSE,
1001       true /* should_do_learning */};
1002   ui::MockInputMethod mock_input_method(nullptr);
1003   TestIMEInputContextHandler test_context_handler(&mock_input_method);
1004   ui::DummyTextInputClient dummy_text_input_client(ui::TEXT_INPUT_TYPE_TEXT);
1005   ui::IMEBridge::Get()->SetInputContextHandler(&test_context_handler);
1006 
1007   // Enable the ARC IME.
1008   ui::IMEBridge::Get()->SetCurrentEngineHandler(engine_handler);
1009   engine_handler->Enable(
1010       chromeos::extension_ime_util::GetComponentIDByInputMethodID(
1011           std::get<1>(imm()->state()->added_input_method_extensions_.at(0))
1012               .at(0)
1013               .id()));
1014 
1015   mock_input_method.SetFocusedTextInputClient(&dummy_text_input_client);
1016 
1017   EXPECT_EQ(0, bridge()->show_virtual_keyboard_calls_count_);
1018   mock_input_method.ShowVirtualKeyboardIfEnabled();
1019   EXPECT_EQ(1, bridge()->show_virtual_keyboard_calls_count_);
1020   ui::IMEBridge::Get()->SetInputContextHandler(nullptr);
1021   ui::IMEBridge::Get()->SetCurrentEngineHandler(nullptr);
1022 }
1023 
TEST_F(ArcInputMethodManagerServiceTest,VisibilityObserver)1024 TEST_F(ArcInputMethodManagerServiceTest, VisibilityObserver) {
1025   ToggleTabletMode(true);
1026 
1027   FakeInputMethodBoundsObserver observer;
1028   service()->AddObserver(&observer);
1029   ASSERT_FALSE(observer.last_visibility());
1030   ASSERT_EQ(0, observer.visibility_changed_call_count());
1031 
1032   // Notify new non-empty bounds not when ARC IME is active.
1033   NotifyNewBounds(gfx::Rect(0, 0, 100, 100));
1034   // It should not cause visibility changed event.
1035   EXPECT_FALSE(observer.last_visibility());
1036   EXPECT_EQ(0, observer.visibility_changed_call_count());
1037 
1038   NotifyNewBounds(gfx::Rect(0, 0, 0, 0));
1039   EXPECT_FALSE(observer.last_visibility());
1040   EXPECT_EQ(0, observer.visibility_changed_call_count());
1041   observer.Reset();
1042 
1043   // Adding one ARC IME.
1044   {
1045     const std::string android_ime_id = "test.arc.ime";
1046     const std::string display_name = "DisplayName";
1047     const std::string settings_url = "url_to_settings";
1048     mojom::ImeInfoPtr info = GenerateImeInfo(android_ime_id, display_name,
1049                                              settings_url, false, false);
1050     info->ime_id = android_ime_id;
1051     info->display_name = display_name;
1052     info->enabled = false;
1053     info->settings_url = settings_url;
1054 
1055     std::vector<mojom::ImeInfoPtr> info_array;
1056     info_array.emplace_back(std::move(info));
1057     service()->OnImeInfoChanged(std::move(info_array));
1058   }
1059   // The proxy IME engine should be added.
1060   ASSERT_EQ(1u, imm()->state()->added_input_method_extensions_.size());
1061   ui::IMEEngineHandlerInterface* engine_handler =
1062       std::get<2>(imm()->state()->added_input_method_extensions_.at(0));
1063 
1064   // Set up mock input context.
1065   constexpr int test_context_id = 0;
1066   const ui::IMEEngineHandlerInterface::InputContext test_context{
1067       test_context_id,
1068       ui::TEXT_INPUT_TYPE_TEXT,
1069       ui::TEXT_INPUT_MODE_DEFAULT,
1070       0 /* flags */,
1071       ui::TextInputClient::FOCUS_REASON_MOUSE,
1072       true /* should_do_learning */};
1073   ui::MockInputMethod mock_input_method(nullptr);
1074   TestIMEInputContextHandler test_context_handler(&mock_input_method);
1075   ui::DummyTextInputClient dummy_text_input_client(ui::TEXT_INPUT_TYPE_TEXT);
1076   ui::IMEBridge::Get()->SetInputContextHandler(&test_context_handler);
1077 
1078   // Enable the ARC IME.
1079   ui::IMEBridge::Get()->SetCurrentEngineHandler(engine_handler);
1080   engine_handler->Enable(
1081       chromeos::extension_ime_util::GetComponentIDByInputMethodID(
1082           std::get<1>(imm()->state()->added_input_method_extensions_.at(0))
1083               .at(0)
1084               .id()));
1085   mock_input_method.SetFocusedTextInputClient(&dummy_text_input_client);
1086 
1087   // Notify non-empty bounds should cause a visibility changed event now.
1088   NotifyNewBounds(gfx::Rect(0, 0, 100, 100));
1089   EXPECT_TRUE(observer.last_visibility());
1090   EXPECT_EQ(1, observer.visibility_changed_call_count());
1091   // A visibility changed event won't be sent if only size is changed.
1092   NotifyNewBounds(gfx::Rect(0, 0, 200, 200));
1093   EXPECT_TRUE(observer.last_visibility());
1094   EXPECT_EQ(1, observer.visibility_changed_call_count());
1095 
1096   NotifyNewBounds(gfx::Rect(0, 0, 0, 0));
1097   EXPECT_FALSE(observer.last_visibility());
1098   EXPECT_EQ(2, observer.visibility_changed_call_count());
1099 
1100   service()->RemoveObserver(&observer);
1101 }
1102 
1103 }  // namespace arc
1104