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 <algorithm> 6 7 #include "ash/shell.h" 8 #include "chrome/browser/chromeos/input_method/mode_indicator_controller.h" 9 #include "chrome/test/base/in_process_browser_test.h" 10 #include "chromeos/ime/component_extension_ime_manager.h" 11 #include "chromeos/ime/input_method_manager.h" 12 #include "content/public/test/browser_test_utils.h" 13 #include "content/public/test/test_utils.h" 14 #include "testing/gtest/include/gtest/gtest.h" 15 #include "ui/base/ime/chromeos/ibus_bridge.h" 16 #include "ui/base/ime/input_method_factory.h" 17 #include "ui/views/widget/widget.h" 18 #include "ui/views/widget/widget_observer.h" 19 20 namespace chromeos { 21 namespace input_method { 22 23 class ScopedModeIndicatorObserverForTesting : 24 public ModeIndicatorObserverInterface { 25 public: 26 ScopedModeIndicatorObserverForTesting() 27 : max_widget_list_size_(0) { 28 ModeIndicatorController::SetModeIndicatorObserverForTesting(this); 29 } 30 31 virtual ~ScopedModeIndicatorObserverForTesting() { 32 for (size_t i = 0; i < widget_list_.size(); ++i) { 33 widget_list_[i]->RemoveObserver(this); 34 } 35 ModeIndicatorController::SetModeIndicatorObserverForTesting(NULL); 36 } 37 38 gfx::Rect last_bounds() const { 39 return last_bounds_; 40 } 41 42 bool is_displayed() const { 43 return is_displayed_; 44 } 45 46 const std::vector<views::Widget*>& widget_list() const { 47 return widget_list_; 48 } 49 50 size_t widget_list_size() const { 51 return widget_list_.size(); 52 } 53 54 size_t max_widget_list_size() const { 55 return max_widget_list_size_; 56 } 57 58 // ModeIndicatorObserverInterface override: 59 virtual void AddModeIndicatorWidget(views::Widget* widget) OVERRIDE { 60 widget_list_.push_back(widget); 61 max_widget_list_size_ = 62 std::max(max_widget_list_size_, widget_list_.size()); 63 widget->AddObserver(this); 64 } 65 66 // views::WidgetObserver override: 67 virtual void OnWidgetDestroying(views::Widget* widget) OVERRIDE { 68 std::vector<views::Widget*>::iterator it = 69 std::find(widget_list_.begin(), widget_list_.end(), widget); 70 if (it != widget_list_.end()) 71 widget_list_.erase(it); 72 } 73 74 // views::WidgetObserver override: 75 virtual void OnWidgetVisibilityChanged(views::Widget* widget, 76 bool visible) OVERRIDE { 77 last_bounds_ = widget->GetWindowBoundsInScreen(); 78 is_displayed_ |= visible; 79 } 80 81 private: 82 bool is_displayed_; 83 gfx::Rect last_bounds_; 84 size_t max_widget_list_size_; 85 std::vector<views::Widget*> widget_list_; 86 }; 87 88 class ModeIndicatorBrowserTest : public InProcessBrowserTest { 89 public: 90 ModeIndicatorBrowserTest() 91 : InProcessBrowserTest() {} 92 virtual ~ModeIndicatorBrowserTest() {} 93 94 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { 95 ui::SetUpInputMethodFactoryForTesting(); 96 } 97 98 void InitializeIMF() { 99 // Make sure ComponentExtensionIMEManager is initialized. 100 // ComponentExtensionIMEManagerImpl::InitializeAsync posts 101 // ReadComponentExtensionsInfo to the FILE thread for the 102 // initialization. If it is never initialized for some reasons, 103 // the test is timed out and failed. 104 ComponentExtensionIMEManager* ceimm = 105 InputMethodManager::Get()->GetComponentExtensionIMEManager(); 106 while (!ceimm->IsInitialized()) { 107 content::RunAllPendingInMessageLoop(content::BrowserThread::FILE); 108 } 109 } 110 111 private: 112 DISALLOW_COPY_AND_ASSIGN(ModeIndicatorBrowserTest); 113 }; 114 115 namespace { 116 // 43 is the designed size of the inner contents. 117 // This value corresponds with kMinSize defined in 118 // mode_indicator_delegate_view.cc. 119 const int kInnerSize = 43; 120 } // namespace 121 122 IN_PROC_BROWSER_TEST_F(ModeIndicatorBrowserTest, Bounds) { 123 InitializeIMF(); 124 125 InputMethodManager* imm = InputMethodManager::Get(); 126 ASSERT_TRUE(imm); 127 128 // Add keyboard layouts to enable the mode indicator. 129 imm->EnableLayouts("fr", "xkb:fr::fra"); 130 ASSERT_LT(1UL, imm->GetNumActiveInputMethods()); 131 132 chromeos::IBusPanelCandidateWindowHandlerInterface* candidate_window = 133 chromeos::IBusBridge::Get()->GetCandidateWindowHandler(); 134 candidate_window->FocusStateChanged(true); 135 136 // Check if the size of the mode indicator is expected. 137 gfx::Rect cursor1_bounds(100, 100, 1, 20); 138 gfx::Rect mi1_bounds; 139 { 140 ScopedModeIndicatorObserverForTesting observer; 141 candidate_window->SetCursorBounds(cursor1_bounds, cursor1_bounds); 142 EXPECT_TRUE(imm->SwitchToNextInputMethod()); 143 mi1_bounds = observer.last_bounds(); 144 // The bounds should be bigger than the inner size. 145 EXPECT_LE(kInnerSize, mi1_bounds.width()); 146 EXPECT_LE(kInnerSize, mi1_bounds.height()); 147 EXPECT_TRUE(observer.is_displayed()); 148 } 149 150 // Check if the location of the mode indicator is coresponded to 151 // the cursor bounds. 152 gfx::Rect cursor2_bounds(50, 200, 1, 20); 153 gfx::Rect mi2_bounds; 154 { 155 ScopedModeIndicatorObserverForTesting observer; 156 candidate_window->SetCursorBounds(cursor2_bounds, cursor2_bounds); 157 EXPECT_TRUE(imm->SwitchToNextInputMethod()); 158 mi2_bounds = observer.last_bounds(); 159 EXPECT_TRUE(observer.is_displayed()); 160 } 161 162 EXPECT_EQ(cursor1_bounds.x() - cursor2_bounds.x(), 163 mi1_bounds.x() - mi2_bounds.x()); 164 EXPECT_EQ(cursor1_bounds.y() - cursor2_bounds.y(), 165 mi1_bounds.y() - mi2_bounds.y()); 166 EXPECT_EQ(mi1_bounds.width(), mi2_bounds.width()); 167 EXPECT_EQ(mi1_bounds.height(), mi2_bounds.height()); 168 169 const gfx::Rect screen_bounds = 170 ash::Shell::GetScreen()->GetDisplayMatching(cursor1_bounds).work_area(); 171 172 // Check if the location of the mode indicator is concidered with 173 // the screen size. 174 const gfx::Rect cursor3_bounds(100, screen_bounds.bottom() - 25, 1, 20); 175 gfx::Rect mi3_bounds; 176 { 177 ScopedModeIndicatorObserverForTesting observer; 178 candidate_window->SetCursorBounds(cursor3_bounds, cursor3_bounds); 179 EXPECT_TRUE(imm->SwitchToNextInputMethod()); 180 mi3_bounds = observer.last_bounds(); 181 EXPECT_TRUE(observer.is_displayed()); 182 EXPECT_LT(mi3_bounds.bottom(), screen_bounds.bottom()); 183 } 184 } 185 186 IN_PROC_BROWSER_TEST_F(ModeIndicatorBrowserTest, NumOfWidgets) { 187 InitializeIMF(); 188 189 InputMethodManager* imm = InputMethodManager::Get(); 190 ASSERT_TRUE(imm); 191 192 // Add keyboard layouts to enable the mode indicator. 193 imm->EnableLayouts("fr", "xkb:fr::fra"); 194 ASSERT_LT(1UL, imm->GetNumActiveInputMethods()); 195 196 chromeos::IBusPanelCandidateWindowHandlerInterface* candidate_window = 197 chromeos::IBusBridge::Get()->GetCandidateWindowHandler(); 198 candidate_window->FocusStateChanged(true); 199 200 { 201 ScopedModeIndicatorObserverForTesting observer; 202 203 EXPECT_TRUE(imm->SwitchToNextInputMethod()); 204 EXPECT_EQ(1UL, observer.max_widget_list_size()); 205 const views::Widget* widget1 = observer.widget_list()[0]; 206 207 EXPECT_TRUE(imm->SwitchToNextInputMethod()); 208 EXPECT_EQ(2UL, observer.max_widget_list_size()); 209 210 // When a new mode indicator is displayed, the previous one should be 211 // closed. 212 content::RunAllPendingInMessageLoop(); 213 EXPECT_EQ(1UL, observer.widget_list_size()); 214 const views::Widget* widget2 = observer.widget_list()[0]; 215 EXPECT_NE(widget1, widget2); 216 } 217 } 218 } // namespace input_method 219 } // namespace chromeos 220