1 // Copyright (c) 2012 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/login/screen_locker.h" 6 7 #include "ash/wm/window_state.h" 8 #include "base/command_line.h" 9 #include "base/memory/scoped_ptr.h" 10 #include "base/message_loop/message_loop.h" 11 #include "chrome/browser/chrome_notification_types.h" 12 #include "chrome/browser/chromeos/login/mock_authenticator.h" 13 #include "chrome/browser/chromeos/login/screen_locker_tester.h" 14 #include "chrome/browser/chromeos/login/user_manager.h" 15 #include "chrome/browser/profiles/profile_manager.h" 16 #include "chrome/browser/ui/browser.h" 17 #include "chrome/browser/ui/browser_window.h" 18 #include "chrome/browser/ui/fullscreen/fullscreen_controller.h" 19 #include "chrome/browser/ui/tabs/tab_strip_model.h" 20 #include "chrome/common/chrome_switches.h" 21 #include "chrome/test/base/in_process_browser_test.h" 22 #include "chrome/test/base/ui_test_utils.h" 23 #include "chromeos/chromeos_switches.h" 24 #include "chromeos/dbus/fake_dbus_thread_manager.h" 25 #include "chromeos/dbus/fake_session_manager_client.h" 26 #include "content/public/browser/notification_service.h" 27 #include "testing/gmock/include/gmock/gmock.h" 28 #include "testing/gtest/include/gtest/gtest.h" 29 #include "ui/base/test/ui_controls.h" 30 #include "ui/compositor/layer_animator.h" 31 #include "ui/compositor/scoped_animation_duration_scale_mode.h" 32 #include "ui/views/widget/widget.h" 33 34 using testing::_; 35 using testing::AnyNumber; 36 using testing::Return; 37 38 namespace { 39 40 // An object that wait for lock state and fullscreen state. 41 class Waiter : public content::NotificationObserver { 42 public: 43 explicit Waiter(Browser* browser) 44 : browser_(browser), 45 running_(false) { 46 registrar_.Add(this, 47 chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED, 48 content::NotificationService::AllSources()); 49 registrar_.Add(this, 50 chrome::NOTIFICATION_FULLSCREEN_CHANGED, 51 content::NotificationService::AllSources()); 52 } 53 54 virtual ~Waiter() { 55 } 56 57 virtual void Observe(int type, 58 const content::NotificationSource& source, 59 const content::NotificationDetails& details) OVERRIDE { 60 DCHECK(type == chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED || 61 type == chrome::NOTIFICATION_FULLSCREEN_CHANGED); 62 if (running_) 63 base::MessageLoop::current()->Quit(); 64 } 65 66 // Wait until the two conditions are met. 67 void Wait(bool locker_state, bool fullscreen) { 68 running_ = true; 69 scoped_ptr<chromeos::test::ScreenLockerTester> 70 tester(chromeos::ScreenLocker::GetTester()); 71 while (tester->IsLocked() != locker_state || 72 browser_->window()->IsFullscreen() != fullscreen) { 73 content::RunMessageLoop(); 74 } 75 // Make sure all pending tasks are executed. 76 content::RunAllPendingInMessageLoop(); 77 running_ = false; 78 } 79 80 private: 81 Browser* browser_; 82 content::NotificationRegistrar registrar_; 83 84 // Are we currently running the message loop? 85 bool running_; 86 87 DISALLOW_COPY_AND_ASSIGN(Waiter); 88 }; 89 90 } // namespace 91 92 namespace chromeos { 93 94 class ScreenLockerTest : public InProcessBrowserTest { 95 public: 96 ScreenLockerTest() : fake_session_manager_client_(NULL) { 97 } 98 99 protected: 100 FakeSessionManagerClient* fake_session_manager_client_; 101 102 void LockScreen(test::ScreenLockerTester* tester) { 103 ScreenLocker::Show(); 104 tester->EmulateWindowManagerReady(); 105 content::WindowedNotificationObserver lock_state_observer( 106 chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED, 107 content::NotificationService::AllSources()); 108 if (!tester->IsLocked()) 109 lock_state_observer.Wait(); 110 EXPECT_TRUE(tester->IsLocked()); 111 } 112 113 // Verifies if LockScreenDismissed() was called once. 114 bool VerifyLockScreenDismissed() { 115 return 1 == fake_session_manager_client_-> 116 notify_lock_screen_dismissed_call_count(); 117 } 118 119 private: 120 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { 121 FakeDBusThreadManager* fake_dbus_thread_manager = new FakeDBusThreadManager; 122 fake_dbus_thread_manager->SetFakeClients(); 123 fake_session_manager_client_ = new FakeSessionManagerClient; 124 fake_dbus_thread_manager->SetSessionManagerClient( 125 scoped_ptr<SessionManagerClient>(fake_session_manager_client_)); 126 DBusThreadManager::SetInstanceForTesting(fake_dbus_thread_manager); 127 128 InProcessBrowserTest::SetUpInProcessBrowserTestFixture(); 129 zero_duration_mode_.reset(new ui::ScopedAnimationDurationScaleMode( 130 ui::ScopedAnimationDurationScaleMode::ZERO_DURATION)); 131 } 132 133 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 134 command_line->AppendSwitchASCII(switches::kLoginProfile, "user"); 135 } 136 137 scoped_ptr<ui::ScopedAnimationDurationScaleMode> zero_duration_mode_; 138 139 DISALLOW_COPY_AND_ASSIGN(ScreenLockerTest); 140 }; 141 142 IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestBasic) { 143 ScreenLocker::Show(); 144 scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester()); 145 tester->EmulateWindowManagerReady(); 146 content::WindowedNotificationObserver lock_state_observer( 147 chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED, 148 content::NotificationService::AllSources()); 149 if (!chromeos::ScreenLocker::GetTester()->IsLocked()) 150 lock_state_observer.Wait(); 151 152 // Test to make sure that the widget is actually appearing and is of 153 // reasonable size, preventing a regression of 154 // http://code.google.com/p/chromium-os/issues/detail?id=5987 155 gfx::Rect lock_bounds = tester->GetChildWidget()->GetWindowBoundsInScreen(); 156 EXPECT_GT(lock_bounds.width(), 10); 157 EXPECT_GT(lock_bounds.height(), 10); 158 159 tester->InjectMockAuthenticator(UserManager::kStubUser, "pass"); 160 EXPECT_TRUE(tester->IsLocked()); 161 tester->EnterPassword("fail"); 162 content::RunAllPendingInMessageLoop(); 163 EXPECT_TRUE(tester->IsLocked()); 164 tester->EnterPassword("pass"); 165 content::RunAllPendingInMessageLoop(); 166 // Successful authentication clears the lock screen and tells the 167 // SessionManager to announce this over DBus. 168 EXPECT_FALSE(tester->IsLocked()); 169 EXPECT_EQ( 170 1, 171 fake_session_manager_client_->notify_lock_screen_shown_call_count()); 172 173 EXPECT_TRUE(VerifyLockScreenDismissed()); 174 } 175 176 // Test how locking the screen affects an active fullscreen window. 177 IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestFullscreenExit) { 178 // 1) If the active browser window is in fullscreen and the fullscreen window 179 // does not have all the pixels (e.g. the shelf is auto hidden instead of 180 // hidden), locking the screen should not exit fullscreen. The shelf is 181 // auto hidden when in immersive fullscreen. 182 scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester()); 183 BrowserWindow* browser_window = browser()->window(); 184 ash::wm::WindowState* window_state = ash::wm::GetWindowState( 185 browser_window->GetNativeWindow()); 186 { 187 Waiter waiter(browser()); 188 browser()->fullscreen_controller()->ToggleFullscreenMode(); 189 waiter.Wait(false /* not locked */, true /* full screen */); 190 EXPECT_TRUE(browser_window->IsFullscreen()); 191 EXPECT_FALSE(window_state->hide_shelf_when_fullscreen()); 192 EXPECT_FALSE(tester->IsLocked()); 193 } 194 { 195 Waiter waiter(browser()); 196 ScreenLocker::Show(); 197 tester->EmulateWindowManagerReady(); 198 waiter.Wait(true /* locked */, true /* full screen */); 199 EXPECT_TRUE(browser_window->IsFullscreen()); 200 EXPECT_FALSE(window_state->hide_shelf_when_fullscreen()); 201 EXPECT_TRUE(tester->IsLocked()); 202 } 203 tester->InjectMockAuthenticator(UserManager::kStubUser, "pass"); 204 tester->EnterPassword("pass"); 205 content::RunAllPendingInMessageLoop(); 206 EXPECT_FALSE(tester->IsLocked()); 207 { 208 Waiter waiter(browser()); 209 browser()->fullscreen_controller()->ToggleFullscreenMode(); 210 waiter.Wait(false /* not locked */, false /* fullscreen */); 211 EXPECT_FALSE(browser_window->IsFullscreen()); 212 } 213 214 // 2) If the active browser window is in fullscreen and the fullscreen window 215 // has all of the pixels, locking the screen should exit fullscreen. The 216 // fullscreen window has all of the pixels when in tab fullscreen. 217 { 218 Waiter waiter(browser()); 219 content::WebContents* web_contents = 220 browser()->tab_strip_model()->GetActiveWebContents(); 221 browser()->fullscreen_controller()->ToggleFullscreenModeForTab( 222 web_contents, true); 223 waiter.Wait(false /* not locked */, true /* fullscreen */); 224 EXPECT_TRUE(browser_window->IsFullscreen()); 225 EXPECT_TRUE(window_state->hide_shelf_when_fullscreen()); 226 EXPECT_FALSE(tester->IsLocked()); 227 } 228 { 229 Waiter waiter(browser()); 230 ScreenLocker::Show(); 231 tester->EmulateWindowManagerReady(); 232 waiter.Wait(true /* locked */, false /* full screen */); 233 EXPECT_FALSE(browser_window->IsFullscreen()); 234 EXPECT_TRUE(tester->IsLocked()); 235 } 236 237 tester->InjectMockAuthenticator(UserManager::kStubUser, "pass"); 238 tester->EnterPassword("pass"); 239 content::RunAllPendingInMessageLoop(); 240 EXPECT_FALSE(tester->IsLocked()); 241 242 EXPECT_EQ( 243 2, 244 fake_session_manager_client_->notify_lock_screen_shown_call_count()); 245 EXPECT_EQ( 246 2, 247 fake_session_manager_client_->notify_lock_screen_dismissed_call_count()); 248 } 249 250 void SimulateKeyPress(views::Widget* widget, ui::KeyboardCode key_code) { 251 ui_controls::SendKeyPress(widget->GetNativeWindow(), 252 key_code, false, false, false, false); 253 } 254 255 void UnlockKeyPress(views::Widget* widget) { 256 SimulateKeyPress(widget, ui::VKEY_SPACE); 257 } 258 259 IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestShowTwice) { 260 scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester()); 261 LockScreen(tester.get()); 262 263 // Calling Show again simply send LockCompleted signal. 264 ScreenLocker::Show(); 265 EXPECT_TRUE(tester->IsLocked()); 266 EXPECT_EQ( 267 2, 268 fake_session_manager_client_->notify_lock_screen_shown_call_count()); 269 270 271 // Close the locker to match expectations. 272 ScreenLocker::Hide(); 273 content::RunAllPendingInMessageLoop(); 274 EXPECT_FALSE(tester->IsLocked()); 275 EXPECT_TRUE(VerifyLockScreenDismissed()); 276 } 277 278 // TODO(flackr): Find out why the RenderView isn't getting the escape press 279 // and re-enable this test (currently this test is flaky). 280 IN_PROC_BROWSER_TEST_F(ScreenLockerTest, DISABLED_TestEscape) { 281 scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester()); 282 LockScreen(tester.get()); 283 284 EXPECT_EQ( 285 1, 286 fake_session_manager_client_->notify_lock_screen_shown_call_count()); 287 288 tester->SetPassword("password"); 289 EXPECT_EQ("password", tester->GetPassword()); 290 // Escape clears the password. 291 SimulateKeyPress(tester->GetWidget(), ui::VKEY_ESCAPE); 292 content::RunAllPendingInMessageLoop(); 293 EXPECT_EQ("", tester->GetPassword()); 294 295 // Close the locker to match expectations. 296 ScreenLocker::Hide(); 297 content::RunAllPendingInMessageLoop(); 298 EXPECT_FALSE(tester->IsLocked()); 299 EXPECT_TRUE(VerifyLockScreenDismissed()); 300 } 301 302 } // namespace chromeos 303