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.
5 #include "chrome/browser/chromeos/kiosk_mode/kiosk_mode_screensaver.h"
7 #include "ash/screensaver/screensaver_view.h"
9 #include "ash/wm/user_activity_detector.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/lazy_instance.h"
13 #include "base/logging.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/chromeos/kiosk_mode/kiosk_mode_settings.h"
17 #include "chrome/browser/chromeos/login/existing_user_controller.h"
18 #include "chrome/browser/chromeos/login/login_display_host_impl.h"
19 #include "chrome/browser/chromeos/policy/app_pack_updater.h"
20 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
21 #include "chrome/browser/chromeos/profiles/profile_helper.h"
22 #include "chrome/browser/extensions/extension_service.h"
23 #include "chrome/browser/extensions/extension_system.h"
24 #include "chrome/browser/extensions/sandboxed_unpacker.h"
25 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
26 #include "chrome/common/extensions/extension_file_util.h"
27 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
28 #include "chromeos/login/login_state.h"
29 #include "content/public/browser/browser_thread.h"
30 #include "content/public/browser/notification_service.h"
31 #include "extensions/common/extension.h"
33 using extensions::Extension;
34 using extensions::SandboxedUnpacker;
40 ExtensionService* GetDefaultExtensionService() {
41 Profile* default_profile = ProfileHelper::GetSigninProfile();
44 return extensions::ExtensionSystem::Get(
45 default_profile)->extension_service();
48 typedef base::Callback<void(
49 scoped_refptr<Extension>,
50 const base::FilePath&)> UnpackCallback;
52 class ScreensaverUnpackerClient
53 : public extensions::SandboxedUnpackerClient {
55 ScreensaverUnpackerClient(const base::FilePath& crx_path,
56 const UnpackCallback& unpacker_callback)
57 : crx_path_(crx_path),
58 unpack_callback_(unpacker_callback) {}
60 virtual void OnUnpackSuccess(const base::FilePath& temp_dir,
61 const base::FilePath& extension_root,
62 const base::DictionaryValue* original_manifest,
63 const Extension* extension,
64 const SkBitmap& install_icon) OVERRIDE;
65 virtual void OnUnpackFailure(const base::string16& error) OVERRIDE;
68 virtual ~ScreensaverUnpackerClient() {}
71 void LoadScreensaverExtension(
72 const base::FilePath& extension_base_path,
73 const base::FilePath& screensaver_extension_path);
75 void NotifyAppPackOfDamagedFile();
77 base::FilePath crx_path_;
78 UnpackCallback unpack_callback_;
80 DISALLOW_COPY_AND_ASSIGN(ScreensaverUnpackerClient);
83 void ScreensaverUnpackerClient::OnUnpackSuccess(
84 const base::FilePath& temp_dir,
85 const base::FilePath& extension_root,
86 const base::DictionaryValue* original_manifest,
87 const Extension* extension,
88 const SkBitmap& install_icon) {
89 content::BrowserThread::PostTask(
90 content::BrowserThread::FILE,
92 base::Bind(&ScreensaverUnpackerClient::LoadScreensaverExtension,
98 void ScreensaverUnpackerClient::OnUnpackFailure(const base::string16& error) {
99 LOG(ERROR) << "Couldn't unpack screensaver extension. Error: " << error;
100 NotifyAppPackOfDamagedFile();
103 void ScreensaverUnpackerClient::LoadScreensaverExtension(
104 const base::FilePath& extension_base_path,
105 const base::FilePath& screensaver_extension_path) {
106 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
108 ExtensionService* service = GetDefaultExtensionService();
109 // TODO(rkc): This is a HACK, please remove this method from extension
110 // service once this code is deprecated. See crbug.com/280363
112 service->disable_garbage_collection();
115 scoped_refptr<Extension> screensaver_extension =
116 extension_file_util::LoadExtension(screensaver_extension_path,
117 extensions::Manifest::COMPONENT,
120 if (!screensaver_extension.get()) {
121 LOG(ERROR) << "Could not load screensaver extension from: "
122 << screensaver_extension_path.value() << " due to: " << error;
123 NotifyAppPackOfDamagedFile();
127 content::BrowserThread::PostTask(
128 content::BrowserThread::UI,
132 screensaver_extension,
133 extension_base_path));
136 void ScreensaverUnpackerClient::NotifyAppPackOfDamagedFile() {
137 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
138 content::BrowserThread::PostTask(
139 content::BrowserThread::UI, FROM_HERE,
140 base::Bind(&ScreensaverUnpackerClient::NotifyAppPackOfDamagedFile,
145 policy::BrowserPolicyConnectorChromeOS* connector =
146 g_browser_process->platform_part()->browser_policy_connector_chromeos();
147 policy::AppPackUpdater* updater = connector->GetAppPackUpdater();
149 updater->OnDamagedFileDetected(crx_path_);
154 KioskModeScreensaver::KioskModeScreensaver()
155 : weak_ptr_factory_(this) {
156 chromeos::KioskModeSettings* kiosk_mode_settings =
157 chromeos::KioskModeSettings::Get();
159 if (kiosk_mode_settings->is_initialized()) {
160 GetScreensaverCrxPath();
162 kiosk_mode_settings->Initialize(base::Bind(
163 &KioskModeScreensaver::GetScreensaverCrxPath,
164 weak_ptr_factory_.GetWeakPtr()));
168 KioskModeScreensaver::~KioskModeScreensaver() {
169 // If we are shutting down the system might already be gone and we shouldn't
170 // do anything (see crbug.com/288216).
171 if (!g_browser_process || g_browser_process->IsShuttingDown())
174 // If the extension was unpacked.
175 if (!extension_base_path_.empty()) {
176 ExtensionService* service = GetDefaultExtensionService();
177 // TODO(rkc): This is a HACK, please remove this method from extension
178 // service once this code is deprecated. See crbug.com/280363
180 service->enable_garbage_collection();
183 content::BrowserThread::PostTask(
184 content::BrowserThread::FILE,
187 &extension_file_util::DeleteFile, extension_base_path_, true));
190 // In case we're shutting down without ever triggering the active
191 // notification and/or logging in.
192 if (ash::Shell::GetInstance() &&
193 ash::Shell::GetInstance()->user_activity_detector() &&
194 ash::Shell::GetInstance()->user_activity_detector()->HasObserver(this))
195 ash::Shell::GetInstance()->user_activity_detector()->RemoveObserver(this);
198 void KioskModeScreensaver::GetScreensaverCrxPath() {
199 chromeos::KioskModeSettings::Get()->GetScreensaverPath(
200 base::Bind(&KioskModeScreensaver::ScreensaverPathCallback,
201 weak_ptr_factory_.GetWeakPtr()));
204 void KioskModeScreensaver::ScreensaverPathCallback(
205 const base::FilePath& screensaver_crx) {
206 if (screensaver_crx.empty())
209 ExtensionService* extension_service = GetDefaultExtensionService();
210 if (!extension_service)
212 base::FilePath extensions_dir = extension_service->install_directory();
213 scoped_refptr<SandboxedUnpacker> screensaver_unpacker(
214 new SandboxedUnpacker(
216 extensions::Manifest::COMPONENT,
219 content::BrowserThread::GetMessageLoopProxyForThread(
220 content::BrowserThread::FILE).get(),
221 new ScreensaverUnpackerClient(
224 &KioskModeScreensaver::SetupScreensaver,
225 weak_ptr_factory_.GetWeakPtr()))));
227 // Fire off the unpacker on the file thread; don't need it to return.
228 content::BrowserThread::PostTask(
229 content::BrowserThread::FILE,
232 &SandboxedUnpacker::Start, screensaver_unpacker.get()));
235 void KioskModeScreensaver::SetupScreensaver(
236 scoped_refptr<Extension> extension,
237 const base::FilePath& extension_base_path) {
238 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
239 extension_base_path_ = extension_base_path;
241 // If the user is already logged in, don't need to display the screensaver.
242 if (chromeos::LoginState::Get()->IsUserLoggedIn())
245 ash::Shell::GetInstance()->user_activity_detector()->AddObserver(this);
247 ExtensionService* extension_service = GetDefaultExtensionService();
248 // Add the extension to the extension service and display the screensaver.
249 if (extension_service) {
250 extension_service->AddExtension(extension.get());
251 ash::ShowScreensaver(
252 extensions::AppLaunchInfo::GetFullLaunchURL(extension.get()));
254 LOG(ERROR) << "Couldn't get extension system. Unable to load screensaver!";
255 ShutdownKioskModeScreensaver();
259 void KioskModeScreensaver::OnUserActivity(const ui::Event* event) {
260 // We don't want to handle further user notifications; we'll either login
261 // the user and close out or or at least close the screensaver.
262 ash::Shell::GetInstance()->user_activity_detector()->RemoveObserver(this);
264 // Find the retail mode login page.
265 if (LoginDisplayHostImpl::default_host()) {
266 LoginDisplayHostImpl* webui_host =
267 static_cast<LoginDisplayHostImpl*>(
268 LoginDisplayHostImpl::default_host());
269 OobeUI* oobe_ui = webui_host->GetOobeUI();
271 // Show the login spinner.
273 oobe_ui->ShowRetailModeLoginSpinner();
275 // Close the screensaver, our login spinner is already showing.
276 ash::CloseScreensaver();
279 ExistingUserController* controller =
280 ExistingUserController::current_controller();
281 if (controller && !chromeos::LoginState::Get()->IsUserLoggedIn())
282 controller->LoginAsRetailModeUser();
284 // No default host for the WebUiLoginDisplay means that we're already in the
285 // process of logging in - shut down screensaver and do nothing else.
286 ash::CloseScreensaver();
289 ShutdownKioskModeScreensaver();
292 static KioskModeScreensaver* g_kiosk_mode_screensaver = NULL;
294 void InitializeKioskModeScreensaver() {
295 if (g_kiosk_mode_screensaver) {
296 LOG(WARNING) << "Screensaver was already initialized";
300 g_kiosk_mode_screensaver = new KioskModeScreensaver();
303 void ShutdownKioskModeScreensaver() {
304 delete g_kiosk_mode_screensaver;
305 g_kiosk_mode_screensaver = NULL;
308 } // namespace chromeos