1 // Copyright 2017 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 "ash/login/ui/lock_screen.h"
6 
7 #include <algorithm>
8 #include <memory>
9 #include <utility>
10 
11 #include "ash/login/ui/lock_contents_view.h"
12 #include "ash/login/ui/lock_debug_view.h"
13 #include "ash/login/ui/login_data_dispatcher.h"
14 #include "ash/login/ui/login_detachable_base_model.h"
15 #include "ash/public/cpp/lock_screen_widget_factory.h"
16 #include "ash/public/cpp/shell_window_ids.h"
17 #include "ash/shelf/login_shelf_view.h"
18 #include "ash/shelf/shelf.h"
19 #include "ash/shelf/shelf_widget.h"
20 #include "ash/shell.h"
21 #include "ash/wallpaper/wallpaper_controller_impl.h"
22 #include "base/bind.h"
23 #include "base/callback.h"
24 #include "base/command_line.h"
25 #include "chromeos/constants/chromeos_switches.h"
26 #include "ui/display/display.h"
27 #include "ui/display/screen.h"
28 #include "ui/views/widget/widget.h"
29 #include "ui/wm/core/capture_controller.h"
30 
31 namespace ash {
32 namespace {
33 
34 // Global lock screen instance. There can only ever be on lock screen at a
35 // time.
36 LockScreen* instance_ = nullptr;
37 
38 }  // namespace
39 
TestApi(LockScreen * lock_screen)40 LockScreen::TestApi::TestApi(LockScreen* lock_screen)
41     : lock_screen_(lock_screen) {}
42 
43 LockScreen::TestApi::~TestApi() = default;
44 
contents_view() const45 LockContentsView* LockScreen::TestApi::contents_view() const {
46   return lock_screen_->contents_view_;
47 }
48 
AddOnShownCallback(base::OnceClosure on_shown)49 void LockScreen::TestApi::AddOnShownCallback(base::OnceClosure on_shown) {
50   if (lock_screen_->is_shown_) {
51     std::move(on_shown).Run();
52     return;
53   }
54   lock_screen_->on_shown_callbacks_.push_back(std::move(on_shown));
55 }
56 
LockScreen(ScreenType type)57 LockScreen::LockScreen(ScreenType type) : type_(type) {
58   tray_action_observer_.Add(Shell::Get()->tray_action());
59   saved_clipboard_ = ui::Clipboard::TakeForCurrentThread();
60 }
61 
~LockScreen()62 LockScreen::~LockScreen() {
63   widget_.reset();
64 
65   ui::Clipboard::DestroyClipboardForCurrentThread();
66   if (saved_clipboard_)
67     ui::Clipboard::SetClipboardForCurrentThread(std::move(saved_clipboard_));
68 }
69 
MakeContentsView()70 std::unique_ptr<views::View> LockScreen::MakeContentsView() {
71   auto initial_note_action_state =
72       Shell::Get()->tray_action()->GetLockScreenNoteState();
73   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
74           chromeos::switches::kShowLoginDevOverlay)) {
75     auto debug_view =
76         std::make_unique<LockDebugView>(initial_note_action_state, type_);
77     contents_view_ = debug_view->lock();
78     return debug_view;
79   }
80 
81   auto detachable_base_model =
82       LoginDetachableBaseModel::Create(Shell::Get()->detachable_base_handler());
83   auto view = std::make_unique<LockContentsView>(
84       initial_note_action_state, type_,
85       Shell::Get()->login_screen_controller()->data_dispatcher(),
86       std::move(detachable_base_model));
87   contents_view_ = view.get();
88   return view;
89 }
90 
91 // static
Get()92 LockScreen* LockScreen::Get() {
93   CHECK(instance_);
94   return instance_;
95 }
96 
97 // static
Show(ScreenType type)98 void LockScreen::Show(ScreenType type) {
99   CHECK(!instance_);
100   // Capture should be released when locked.
101   ::wm::CaptureController::Get()->SetCapture(nullptr);
102 
103   instance_ = new LockScreen(type);
104 
105   aura::Window* parent = nullptr;
106   if (Shell::HasInstance()) {
107     parent = Shell::GetContainer(Shell::GetPrimaryRootWindow(),
108                                  kShellWindowId_LockScreenContainer);
109   }
110   instance_->widget_ =
111       CreateLockScreenWidget(parent, instance_->MakeContentsView());
112   instance_->widget_->SetBounds(
113       display::Screen::GetScreen()->GetPrimaryDisplay().bounds());
114 
115   // Postpone showing the screen after the animation of the first wallpaper
116   // completes, to make the transition smooth. The callback will be dispatched
117   // immediately if the animation is already complete (e.g. kLock).
118   Shell::Get()->wallpaper_controller()->AddFirstWallpaperAnimationEndCallback(
119       base::BindOnce(&LockScreen::ShowWidgetUponWallpaperReady),
120       instance_->widget_->GetNativeView());
121 }
122 
123 // static
HasInstance()124 bool LockScreen::HasInstance() {
125   return !!instance_;
126 }
127 
Destroy()128 void LockScreen::Destroy() {
129   LoginScreenController::AuthenticationStage authentication_stage =
130       Shell::Get()->login_screen_controller()->authentication_stage();
131   base::debug::Alias(&authentication_stage);
132   if (Shell::Get()->login_screen_controller()->authentication_stage() !=
133       authentication_stage) {
134     LOG(FATAL) << "Unexpected authentication stage "
135                << static_cast<int>(authentication_stage);
136   }
137   CHECK_EQ(instance_, this);
138 
139   Shell::Get()->login_screen_controller()->data_dispatcher()->RemoveObserver(
140       Shelf::ForWindow(Shell::GetPrimaryRootWindow())
141           ->shelf_widget()
142           ->login_shelf_view());
143 
144   delete instance_;
145   instance_ = nullptr;
146 }
147 
FocusNextUser()148 void LockScreen::FocusNextUser() {
149   contents_view_->FocusNextUser();
150 }
151 
FocusPreviousUser()152 void LockScreen::FocusPreviousUser() {
153   contents_view_->FocusPreviousUser();
154 }
155 
ShowParentAccessDialog()156 void LockScreen::ShowParentAccessDialog() {
157   contents_view_->ShowParentAccessDialog();
158 }
159 
OnLockScreenNoteStateChanged(mojom::TrayActionState state)160 void LockScreen::OnLockScreenNoteStateChanged(mojom::TrayActionState state) {
161   Shell::Get()
162       ->login_screen_controller()
163       ->data_dispatcher()
164       ->SetLockScreenNoteState(state);
165 }
166 
OnSessionStateChanged(session_manager::SessionState state)167 void LockScreen::OnSessionStateChanged(session_manager::SessionState state) {
168   if (type_ == ScreenType::kLogin &&
169       state == session_manager::SessionState::ACTIVE) {
170     Destroy();
171   }
172 }
173 
OnLockStateChanged(bool locked)174 void LockScreen::OnLockStateChanged(bool locked) {
175   if (type_ != ScreenType::kLock)
176     return;
177 
178   if (!locked)
179     Destroy();
180 }
181 
OnChromeTerminating()182 void LockScreen::OnChromeTerminating() {
183   Destroy();
184 }
185 
186 // static
ShowWidgetUponWallpaperReady()187 void LockScreen::ShowWidgetUponWallpaperReady() {
188   // |instance_| may already be destroyed in tests.
189   if (!instance_ || instance_->is_shown_)
190     return;
191   instance_->is_shown_ = true;
192   instance_->widget_->Show();
193 
194   std::vector<base::OnceClosure> on_shown_callbacks;
195   swap(instance_->on_shown_callbacks_, on_shown_callbacks);
196   for (auto& callback : on_shown_callbacks)
197     std::move(callback).Run();
198 
199   Shell::Get()->login_screen_controller()->NotifyLoginScreenShown();
200 }
201 
202 }  // namespace ash
203